웹에서 전문적인 디자인 도구 (새 탭에서 열림)
Figma는 전문 디자이너가 받아들일 수 있는 고품질 편집 경험을 웹 브라우저에서 제공하기 위해, 사실상 “브라우저 안의 브라우저”를 구축했다. 웹 플랫폼의 제한적인 추상화 대신 WebGL·asm.js 같은 저수준 기술을 활용하고, C++ 기반 편집기와 자체 메모리·렌더링 시스템으로 성능과 플랫폼 간 일관성을 확보했다. 특히 Emscripten을 통해 네이티브에 가까운 성능과 예측 가능한 프레임률을 달성하는 것이 핵심 전략이다.
웹에서 전문 디자인 도구를 만들기 어려운 이유
- 웹은 원래 문서 표시를 위해 설계되었고, 애플리케이션 개발 기능은 이후 개별 API 형태로 덧붙여졌다.
- 따라서 특정 기능은 제공하지만, 개발자가 이를 조합해 새로운 동작을 구현할 수 있는 범용적인 저수준 primitive가 부족하다.
- CSS는 복잡한 텍스트 배치 알고리즘을 제공하지만:
- 알고리즘을 직접 커스터마이즈하기 어렵고
- 브라우저가 계산한 결과를 읽어 다른 알고리즘에 재사용하기도 어렵다.
- 브라우저의 GPU 컴포지터는 고성능이지만:
- 렌더링 과정에 직접 개입하기 어렵고
- 사용자 정의 블렌드 모드나 애플리케이션 특화 최적화를 추가할 수 없다.
- 이미지 디코더는 하드웨어 가속과 비동기 처리를 지원하지만:
- EXIF 방향 정보를 어떻게 처리할지 지정하기 어렵고
- 디스플레이 색 공간을 이미지 데이터에 미리 반영하지 않도록 제어하기 어렵다.
- WebGL과 asm.js의 등장으로 개발자가 브라우저에 기능 추가를 기다리지 않고 하드웨어에 가까운 수준에서 필요한 기능을 직접 구현할 수 있게 되었다.
Emscripten으로 C++ 편집기 실행
- Figma 편집기는 C++로 작성하고 Emscripten으로 JavaScript로 크로스 컴파일했다.
- Emscripten은 asm.js를 대상으로 하며, asm.js는 JIT가 예측 가능하고 compact한 기계어를 생성하기 쉬운 JavaScript 부분집합이다.
- 이 방식의 장점:
- 메모리 배치를 직접 제어할 수 있어 64비트 부동소수점 중심인 JavaScript보다 32비트 float나 byte를 효율적으로 사용할 수 있다.
- 객체를 미리 할당한 typed array 영역에 배치해 JavaScript 가비지 컬렉터의 개입을 피한다.
- GC 중단으로 인한 프레임 저하를 줄여 60fps 달성에 유리하다.
- LLVM 최적화와 C++ 템플릿 특수화를 활용해 네이티브 성능의 약 2배 이내 수준까지 접근할 수 있다.
- asm.js에는 일반 JavaScript의 JIT 추론에 따른 deoptimization 지점이 없어 실행 성능이 더 예측 가능하다.
대용량 메모리와 브라우저 제약 대응
- Emscripten은 전체 메모리 공간을 하나의 큰 typed array에 담기 때문에 연속된 주소 공간을 충분히 확보해야 한다.
- 특히 32비트 Chrome on Windows에서는 ASLR이 주소 공간을 파편화해 256MB typed array조차 할당하지 못하는 문제가 있었다.
- Figma는 대형 이미지·기하 버퍼를 주 힙 외부의 별도 typed array에 저장하고, 이를 C++에서 참조하는
IndirectBufferAPI를 만들었다. - 이 방식은:
- 장시간 실행 시 메모리 파편화를 줄이고
- 32비트 브라우저의 제한된 주소 공간을 더 효율적으로 사용하며
- 64비트 브라우저의 31비트 typed array 크기 제한을 우회한다.
- 해당
IndirectBuffer구현은 오픈소스로 공개되었다.
asm.js 이후의 발전 방향
- WebAssembly는 asm.js 코드를 바이너리 형식으로 표현해 JavaScript 파싱 시간을 크게 줄이는 것을 목표로 한다.
- 당시 웹의 멀티스레딩은 Web Worker와 메시지 전달 방식에 의존했다.
- Shared Typed Array가 도입되면 여러 실행 흐름이 메모리를 공유하는 진정한 공유 메모리 기반 멀티스레딩이 가능해질 것으로 전망했다.
자체 렌더링 엔진
- 브라우저의 그래픽 기능을 그대로 사용하는 대신, 콘텐츠를 빠르고 플랫폼 간 일관되게 표시하기 위해 자체 렌더링 엔진을 구현했다.
- 이는 브라우저마다 다른 그래픽 구현과 동작 차이를 통제하고, 전문 디자인 도구에 필요한 고성능 렌더링을 직접 최적화하기 위한 선택이다.
- 제공된 글 내용은 자체 렌더링 엔진의 필요성을 설명하는 부분에서 끝나므로, 구체적인 렌더링 구조와 최적화 기법은 확인할 수 없다.
브라우저 기반 고성능 그래픽 애플리케이션을 만들 때는 DOM과 일반 JavaScript API만으로 해결하려 하기보다, C++/WebAssembly 계열의 실행 모델과 명시적인 메모리 관리, 자체 렌더링 계층을 고려하는 것이 효과적이다. 특히 60fps가 중요한 편집 도구라면 가비지 컬렉션과 브라우저별 렌더링 차이를 구조적으로 줄이는 설계가 중요하다.