plugin-system

2 개의 포스트

플러그인 보안 업데이트 (새 탭에서 열림)

Figma는 사용자의 생산성을 높이기 위해 서드파티 개발자가 기능을 확장할 수 있는 플러그인 시스템을 구축하면서, 보안과 성능이라는 두 마리 토끼를 잡는 것을 최우선 과제로 삼았습니다. 단순히 외부 코드를 실행하는 것을 넘어, 메인 애플리케이션의 안정성을 해치지 않으면서도 네이티브 기능처럼 매끄럽게 작동하는 환경을 만드는 것이 핵심입니다. 이를 위해 Figma는 브라우저의 기본 보안 모델을 넘어 WebAssembly와 가상화된 JavaScript 엔진을 결합한 독창적인 샌드박스 구조를 설계했습니다. ### 기존 격리 방식의 한계와 도전 * **Iframe의 제약:** 초기에 고려된 Iframe 방식은 보안상 이점이 있지만, 렌더링 오버헤드가 크고 메인 스레드와의 통신(postMessage)이 비동기적으로만 이루어져 성능 병목 현상이 발생했습니다. * **Web Worker의 부재:** Worker는 별도 스레드에서 실행되어 메인 UI를 차단하지 않지만, DOM에 직접 접근할 수 없고 데이터 복사 비용이 커서 복잡한 그래픽 작업이 많은 Figma에는 부적합했습니다. * **신뢰할 수 없는 코드:** 누구나 플러그인을 올릴 수 있는 환경에서 사용자의 비공개 디자인 데이터와 액세스 토큰을 보호하기 위해서는 완벽한 코드 격리가 필수적이었습니다. ### WebAssembly와 QuickJS를 이용한 보안 샌드박스 * **QuickJS의 도입:** Figma는 매우 가볍고 표준을 잘 준수하는 JavaScript 엔진인 QuickJS를 WebAssembly(Wasm)로 컴파일하여 브라우저 내부에서 실행했습니다. * **가상 머신 기반 격리:** 플러그인 코드는 메인 브라우저 엔진이 아닌, Wasm 위에서 돌아가는 QuickJS 엔진 안에서만 실행됩니다. 이를 통해 플러그인은 브라우저 API나 메인 앱의 메모리에 직접 접근하는 것이 원천적으로 차단됩니다. * **자원 제어:** Wasm 기반 샌드박스를 사용하면 플러그인이 사용하는 메모리 양을 제한하거나, 무한 루프에 빠진 플러그인을 강제로 종료하는 등의 세밀한 제어가 가능해져 앱 전체의 안정성을 보장합니다. ### 효율적인 통신을 위한 API 설계 * **프록시를 통한 간접 접근:** 플러그인이 Figma의 문서 객체 모델(SceneGraph)을 다룰 때, 직접적인 객체 참조 대신 프록시(Proxy)를 거쳐 필요한 데이터만 선별적으로 전달합니다. * **동기적 인터페이스의 구현:** 개발자 경험을 위해 비동기 통신 구조 위에서도 마치 동기적으로 코드를 작성하는 것과 같은 직관적인 API를 제공하여 플러그인 제작의 진입장벽을 낮췄습니다. * **성능 최적화:** 메인 앱과 샌드박스 간의 데이터 교환을 최소화하기 위해 변경 사항을 배치(Batch)로 처리하거나 필요한 시점에만 데이터를 가져오는 지연 평가(Lazy evaluation) 방식을 도입했습니다. ### 안전하고 지속 가능한 확장성 웹 환경에서 외부 코드를 실행해야 한다면 브라우저가 제공하는 기본 격리 기능에만 의존하기보다, WebAssembly와 같은 기술을 활용해 자체적인 샌드박스 계층을 구축하는 것이 보안과 성능을 모두 지키는 가장 확실한 방법입니다. Figma의 사례는 사용자 데이터 보호라는 엄격한 기준을 유지하면서도 개발자 생태계를 성공적으로 확장할 수 있는 기술적 청사진을 제시합니다.

Figma 데이터의 안전한 보호 및 (새 탭에서 열림)

