사파리 익스텐션 만들어보기...

오래전부터 생각해봤던 것인데 이제서야 실행에 옮겨봤다. 뭔가 나 스스로 새로운 것을 배워봐야지 할 때가 되어서 그랬는지는 몰라도.

이름하야 torrent hash adder? submitter? dispatcher? 그러니까 내가 어떤 것을 torrent로 다운로드 받을 수 있게 hash를 대신 전달해주는 것? 그러니까 토런트 해쉬코드 혹은 매그넷 링크 위에 커서를 가져다놓고 오른쪽 버튼을 누르고 컨텍스트 메뉴를 선택하면 즉각적으로 대신 전달해주는 거 말이다.

이런 게 누군가 만들어서 나와있겠지 아니면 만들겠지 싶었는지 십수년이 지났지만 비슷한 것도 본 적이 없었다. 크롬 브라우저 익스텐션으로 누군가 만든게 있긴 한가본데 내가 원하는 것과 딱 떨어지지 않았다. 그래서 내가 만들어봤다. 나는 사파리를 쓰니까 사파리 익스텐션으로 말이다. 솔직히 말하면 Xcode에서 사파리 익스텐션 용으로 탬플릿 같은 게 보이길래 이 생각을 해서 만들어봤달까?

내가 윈도우즈에만 들어가더라도 크롬을 즐겨쓸텐데 그러지 않으니 사파리가 내겐 더 편하다. 아이패드/아이폰/메신저 등등 데이터 공유하기도 편하고 해서. 이렇게 애플 생태계가 날 잡아두었다. 안드로이드 폰만 썼어도 얘기가 좀 달라졌을지도 모르겠다만.

어쨌거나 만들어봤다. 하루를 꼬박 투자해서. 사실 일하면서 잠시 잠깐 들여다보면서 소비된 시간도 제법된다. 왜? 무슨 일을 하든 초반 러닝 커브랄까 일단 익숙해지면 쉬운 것인데 아무것도 모를 때는 엄청나게 블록킹 이슈가 되는 것들 때문에.

일단 구글링을 해보면 누군가 10분만에 컨텍스트 메뉴로 컨트롤하는 사파리 익스텐션 만들기에 대한 글을 올려둔 게 있는데 난 그걸 참조했다. 애플의 디벨로퍼를 위한 메뉴얼 사이트도 참조해가면서. 그런데 왜 이렇게 오랜 시간이 걸렸느냐? 그것은 정확하게 어디에 있는 어떤 파일을 건드려서 어떻게 해야 한다라는 설명이 부실했기 때문이다. 이런 게 다 처음 시작하는 사람에게 큰 걸림돌이 되는 거다. 사실 그 위치만 제대로 알았어도 하루도 안걸려서 쉽게 할 수 있는 것이었는데 애먼 2일 동안 일하면서 짬짬히 시간을 소비했는데도 못 만들게 된 원인이 된거다.

처음 부딪치게 된 문제들을 정리하면:

Info.plist를 건드려서 context menu를 알려줘야 되는데, 이게 plist editor에서 하는 것과 xml을 직접 건드려서 하는 것과 보이는 것과 내용물의 차이가 제법 있었다.

어떻게 해결했냐고? 그냥 직접 xml을 편집했다. 처음엔 plist editor로 했다가 아무런 변화가 없어서 또 쓸데없는 곳을 쑤셔대느라 시간을 허비했다. 그보다 먼저 프로젝트 안에서 Info.plist가 어디있는지 한참을 찾았다. 사실 Info.plist는 MacOS의 앱이라면 다 가지고 있는 것이라서 앱을 빌드하고 나온 결과물에 나온 것을 건드려야 하나 하는 엉뚱한 생각을 했는데, 프로젝트 안에 있는 파일을 잘 뒤져보면 떡 하니 있는 것을 알 수 있다.

Info.plist의 내용물이 메뉴얼/지침 페이지에 나온 것과 달랐다.

Safari extension에는 web extension과 app extension 두 가지가 있는데 context menu를 건드리는 예로 나온 것들은 다 app extension이다. 이걸 제대로 설명해주지 않아서 web extension을 생성해놓고 또 시간을 허비했다.

