회사 테크 블로그 기고 - 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 스펙을 사용하지 못한다는 것을 의미하고, 버그 수정도 되지 않기에 어플리케이션의 유지보수가 굉장히 어려워짐을 의미합니다. 현재까지의 문제점을 정리하면 아래와 같습니다. ...