Figma는 사용자가 직접 도구를 확장할 수 있는 플러그인 시스템을 구축하면서, 서드파티 코드가 메인 앱의 성능을 저하시키거나 보안 위협이 되지 않도록 하는 데 집중했습니다. 이를 위해 기존의 웹 샌드박싱 기술인 `<iframe>`이나 Web Worker의 한계를 넘어서는 새로운 구조를 설계했습니다. 최종적으로 Figma는 WebAssembly를 통해 QuickJS라는 경량 자바스크립트 엔진을 브라우저 내에서 실행함으로써, 안전하면서도 고성능을 유지하는 독자적인 플러그인 실행 환경을 구현해냈습니다. **기존 웹 기술 기반 샌드박싱의 한계** * **iframe 샌드박스:** 보안을 위해 `sandbox` 속성을 사용할 수 있지만, 동일한 메인 스레드를 공유하기 때문에 플러그인의 무거운 연산이 Figma 전체 UI를 멈추게 할 위험이 있습니다. 또한, 수백 개의 플러그인을 동시에 실행할 경우 메모리 오버헤드가 급증합니다. * **Web Worker:** 별도의 스레드에서 실행되므로 메인 UI를 방해하지 않지만, DOM에 직접 접근할 수 없고 메인 스레드와의 통신(postMessage)이 비동기적으로 이루어져야 하므로 성능 병목 현상이 발생합니다. * **보안과 성능의 트레이드오프:** 기존 방식들은 보안을 강화하면 성능이 떨어지고, 성능을 올리면 보안이나 구현 복잡도가 높아지는 딜레마가 있었습니다. **QuickJS와 WebAssembly를 활용한 격리 환경** * **Sandboxing inside Sandbox:** Figma는 C 언어로 작성된 경량 자바스크립트 엔진인 QuickJS를 WebAssembly(Wasm)로 컴파일하여 브라우저 위에서 실행했습니다. * **메모리 및 자원 제어:** Wasm 환경은 호스트 시스템(브라우저)과 완전히 격리된 선형 메모리 공간을 사용하므로, 플러그인 코드가 Figma의 내부 데이터나 브라우저 API에 직접 접근하는 것을 원천 차단합니다. * **결정론적 실행:** 플러그인이 사용할 수 있는 CPU 시간과 메모리 할당량을 세밀하게 제어할 수 있어, 악성 코드나 무한 루프에 빠진 플러그인이 전체 앱의 가용성을 해치지 않도록 방어합니다. **효율적인 플러그인 API 설계** * **가상 API 브릿지:** 플러그인이 Figma의 문서 구조(Scene graph)에 접근할 때, 실제 데이터를 복사하는 대신 프록시(Proxy) 객체를 통해 필요한 데이터만 동기적으로 가져오도록 설계하여 통신 비용을 최소화했습니다. * **로직과 UI의 분리:** 플러그인의 비즈니스 로직은 QuickJS-Wasm 환경에서 실행되지만, 사용자 인터페이스(UI)는 별도의 `<iframe>`에서 실행되도록 분리했습니다. 이를 통해 안전한 UI 렌더링과 강력한 로직 격리를 동시에 달성했습니다. **시스템 안정성과 확장성 확보** * **동기적 실행 모델:** 개발자들에게 익숙한 동기 방식의 프로그래밍 모델을 제공하면서도, 내부적으로는 Wasm 기반의 격리 계층을 두어 복잡한 플러그인도 안정적으로 구동되게 했습니다. * **브라우저 독립성:** 특정 브라우저의 보안 버그나 패치에 의존하지 않고, Figma가 직접 제어하는 런타임 엔진을 통해 일관된 보안 수준을 유지합니다. 웹 환경에서 신뢰할 수 없는 외부 코드를 실행해야 한다면, 브라우저가 제공하는 기본 기능을 넘어 WebAssembly를 활용한 자체 런타임 환경 구축을 고려해야 합니다. 이는 구현 난이도가 높지만, 사용자 보안과 애플리케이션의 성능이라는 두 마리 토끼를 잡을 수 있는 가장 확실한 방법입니다. 특히 대규모 협업 툴이나 확장성이 중요한 플랫폼 서비스라면 Figma와 같은 "계층화된 샌드박스" 접근 방식이 훌륭한 레퍼런스가 될 것입니다.