script.js라는 파일이 프로젝트 내에 두 개가 있다. 반드시 extension folder 아래에 있는 resouces/script.js를 건드려야 된다.

이것도 마찬가지로 제대로 설명이 안되어있어서 엄한 script.js를 건드려서 시간을 허비했다. 요약하자면 safari extension이라는 건 크게 보자면 해당 extension을 plugin 했을 때 강제로 inject하는 javascript와 swift (혹은 object C)로 만드는 application 두개로 구성된다. 그러니까, 전자가 script.js에 해당하는데 웹 페이지와 application을 연결하는 역할을 한다.

이를테면 context menu를 가지고 뭔가 기능을 하는 extension을 만들고자 하는 경우, context menu 자체를 Info.plist에 선언해놓고 Safari 상에서도 분명히 뜨는 것을 알고 있다고 하더라도 그게 extension app에 직접적으로 연결되지 않는다. 이게 javascript에서 별도 event handler가 있어서 그것이 해당 app에 데이터를 전달해주는 식으로 만들어져야 한다는 거다. (솔직히 나는 이런 종류의 것들이 이렇게 만들어지는 것인지 첨 알았다) 그러니까 safari extension을 한다고 하면 swift만 알아서도 안되고 javascript만 알아서도 안되고 HTML/Javascript/Swift를 모두 할 줄 알아야 한다. 이들간의 정보 교환을 원할하게 하려면 제법 많이 해봐서 이런 저런 잔기술에 능해야 된다는 거다.

어쨌든 이 파일이 프로젝트 내에 두 개 들어있다. 이게 어떤 것으로 로딩되는지 보려면 익스텐션을 활성화 시켜놓고 어떤 웹페이지를 로딩해놓고 소스보기를 하면 알 수 있다. 원래 페이지가 로딩하는 javascript가 있고 extension이 로딩하는 javascript들이 또 있다. 그러니까 내가 여러 개의 extension을 쓰고 있다고 하면 각자의 javascript들이 다 로딩되야 되는 거라 생각보다 매우 많은 js file들이 로딩되어있음을 알 수 있다.

Swift는 내가 생각했던 수준의 것보다는 뭐랄까 아직 좀 미숙하고 에러라든가 예외처리를 위한 예약어나 기호들/클리세들이 제법 있었는데 뭐랄까 내 눈엔 별로 직관적이지 않았다.

내가 구식의 사람이다보니 swift가 이쁘게 보일리 없다고 본다. 어차피 내가 swift는 언제든 제대로 배워야지 했었는데, 뭐랄까 그런 의욕이 다 사그라들었다. swift가 Regex같이 필수적인 기능을 제공하게 된 것도 얼마되지 않았고 어차피 그게 그거인 기능들을 제공하기 위하여 뭐랄까 좀 황당한 기호나 방식을 쓰고 있는 게 영 보기 좋지 않았다. 그에 비하면 javascript는 엄청나게 많이 다듬어져있고 파워풀하니까 뭐랄까 정이 붙지 않는달까.

보안문제 때문에 할 수 있는 게 많지 않았다.

내가 원했던 것은 일단 selected text 혹은 마우스 커서가 놓여있는 곳에 있는 href를 읽어다주면 거기서 40글자로 되어있는 hash만 취해서 그걸 torrent client에게 보내주는 일이었다. 사실 이 과정을 위해서 해당 remote machine에 nodejs로 http service를 하나 만들었다. 그러니까 특정 포트에 http 서비스를 심어놨다. 이것은 자신에게 request된 string을 보고 거기서 그냥 40글자짜리 hash를 찾아다가 xmlrpc로 rtorrent에게 hash를 전달하고 해당 download process를 시작하게 해주는 기능을 한다. 그러니까 익스텐션은 단순히 hash를 읽어다가 http로 해당 머신의 해당 포트로 request만 해주면 되는 거다. 그런데 이게 javascript side에서도 안되었고 swift side에서도 안됐다. 그것은 https가 아닌 이유라서다. 그러니까 브라우저에 기생해서 도는 프로세스나 스크립트는 https가 아니면 request를 하면 거부당하는 거다. error가 잘 알려주는 경우도 있고, 아무 소리 없는 경우도 있었다. 구글링을 줄창해봐도 속시원한 답을 찾을 수 없어서 아직 이부분은 그냥 open 명령을 불러서 처리했다. 보안 때문에 http access는 못하게 하는데 브라우저 탭을 새로 여는 것은 가능했다. 웃기는 일이다.

