2024년이 끝나간다. 창업 이후 가장 많은 변화가 있었던 해였고, 회사와 개인적으로도 큰 도전과 성장이 있었다. 회사의 성장 작년에는 ‘머리에 불이 붙은 문제’를 찾아 헤매던 시기였다면, 올해는 그 문제를 해결하는 제품을 만들어 시장에서 검증받는 시기였다. 문제 정의에서 제품 출시까지 작년에는 60번이 넘는 인터뷰를 진행했다. 인터뷰를 하면서 한 가지 부정할 수 없는 전제를 확인할 수 있었는데, 바로 기업들이 LLM을 활용해 생산성을 개선하고 싶어한다는 것이었다. (너무 당연하다.) 하지만 동시에 큰 도전 과제도 발견했다. 기업들이 실제로 LLM을 도입하고 유용한 수준에 도달하려면 특정 버티컬에서 깊은 연동이 필요하지만 많은 회사는 직접 그걸 하는 ROI가 나오지 않고 아직 검증도 덜 된 상태였다. 그런 문제를 해결해주고 싶은 스타트업으로써는 문제인지 아직 확실하지 않은 상태에서 우리에게 베팅하고 함께 만들어갈 디자인 파트너를 찾는 게 쉽지 않았다. 특히 우리가 ICP로 삼았던 Technical Customer Support 팀이나 SRE 팀은 대부분 시리즈 A 이상의 조직들에서 생겨나는 팀들이었다. 실무자나 팀 리드들은 문제가 있다고 말하고 우리가 제안하려는 솔루션에 관심을 보였지만, 실제로 도입하려면 어느정도 사이즈가 있는 조직 특성 상 긴 리드 타임과 컴플라이언스 같은 높은 진입 장벽을 넘어야 했다. 문제가 확실히 검증됐다면 당연히 감수해야 할 과정이었겠지만, 창업팀 입장에서는 아직 검증되지 않은 가설을 위해 긴 시간과 비용을 투자하기가 어려웠다.(반대편도 마찬가지다.) 전형적인 닭과 달걀의 문제였다. ...
회사 테크 블로그 기고 - PhantomJS를 Headless Chrome(Puppeteer)로 전환하며
이 포스팅은 제가 버즈빌 테크 블로그에 기고한 글을 전제한 것입니다. 버즈빌에서는 모바일 잠금화면에 내보내기 위한 광고 및 컨텐츠 이미지를 생성하기 위한 PhantomJS 렌더링 서버를 다수 운영하고 있습니다. 일반적으로 PhantomJS는 웹페이지 캡쳐에 많이 쓰이지만, 기본적으로 headless하게 웹페이지를 렌더링하고 캡쳐할 수 있다는 특성 때문에 동적인 이미지 생성에도 많이 활용됩니다. 버즈빌의 렌더링 서버는 200개 이상의 컨텐츠 프로바이더로부터 실시간으로 잠금화면 컨텐츠 이미지를 생성하고 있어 분당 수백 건의 이미지를 안정적으로 생성하는 것이 가능해야 합니다. 렌더링 서버의 스케일링 이슈를 해결하기 위해 버즈빌에서는 여러 대의 렌더링 서버를 둬서 횡적으로 확장을 함과 동시에, 개별 서버 내에서도 리소스 사용률을 높이기 위해 Ghost Town이라는 라이브러리를 작성해 PhantomJS 프로세스 풀을 구성하여 사용하고 있었습니다(Scaling PhantomJS With Ghost Town ) 한편, 시간이 지나면서 잠금화면에서 렌더링하는 이미지 템플릿의 종류가 다양해지고, emoji 및 여러 특수문자를 표현하기 위해 렌더링 서버에 여러 폰트(대표적으로 Noto Sans CJK)를 설치해야 하는 요구사항이 추가됐는데, PhantomJS에서 폰트 렌더링이 일관적이지 않은 문제가 발생했습니다. 이 문제의 정확한 원인은 결국 찾지 못했지만 PhantomJS의 이슈였거나 시스템 상에 폰트가 시간이 지나면서 추가 설치됨에 따라 font cache가 서버마다 일관되지 않은 상태가 되었기 때문인 것으로 짐작하고 있습니다. 다른 워크로드와 마찬가지로 렌더링 서버도 최초에는 packer를 이용해 일관되게 이미지를 빌드하고 업데이트하려고 했지만, 자주 기능이 추가되거나 배포되는 서비스가 아니기에 서버를 오래 띄워놓고 수동으로 유지보수를 한 케이스들이 누적되어 더 이상 packer를 이용해 시스템이나 폰트를 최신 상태로 유지하는 것이 어려운 상태였습니다. 모든 눈꽃송이가 자세히 보면 조금씩 다르게 생겼다는 것에서 비롯된 snowflake, 즉 배포된 서버들이 시간이 지남에 따라 조금씩 다른 상태가 된 것입니다. 평소에는 문제가 없어 보이지만, 추가적인 확장성이 필요해 scale out을 하거나 새로운 템플릿을 개발해 배포를 하면 문제가 발생하는 상황이었습니다. 사실 더 큰 문제는 PhantomJS 프로젝트가 더 이상 관리되지 않는다는 점이었습니다. 2017년 Google Chrome 59버전부터 Headless Chrome이 내장되기 시작하였고, 곧바로 Node API인 puppeteer가 릴리즈 되어, 현시점에서 가장 많이 쓰이는 렌더링 엔진을 손쉽게 headless로 사용할 수 있는 환경이 되었습니다. 때문에 PhantomJS 관리자가 사실상의 중단을 선언하였고, 2018년에는 최초 개발자에 의해 프로젝트가 아카이브 되었습니다. 프로젝트가 업데이트되지 않는 것은 템플릿에 최신 CSS 스펙을 사용하지 못한다는 것을 의미하고, 버그 수정도 되지 않기에 어플리케이션의 유지보수가 굉장히 어려워짐을 의미합니다. 현재까지의 문제점을 정리하면 아래와 같습니다. ...
간만의 한글입숨 리뉴얼
한글입숨 기능이 너무 단순해서 딱히 손 안되도 잘 돌아가던 서비스지만, 마지막 수정일이 6년 전인건 좀 너무하다 싶어서 간만에 손을 좀 봤다. 그새 레일즈는 메이저 버전이 3에서 5로 올랐고, 한 때 유행했던 패턴 백그라운드는 관짝에 들어간 지 오래다. 딱히 기능을 바꿀건 없어서 약간의 리팩토링만 하고 배포 완료. 눈꼽만큼이지만 방문자가 꾸준히 있긴 하니 유지는 해야지 싶다.
샤오미 Yeelight 스마트 전구 설치기
회사에 조명에 관심많은 형이 있는데, 하얀 형광등을 무지 싫어하고 간접조명을 좋아하는 스타일이다. 작년에 이사한 사무실도 셀프로 조명 시공을 했고, 집에서도 알렉사와 필립스 Hue 조합으로 조명 시스템을 구비해놓은 사람이다. 나도 어쩌다보니 영향을 받아 스마트 전구에 관심을 갖게됐다. 당시(지금도) 필립스 Hue는 좀 비싼 선택지라 대안이 있는지 알아보던 중 Indiegogo에서 “World’s Most Affordable Wi-Fi Smart Bulb” 라는 타이틀을 단 스마트 전구를 보게됐고, 개당 $20도 안하는 가격이라 확 질렀었다. 그로부터 장장 16개월이라는 기나긴 인내의 시간이 시작된다 -ㅅ- ...
Docker를 이용한 bundle install 및 Gemfile.lock 업데이트하기
여러개의 ruby 프로젝트를 작업하다보면 rbenv, rvm 등으로 버전별, 프로젝트별 ruby와 gem들을(rvm의 경우 gemset) 관리해야하는 귀찮음이 생긴다. 지난번에 로컬에 프로젝트 관련 gem을 깔지 않기로 결심했으니 rubygems의 디펜던시를 기록하는 Gemfile.lock 파일 역시 docker를 이용해 업데이트를 하기로 했다. 아래와 같은 Gemfile이 있다고 하자. # Gemfile ruby '2.3.1' source 'https://rubygems.org' gem 'sinatra' 위 Gemfile에서 ruby 버전을 2.3.1로 명시하고 있기 때문에 rvm이든 rbenv든 이용해서 로컬에 해당 버전을 설치해 줘야 bundle install을 실행할 수 있다. 하지만 docker를 이용하면 원하는 ruby 버전으로 one-off container를 만들어 현재 경로를 mount한 채로 bundle install을 실행하면 Gemfile.lock을 올바르게 업데이트 할 수 있다. ...
로컬 postgres 없이 heroku pg:psql 커맨드 사용하기
최근 로컬 개발환경을 리셋하면서 redis, postgres 등 각종 개발 디펜던시를 설치하지 않고, 오직 docker만 이용해서 로컬 환경을 최대한 깔끔하게 유지하기로 마음먹었다(똥고집이다). 그런데 난관이 등장했으니, 프로덕션 배포 중인 heroku 앱의 postgres 디비 콘솔에 접근하기 위해 heroku toolbelt에서 제공하는 heroku pg:psql 커맨드를 자주 사용하는데, 이 때 로컬 경로의 psql을 이용한다는 것이다. $ heroku pg:psql ---> Connecting to DATABASE_URL sh: psql: command not found 굳이 toolbelt를 쓰지 않고 docker run -it --rm postgres psql 커맨드에 적당히 credential을 넣어주면 되지만, 매번 credential을 알아오기도 귀찮고 heroku toolbelt를 썩히기도 아까워 해결책이 있을지 고민해봤다. 처음에는 간단히 alias를 걸면 될 줄 알았는데 heroku command에서 쉘 환경을 리셋시켜버려서 command not found가 떴다. ...