결국 extension을 다 만들고 나서 보니 대부분의 일은 아니 100%의 일을 javascript에서 수행하게 만드는 게 훨씬 보기 좋고 만들기도 좋고 속도도 빨랐다.

ㅆㅂ Swift로 구성된 프로젝트 자체가 도무지 이해를 할 수가 없는 거다. 그냥 extension을 javascript로만 아주 간단하게 만들 수 있는 걸 swift를 쓰게 만들어서 괜히 크고 복잡해진거다. swift와 javascript를 둘 다 아는 사람들은 알겠지만 두 가지 언어의 성격이 좀 달라서 양쪽을 오가면 기분이 별로 상쾌하지 않다. 이를테면 C++와 javascript를 오가는 것은 나름 쾌적하지만 swift는 좀 거지같다. 마치 C++과 javascript 그리고 perl (혹은 PHP) 사이를 오가는 것은 그럭저럭 재미있지만 python을 오가야 되는 것은 얘기가 좀 달라지는 것처럼 말이다. matlab과 mex를 오가는 것도 여전히 거지같고.

이외에도 느낀 점에 제법 많았는데, swift는 빨리 언어 자체가 죽어버리든지 했으면 하는데 이게 애플생태계에 들어와서 iOS/MacOS app을 개발하는 주축 언어로 있다고 하니 나로서는 별 달리 할 말은 없지만 지금까지의 인상은 영 거지같다고 해야할 것 같다.

지금 예로 들은 이렇게 간단한 일을 하는 프로젝트에서도 그 언어 자체의 능력이 제한적이라 빌빌대는데, 하다 못해 웹 페이지에서 광고를 블록하는 기능을 하는 익스텐션을 짠다고 하는 것을 생각해봐도 RegEx 없인 매우 불편해지는데, 최근 OS에서만 regex를 지원하겠다 이런 식으로 하면 개발자들은 보나마나 앱을 누더기로 만들 수 밖에 없다. 이를테면 regex를 OS 버전이나 device 종류에 상관없이 가져다 쓰게 하려면 별도의 라이브러리를 동원한다거나 플랫폼에 구애 받지 않는 어떤 소프트웨어를 임베드한다거나 해야 되니까. 그래서 제법 단순한 일을 하면서도 몇십 MB 정도의 크기를 갖는 결과물을 만들게 되는거라고 본다.

이 별 것 아닌 걸 하면서도 앞으로의 세상은 원하는 기능은 어느 플랫폼에서나 다 돌아야 되는, 또 각자의 플랫폼이 고집하고 있는 것들은 다 만족시켜줘야 하는 그런 소프트웨어를 만들어야 되다보니 누더기로 만들어야 되는 거구나 했다. 그래서 상업용으로 나온 소프트웨어라고 하더라도 심심할 때 그것들을 열어보면 신기한 모양새를 많이 보게 된다. 한가지 언어로 씌여진 것이 아니고 그것들을 다 같이 돌게 만드려니 온갖 지저분한 것들이 다 달라붙고 있는 그런 모양새.

요새 애들이 부럽다. 잘은 몰라도 이들은 javascript (nodejs)와 python, swift, R 이런 것에 다 능한 것 아닌가? go를 쓰는 인간들도 제법 있다고 들었다. 뭐니뭐니해도 빠르게 또 군더더기 없이 도는 소프트웨어를 만드려면 역시 C/C++이어야지 하는 나와 같은 구식 인간은 여전히 인터프리터에 의존해서 도는 스크립팅 언어들이 주가 되어 소프트웨어를 구성한다는 게 많이 낯설다. 또 html과 javascript가 사실상 presentation의 주가 되고 여기에 R, ruby, go 등등의 언어들을 즐겨써서 작업하는 경우도 신기하고.