webassembly

19 개의 포스트

cloudflare4분 읽기큐레이션 요약

Kitesurf 소개: Cloudflare Workers의 V8 격리 환경에서 실행되는 에이전트 우선 브라우저

Cloudflare는 인간이 아닌 AI 에이전트에 최적화된 브라우저가 필요하다고 판단해, Workers 위에서 동작하는 헤드리스 브라우저 **Kitesurf**를 개발했다. Chromium이 제공하는 탭·확장 기능·정밀한 시각 렌더링보다 토큰 수, 확장성, 성능, 비용, 구조화된 콘텐츠 추출을 우선하며, 일반적인 에이전트 작업에서 CPU와 메모리 사용량을 크게 줄이는 것이 목표다. Kitesurf는 Browser Run에서 베타 서비스로 무료 제공된다. ## AI 에이전트에 기존 브라우저가 과한 이유 - Chromium 같은 브라우저 엔진은 인간 사용자를 중심으로 설계됐다. - AI 에이전트에는 다음 기능의 가치가 낮다. - 탭, 테마, 브라우저 확장 기능 - 여러 기기 간 동기화 - 픽셀 단위로 정확한 렌더링 - 부드러운 60fps 스크롤 - 반대로 에이전트에는 다음 요소가 중요하다. - 적은 토큰 수와 효율적인 컨텍스트 사용 - HTML 등 구조화된 콘텐츠 - 높은 처리량과 확장성 - 낮은 CPU·메모리 사용량과 비용 - AI 브라우저의 위협 모델도 인간용 브라우저와 다르다. - 임의의 웹사이트를 방문하는 에이전트는 모든 페이지를 신뢰할 수 없는 입력으로 다뤄야 한다. - 프롬프트 인젝션과 도구 사용 안전성이 핵심 보안 문제가 된다. ## Cloudflare 플랫폼이 가능하게 한 전환점 - Kitesurf는 Cloudflare Workers 위에서 전체적으로 실행된다. - 다음 기술 발전이 복잡한 브라우저 구현을 가능하게 했다. - Workers에서의 성숙한 WebAssembly 지원 - 동적 워커 - SQLite 기반 Durable Objects - 워커 간 RPC - 서비스 바인딩 - 향상된 Node.js 호환성 - 더 높은 실행 한도 - AI 에이전트용 브라우저 자동화 수요가 커지면서, 기존 Chromium 인스턴스를 에이전트마다 제공하는 방식의 비용 문제가 부각됐다. - Kitesurf는 이러한 환경에서 더 작고 저렴한 브라우저 실행 모델을 제공하려는 시도다. ## 초기 구현과 AI 활용 - 출발점은 Rust로 작성된 AI 자동화용 헤드리스 엔진 **obscura**였다. - Cloudflare 팀은 AI 에이전트의 도움을 받아 이를 Workers로 포팅했다. - 초기 결과는 불완전했지만, 명확한 실행 계획과 성공 조건을 제공하자 AI가 반복적으로 구현·검증하며 작동하는 프로토타입을 만들 수 있었다. - 이후 프로토타입을 기반으로 실제 대규모 서비스에 필요한 구조와 품질 기준을 마련했다. ## 테스트를 중심으로 한 개발 방식 - 복잡한 브라우저를 AI의 도움으로 빠르게 개발하려면, 구현 속도뿐 아니라 결과 품질을 통제해야 했다. - 이를 위해 가능한 많은 테스트를 성공 기준으로 제공했다. - **Web Platform Tests(WPT)**를 활용해 다음을 검증했다. - 웹 표준에 대한 기능 준수 여부 - 각 브라우저 기능의 구현 상태 - AI 에이전트가 작업을 완료했는지 판단할 수 있는 명확한 기준 - WPT만으로는 실제 웹사이트에서의 동작을 충분히 검증할 수 없기 때문에 추가 테스트도 도입했다. - Chromium과 Kitesurf 양쪽에서 실제 사이트를 대상으로 Puppeteer 통합 테스트 실행 - 여러 단계의 사용자 작업과 assertion 비교 - 각 단계의 렌더링 결과를 비교하는 시각적 회귀 테스트 - 예상하지 못한 렌더링 차이를 자동으로 표시 - AI 에이전트는 기능 구현을 담당하고, 사람은 아키텍처 설계와 구현 방식 검토에 집중하는 방식이다. ## Rust와 WebAssembly 선택 - Cloudflare는 C, C++, Rust 코드를 WebAssembly로 컴파일해 Workers에서 실행할 수 있다. - Emscripten을 사용하면 많은 의존성과 모의 계층이 추가되어 결과 바이너리가 커지고 실행이 느려질 수 있다. - Kitesurf는 가능한 한 네이티브 Rust로 구현하고 `wasm-bindgen`을 통해 WebAssembly로 직접 컴파일했다. - 이를 통해 불필요한 에뮬레이션 계층을 피하고, 성능과 안정성을 높였다. ## 예외 처리와 장애 격리 - 웹페이지는 잘못된 HTML, 예상 밖의 입력, 악의적인 콘텐츠를 포함할 수 있으므로 브라우저는 일부 기능이 실패해도 세션 전체를 중단해서는 안 된다. - Kitesurf의 원칙은 다음과 같다. - 오류가 발생하면 죽은 세션 대신 빈 프레임이나 누락된 요소로 처리 - 모든 경계에서 예외를 포착 - 안전하고 비어 있는 기본값 사용 - 문제를 진단할 수 있을 만큼 충분한 로그 기록 - 목표는 페이지 일부가 손상되더라도 브라우저 요청 자체는 계속 유지하는 것이다. ## 페이지와 컴포넌트의 격리 - AI 에이전트는 작업에 따라 임의의 출처에서 코드를 실행하거나 페이지를 방문할 수 있다. - 따라서 모든 페이지 로드를 신뢰할 수 없는 입력으로 취급하고, 모든 세션을 새로 시작한다. - 각 컴포넌트는 필요한 리소스에만 접근하도록 제한한다. - Workers의 격리 모델이 기본적인 보안 경계를 제공하지만, 그것만으로 충분하지 않다. - 애플리케이션 수준에서도 컴포넌트별 접근 권한을 정의해야 한다. - 한 페이지의 데이터나 리소스가 다른 페이지로 유출되지 않도록 별도로 보장해야 한다. ## 가능한 한 무상태로 설계 - 상태가 많을수록 장애 발생 후 복구 비용이 커진다. - 무상태 컴포넌트는 다음 장점을 가진다. - 실패하면 새 인스턴스를 만들고 요청을 다시 재생하면 됨 - 필요할 때 병렬로 대량 실행 가능 - 멈춘 인스턴스를 즉시 폐기 가능 - 트래픽이 급증하는 자동화 작업에 맞춰 수요 기반으로 확장 가능 - 사용한 만큼만 비용을 지불하고 작업 종료 후 리소스를 제거 가능 - 따라서 상태가 꼭 필요하지 않은 컴포넌트는 가능한 한 무상태로 구현한다. ## 실용적인 결론 AI 브라우저는 인간용 브라우저를 그대로 축소하는 것이 아니라, 구조화된 데이터 처리·낮은 비용·높은 확장성·강한 격리를 중심으로 다시 설계해야 한다. 브라우저 자동화 서비스를 구축할 때는 표준 테스트뿐 아니라 실제 사이트 통합 테스트와 시각적 회귀 테스트를 병행하고, Rust/WebAssembly·무상태 설계·방어적인 예외 처리를 활용하는 것이 효과적이다.

원문 읽기(새 탭에서 열림)
cloudflare3분 읽기큐레이션 요약

Workers RPC, 이제 Python과 JavaScript 간에도 작동

Cloudflare Workers의 RPC가 이제 JavaScript/TypeScript와 Python 사이에서도 동작해, 서로 다른 언어로 작성된 Worker가 별도 API 스키마나 의존성 없이 메서드·객체·함수를 주고받을 수 있게 되었습니다. 개발자는 다른 언어의 Worker를 로컬 라이브러리처럼 호출할 수 있으며, 예외·비동기 반환값·콜백도 자연스럽게 전달됩니다. 이를 위해 Pyodide FFI와 사용자 정의 타입 변환 계층을 결합해 두 언어의 타입 체계를 연결했습니다. ## 언어 간 RPC 호출 - TypeScript Worker에서 정의한 `add(a, b)` 메서드를 Python Worker에서 `await rpc.add(42, 144)`처럼 호출할 수 있습니다. - 반대로 Python Worker의 메서드를 JavaScript/TypeScript에서 호출하는 것도 가능합니다. - 별도 패키지나 직렬화 스키마 없이 Service binding만 설정하면 됩니다. ```json { "services": [ { "binding": "RPC", "service": "ts-rpc-server", "entrypoint": "RpcService" } ] } ``` - RPC 대상은 Worker의 엔트리포인트 클래스에 정의된 메서드로 노출됩니다. ## 일반 함수처럼 동작하는 RPC - JavaScript/TypeScript에서는 Promise, Python에서는 Future처럼 비동기 결과를 받습니다. - 원격 메서드에서 발생한 예외는 호출한 쪽의 RPC 호출 지점에서 다시 발생합니다. - Structured Cloneable 타입을 인자와 반환값으로 전달할 수 있습니다. - JavaScript의 `Date`는 Python의 `datetime`으로 변환됩니다. - 숫자, 불리언, 객체, 배열 등 기본 타입도 대응되는 언어의 타입으로 변환됩니다. - 함수를 다른 Worker에 전달하거나 반환할 수 있습니다. - 상대 Worker가 해당 함수를 호출하면 원래 Worker로 역방향 RPC가 발생합니다. - 대부분의 Worker 간 RPC는 네트워크를 거치지 않고 동일한 스레드에서 실행되므로, 같은 Worker 내부 함수 호출에 가까운 성능을 제공합니다. - 구현은 `workerd`와 `workers-runtime-sdk`의 오픈 소스 코드로 제공됩니다. ## JavaScript와 Python의 호출 방식 차이 두 언어는 복잡한 인자를 표현하는 방식이 다릅니다. - JavaScript/TypeScript는 보통 객체를 전달합니다. ```ts myFunction({ key: "myKey", value: true, optional: 1 }); ``` - Python은 위치 인자와 키워드 인자를 사용합니다. ```python my_complex_function("myKey", True, optional=1) ``` - 목표는 호출자가 상대 언어의 내부 표현을 의식하지 않도록 만드는 것입니다. - Python에서 JavaScript 메서드의 옵션 객체를 다음 두 방식으로 모두 전달할 수 있습니다. ```python JSRPC.get("myKey", {"type": "text"}) JSRPC.get("myKey", type="text") ``` - Pyodide FFI가 두 호출을 JavaScript가 기대하는 옵션 객체 형태로 변환합니다. ## Pyodide FFI 기반 타입 변환 Pyodide는 WebAssembly로 컴파일된 CPython이며, Python과 JavaScript 사이의 기본 타입 변환 기능을 제공합니다. | Python 타입 | JavaScript 타입 | |---|---| | `int`, `float` | `Number` | | `bool` | `Boolean` | | `dict` | `Object` | | `list` | `Array` | - 직접 변환하기 어려운 사용자 정의 클래스나 함수는 Proxy 객체로 표현됩니다. - Proxy는 다른 언어의 속성 접근과 메서드 호출을 원격으로 전달합니다. - 이 방식으로 Python 함수를 JavaScript에 콜백으로 넘기는 등의 패턴을 구현할 수 있습니다. ## Cloudflare Workers 객체 처리의 과제 - `Request`, `Response`, `Blob`, `File` 같은 Cloudflare Workers의 Web API 객체는 일반 Python 타입과 달리 Pyodide가 자동 변환하지 못합니다. - 기본 동작에서는 이런 객체를 JavaScript Proxy로 전달합니다. - 기능적으로는 사용할 수 있지만, Python 개발자가 JavaScript 구현 세부사항과 Proxy를 계속 의식해야 하는 문제가 있습니다. - 따라서 언어 간 RPC를 완전히 자연스럽게 만들려면 표준 타입뿐 아니라 Workers 전용 객체에 대한 별도 변환 전략도 필요합니다. ## 실용적인 결론 언어별 강점을 활용해 하나의 Workers 애플리케이션을 구성하려는 경우, 이번 기능은 유용한 선택지입니다. Service binding만으로 TypeScript와 Python Worker를 연결할 수 있으므로, 공통 API 서버나 수동 직렬화 계층을 별도로 만들기보다 RPC를 통해 기능을 라이브러리처럼 분리하는 방식을 고려할 수 있습니다.

원문 읽기(새 탭에서 열림)
cloudflare원문

신뢰할 수 있는 Rust Worker 만들기: wasm-bindgen에서의 패닉 및 중단 복구 (새 탭에서 열림)

Cloudflare Workers 환경에서 Rust로 작성된 WebAssembly(Wasm)는 예기치 못한 패닉(Panic)이나 중단(Abort)이 발생할 경우 런타임이 정의되지 않은 상태로 남아 동일한 인스턴스의 다른 요청까지 실패하게 만드는 '샌드박스 오염' 문제를 안고 있었습니다. 이를 해결하기 위해 Cloudflare는 `wasm-bindgen` 메인 프로젝트와 협력하여 Wasm 예외 처리 기능을 활용한 `panic=unwind` 지원과 중단 복구 메커니즘을 도입했습니다. 결과적으로 단일 요청의 실패가 전체 서비스 중단으로 이어지는 것을 방지하고, 상태 유지가 필요한 애플리케이션에서도 안정적인 오류 복구가 가능해졌습니다. ### 초기 대응 및 완화 전략 본격적인 기능 개선에 앞서, Cloudflare는 운영 환경에서의 피해를 최소화하기 위해 사용자 정의 패닉 핸들러와 JavaScript 프록시를 활용한 초기 완화책을 적용했습니다. * **패닉 핸들러 도입:** Rust 내부에 상태를 추적하는 패닉 핸들러를 설치하여 실패가 발생하면 전체 애플리케이션을 다시 초기화하도록 설정했습니다. * **Proxy 기반 간접 참조:** JavaScript 영역에서 Rust 호출 경계를 `Proxy`로 감싸 모든 진입점을 캡슐화하고, 실패 시 안전하게 Wasm 모듈을 재로드했습니다. * **한계점:** 이 방식은 서비스 가용성은 높였으나, 전체 애플리케이션을 재시작해야 하므로 메모리에 상태를 보관하는 Durable Objects 같은 서비스에서는 데이터 손실이 발생하는 단점이 있었습니다. ### Wasm 예외 처리를 통한 panic=unwind 구현 상태를 보존하면서 오류를 복구하기 위해 Wasm의 최신 표준인 예외 처리(Exception Handling) 제안을 활용하여 Rust의 패닉 언와인딩(Unwinding)을 구현했습니다. * **컴파일 옵션 변경:** `RUSTFLAGS='-Cpanic=unwind'`와 `-Zbuild-std`를 사용하여 표준 라이브러리가 언와인딩을 지원하도록 다시 빌드했습니다. * **wasm-bindgen 툴체인 업데이트:** Wasm 파서인 Walrus가 `try`, `catch`, `rethrow` 명령어를 인식하도록 수정하고, JavaScript와 Rust 경계에서 예외가 올바르게 전달되도록 개선했습니다. * **Boundary 처리:** Rust에서 발생한 패닉이 JavaScript의 `PanicError`로 변환되도록 했으며, `extern "C-unwind"`를 통해 함수 호출 경계를 넘나드는 언와인딩을 허용했습니다. * **클로저 안전성:** `MaybeUnwindSafe` 트레잇을 도입하여 언와인딩 시 안전하지 않은 참조를 캡처하는 클로저를 체크하고, 필요한 경우 패닉 시 즉시 중단되는 `Closure::new_aborting` 변형을 제공합니다. ### 중단(Abort) 복구 및 포이즌 필(Poison Pill) 메커니즘 메모리 부족(OOM)과 같이 언와인딩이 불가능한 '중단' 상황에서는 메모리 상태가 오염될 가능성이 높기 때문에, 해당 인스턴스의 재실행을 원천 봉쇄하는 전략을 사용합니다. * **포이즌 필 플래그:** `wasm-bindgen`은 이제 모든 Wasm 모듈에 내부 상태 플래그를 주입합니다. 만약 Rust 코드에서 중단이 발생하면 이 플래그가 즉시 설정됩니다. * **재실행 방지:** 이후 JavaScript에서 해당 Wasm 인스턴스의 어떤 함수라도 호출하려고 하면, Rust 코드를 실행하기 전에 플래그를 먼저 확인하여 즉시 JavaScript 에러를 발생시킵니다. * **안전성 보장:** 이를 통해 오염된 메모리 상태에서 코드가 다시 실행되어 발생할 수 있는 보안 취약점이나 예측 불가능한 동작을 완전히 차단합니다. Wasm 기반의 Rust 서비스를 운영한다면 최신 버전의 `wasm-bindgen`과 `workers` 라이브러리를 사용하고, 특히 Durable Objects와 같이 상태 보존이 중요한 경우 `panic=unwind` 설정을 활성화할 것을 권장합니다. 이는 단순한 안정성 향상을 넘어, Wasm이 네이티브 환경과 동등한 수준의 오류 복구 능력을 갖추게 되었음을 의미합니다.

discord4분 읽기큐레이션 요약

모든 디스코드

Discord는 음성·영상 통화의 표준을 DAVE 기반 종단간 암호화(E2EE)로 전환하며, 브라우저·콘솔·Social SDK까지 지원 범위를 확대한다. 2026년 3월 1일부터는 DAVE를 지원하지 않는 클라이언트와 앱이 Discord 통화에 참여할 수 없다. 브라우저 환경에서는 WebRTC Encoded Transform API, Web Worker, WebAssembly를 조합해 보안성과 성능을 확보했다. ## DAVE의 전 플랫폼 확대 - DAVE는 Discord의 음성·영상 통화에 E2EE를 제공하는 프로토콜이다. - 이미 매일 수천만 건의 통화에 적용되고 있으며, 이번 작업으로 다음 플랫폼까지 지원한다. - 웹 브라우저 - 콘솔 - Discord Social SDK - 2026년 3월 1일부터 비-DAVE 클라이언트는 음성 채널과 영상 통화에 참여할 수 없다. - 이는 기존 실험적 도입을 종료하고 DAVE를 Discord 통화의 기본 보안 표준으로 삼는 단계다. ## WebRTC Encoded Transform API 활용 - 브라우저에서는 WebRTC 미디어 파이프라인 내부의 인코딩 전후 지점에서 오디오·영상 프레임을 암호화한다. - Encoded Transform API를 사용하면 브라우저의 코덱과 WebRTC 기능을 유지하면서 DAVE 암호화를 삽입할 수 있다. - Discord는 H.265, AV1 등 최신 코덱의 하드웨어 지원도 활용할 수 있도록 설계했다. - 더 높은 화질 - 더 효율적인 대역폭 사용 - 플랫폼별 코덱 지원 유지 ## Firefox에서 발견한 교착 상태 문제 - 초기 테스트에서는 Firefox에서도 DAVE가 정상 작동했지만, 실제 Discord 통화에서는 암호화용 Web Worker가 프레임을 받지 못했다. - 원인은 Firefox의 `FrameTransformerProxy`가 비디오 데이터가 너무 일찍 전달될 때 교착 상태에 빠지는 엣지 케이스였다. - 지연 처리를 위해 저장된 비디오 데이터가 뮤텍스를 사용했으며, 변환 작업이 같은 뮤텍스에 재진입하면서 재귀적 교착이 발생했다. - Discord 팀은 Firefox를 직접 빌드해 브라우저 수준에서 문제를 추적하고 Mozilla에 패치를 제출했다. - 수정 사항은 Firefox 142.0에 포함되며, DAVE를 사용하려면 최소 Firefox 142.0이 필요하다. ## Web Worker 기반 암호화 구조 - 각 Discord 연결은 미디어 암호화와 복호화를 담당하는 전용 Web Worker를 사용한다. - 일반 통화에서는 다음 미디어를 처리한다. - 통화 오디오 - 카메라 영상 - 화면 공유나 게임 스트림은 오디오와 영상별로 별도의 Worker가 처리한다. - 각 WebRTC 스트림에는 고유한 SSRC가 있으며, DAVE는 SSRC를 기준으로 프레임에 사용할 대칭키를 식별한다. - Worker는 다음과 같은 최소한의 상태만 유지한다. - 송수신 오디오·영상 정보 - SSRC와 사용자 ID의 매핑 - 각 사용자에 대한 암호화 키 ## 메인 스레드와 암호화 Worker의 역할 분리 - 메인 JavaScript 스레드는 WebRTC 연결과 참여자, 미디어 트랙을 관리한다. - 사용자 입장·퇴장 시 필요한 MLS(Message Layer Security) 협상도 메인 스레드에서 수행한다. - MLS 그룹 변경을 Worker에서 처리하지 않기 때문에, Worker가 프레임 암호화를 끝낼 때까지 메인 스레드가 대기할 필요가 없다. - MLS 상태가 바뀌면 메인 스레드가 Worker에 비동기 메시지를 보내 암호화 상태를 갱신한다. - 이 구조는 멤버 변경 중에도 미디어 암호화를 계속 수행해 통화 지연을 줄인다. ## 검증된 C++ 구현의 WebAssembly 재사용 - Discord는 데스크톱과 모바일에서 이미 대규모로 검증된 DAVE C++ 코드베이스를 WebAssembly로 컴파일했다. - 동일한 암호화 구현을 여러 플랫폼에서 재사용하면 플랫폼별 재구현으로 인한 보안 취약점과 동작 차이를 줄일 수 있다. - WebRTC 패킷에는 라우팅과 패킷 처리를 위해 일부 메타데이터가 평문으로 남아 있어야 한다. - 암호화된 데이터가 WebRTC 패킷화기, SFU, 역패킷화기를 거치는 동안 변경되면 복호화가 실패한다. - 따라서 프레임을 바이트 단위로 파싱하고 필요한 부분만 선택적으로 암호화해야 한다. - 이 작업은 JavaScript로 직접 구현하기에는 복잡하고 오류 가능성이 높지만, WebAssembly를 사용하면 네이티브에 가까운 성능으로 처리할 수 있다. ## WebAssembly와 SubtleCrypto의 성능 절충 - WebAssembly는 프레임 파싱과 선택적 암호화 로직을 효율적으로 처리한다. - 반면 암호화 연산 자체는 브라우저의 네이티브 API인 `SubtleCrypto`보다 약간 느릴 수 있다. - Discord의 벤치마크는 두 방식의 성능 차이가 단순하지 않으며, 실제 병목이 암호화 연산뿐 아니라 프레임 처리와 데이터 이동에도 있음을 시사한다. - 최종 선택은 단순한 최고 암호화 속도보다 다음 요소를 종합한 결과다. - 검증된 코드 재사용 - 플랫폼 간 동작 일관성 - 프레임 파싱 성능 - 보안 로직의 유지보수성 ## 실용적인 권장 사항 - Discord 통화에 계속 참여하려면 2026년 3월 1일 전까지 DAVE 지원 클라이언트로 업데이트해야 한다. - Firefox 사용자는 최소 Firefox 142.0 이상으로 업그레이드해야 한다. - Discord용 앱이나 Social SDK를 개발 중이라면 DAVE 지원 여부를 확인하고, 비-E2EE 통화에 의존하는 구현을 제거해야 한다.

원문 읽기(새 탭에서 열림)
datadog원문

서버 의존성 없이 실시간 클라이언트 사이드 노이즈 억제 라이브러리를 구축한 방법 (새 탭에서 열림)

Datadog의 CoScreen 팀은 원격 협업 중 발생하는 배경 소음을 실시간으로 제거하기 위해, 높은 성능과 이식성을 갖춘 오픈소스 라이브러리인 `dtln-rs`를 개발했습니다. 기존의 노이즈 억제 도구들은 WebRTC와의 통합이 어렵거나 고성능 서버 자원을 요구한다는 한계가 있었으나, 이 라이브러리는 클라이언트 측에서 효율적으로 동작하도록 설계되었습니다. 결과적으로 M1 맥북 프로 기준 1초의 오디오를 단 33ms 만에 처리하며, 서버 의존성 없이 다양한 플랫폼에서 고품질의 실시간 소음 제거를 가능하게 합니다. **dtln-rs: 경량화된 실시간 노이즈 억제 라이브러리** * DTLN(Dual-Signal Transformation LSTM Network) 모델을 기반으로 구축된 Rust 언어 기반의 프로젝트입니다. * WebAssembly(WASM), Native Rust, Node.js 네이티브 모듈 등 다양한 타겟으로 빌드할 수 있어 웹 브라우저와 네이티브 앱 모두에 쉽게 통합 가능합니다. * 실제 테스트에서 이웃의 잔디 깎는 기계 소음을 완전히 제거할 정도로 뛰어난 성능을 보여주었으며, 이를 통해 사용자에게 실제적인 가치를 전달합니다. **DTLN 모델의 작동 원리와 효율성** * STFT(단시간 푸리에 변환)를 사용하여 소리를 작은 단위로 분해하고, 주파수별 볼륨(크기 스펙트럼)과 위상(Phase) 정보를 분석합니다. * LSTM(장단기 메모리) 신경망이 포함된 모델을 통해 분석된 데이터 중 어떤 부분이 음성이고 어떤 부분이 소음인지 실시간으로 판단합니다. * 위상 정보와 크기 성분을 모두 활용하는 딥러닝 방식 덕분에 에어컨 소음, 카페 소음, 종이 부스럭거리는 소리 등 다양한 환경에 동적으로 적응하며 즉각적인 감쇠가 가능합니다. **기존 기술의 한계와 개발 배경** * 기존의 고성능 AI 노이즈 제거 모델들은 대부분 강력한 서버 하드웨어를 필요로 하며, 이는 추가적인 지연 시간(Latency)과 막대한 서버 비용을 발생시킵니다. * WebRTC는 널리 쓰이는 오픈소스 기술임에도 불구하고 내장된 노이즈 제거 기능은 구세대 솔루션에 머물러 있어 현대적인 협업 도구들의 품질 요구 수준을 충족하지 못했습니다. * Google 등 대기업이 사용하는 최첨단 모델은 비공개 소스이거나 전용 서버 인프라에 종속되어 있어, 소규모 팀이나 일반 개발자들이 자신들의 앱에 고품질 기능을 구현하기에는 제약이 컸습니다. 실시간 오디오 및 비디오 애플리케이션을 개발하면서 서버 비용 부담 없이 고성능 노이즈 캔슬링 기능을 추가하고 싶다면 `dtln-rs`를 검토해 보시기 바랍니다. 클라이언트 측 리소스를 효율적으로 활용하면서도 WebRTC와 매끄럽게 결합되는 이 라이브러리는 사용자 경험을 한 단계 끌어올리는 실용적인 해결책이 될 것입니다.

figma원문

피그마의 TypeScript (새 탭에서 열림)

피그마(Figma)는 자사의 모바일 렌더링 엔진의 핵심 언어였던 자체 개발 언어 'Skew'를 산업 표준인 TypeScript로 완전히 전환하는 데 성공했습니다. 과거 성능 최적화를 위해 도입했던 Skew가 팀 규모 확장에 따라 생산성 저해와 생태계 부재라는 한계에 부딪히자, 피그마는 일상적인 개발 흐름을 방해하지 않으면서도 자동화된 마이그레이션을 완수했습니다. 결과적으로 피그마는 성능 손실 없이 더 나은 개발 환경과 최신 JavaScript 생태계의 이점을 누릴 수 있게 되었습니다. ### 자체 개발 언어 Skew의 도입과 한계 * **성능 중심의 탄생:** 초기 피그마는 웹과 모바일 모두에서 프로토타입 뷰어를 구현하기 위해 Skew를 개발했습니다. 당시 Skew는 정적 타이핑과 더불어 상수 폴딩(constant folding), 가상 함수 호출 최적화(devirtualization) 등 고급 컴파일러 최적화를 통해 JavaScript보다 뛰어난 성능을 제공했습니다. * **확장의 걸림돌:** 하지만 팀이 커지면서 Skew는 신규 입사자의 적응을 어렵게 만들고, 린터(linter)나 정적 분석기 같은 현대적인 개발 도구 생태계를 활용할 수 없다는 단점이 부각되었습니다. * **기능의 부재:** async/await와 같은 현대적인 JavaScript 기능이 부족했고, 피그마 내부의 다른 코드베이스와 통합하는 데에도 높은 비용이 발생했습니다. ### TypeScript 전환이 가능해진 기술적 배경 * **WebAssembly(Wasm)의 보편화:** 2018년 이후 모바일 브라우저에서 WebAssembly 지원이 확대되었고, 2020년경에는 모바일에서도 안정적인 성능을 발휘하게 되었습니다. * **C++ 엔진으로의 교체:** Skew로 작성되었던 파일 로딩 등 핵심 성능 경로를 WebAssembly로 컴파일되는 C++ 엔진으로 대체함으로써, 나머지 로직을 TypeScript로 전환하더라도 전체 성능에 미치는 영향이 미미해졌습니다. * **팀 규모의 성장:** 개발 경험(DX) 개선에 전념할 수 있는 리소스를 확보할 만큼 팀이 성장하면서 자동화된 마이그레이션 도구 개발이 가능해졌습니다. ### 안전한 전환을 위한 3단계 자동화 프로세스 * **1단계 (Skew 작성, Skew 빌드):** 기존 빌드 프로세스를 유지하면서 Skew 코드를 TypeScript로 변환하는 트랜스파일러를 개발했습니다. 변환된 TypeScript 코드를 깃허브에 체크인하여 개발자들이 미래의 코드 모습을 확인할 수 있게 했습니다. * **2단계 (Skew 작성, TypeScript 빌드):** 개발자는 여전히 Skew로 코드를 짜지만, 실제 프로덕션 빌드는 트랜스파일러를 거친 TypeScript 코드로 진행했습니다. 이 과정에서 유닛 테스트를 통과시키고 타입 오류를 점진적으로 수정하며 안정성을 확보했습니다. * **3단계 (TypeScript 작성, TypeScript 빌드):** 특정 시점에 Skew 코드 생성을 중단하고 모든 Skew 소스 파일을 삭제했습니다. 이후 모든 개발자는 TypeScript를 직접 작성하게 되었으며, CI/CD 파이프라인도 TypeScript 기반으로 완전히 전환되었습니다. ### 실용적인 결론 및 시사점 피그마의 사례는 서비스 초기 성능을 위해 도입한 커스텀 기술이 성숙기에는 오히려 부채가 될 수 있음을 보여줍니다. 특히 대규모 코드베이스를 전환할 때는 **'점진적인 롤아웃'**과 **'자동화된 트랜스파일링'**이 핵심입니다. Skew와 TypeScript 간의 시맨틱 차이(예: 네임스페이스 초기화 순서 등)로 발생할 수 있는 런타임 오류를 방지하기 위해, 자체 컴파일러를 수정하여 제어권을 확보한 점은 기술적 난관을 극복한 훌륭한 전략으로 평가됩니다.

figma3분 읽기큐레이션 요약

서버 측 샌드박싱

서버 측 샌드박싱은 악성 입력을 처리하는 애플리케이션의 취약점이 전체 인프라로 확산되는 것을 막는 방어 계층이다. 이미지·데이터 처리 라이브러리처럼 메모리 안전성이 낮고 취약점이 반복적으로 발견되는 소프트웨어를 완전히 제거하거나 재작성하기는 현실적으로 어렵기 때문에, VM·컨테이너·seccomp 등을 이용해 실행 환경과 접근 가능한 자원을 제한해야 한다. 중요한 것은 특정 기술 하나를 선택하는 것이 아니라 보안성, 운영 복잡도, 성능, 격리 수준 사이의 트레이드오프를 workload 특성에 맞게 평가하는 것이다. ## 사용자 입력을 처리할 때 발생하는 위험 - 이미지 처리, 파싱, 압축, 썸네일 생성은 SaaS 애플리케이션에서 흔히 필요한 작업이다. - 이러한 기능은 C++ 같은 메모리 비안전 언어로 작성된 라이브러리에 의존하는 경우가 많다. - 해당 라이브러리는 원래 악의적인 입력을 처리하도록 설계되지 않았으며, 메모리 손상 취약점이 반복적으로 발견되어 왔다. - 대표적인 사례가 2016년 ImageMagick에서 발견된 **ImageTragick**이다. - 사용자가 제공한 이미지를 서버에서 처리하는 서비스가 원격 코드 실행 공격에 노출될 수 있었다. - 모든 버그와 취약점을 사전에 제거하는 것은 사실상 불가능하므로, 취약점이 발생하더라도 피해 범위를 제한하는 방어책이 필요하다. ## 서버 측 샌드박싱의 역할 - 샌드박싱은 애플리케이션이나 작업을 제한된 환경에서 실행하는 **workload isolation** 기법이다. - 공격자가 취약한 작업을 장악하더라도 다음과 같은 접근을 제한하는 것이 목표다. - 다른 작업의 사용자 데이터 - 운영 환경의 내부 서비스 - 파일 시스템과 네트워크 자원 - 추가 시스템으로의 lateral movement - Figma는 C++로 작성된 서버 측 렌더링 시스템인 RenderServer와 사용자 생성 그래픽 데이터를 처리하는 서드파티 라이브러리를 사용한다. - 이러한 작업을 인프라 내부에서 직접 실행하면 단 하나의 심각한 버그가 다른 사용자 데이터나 운영 시스템 침해로 이어질 수 있다. - 모든 unsafe 코드를 메모리 안전 언어로 다시 작성하고 정적 분석으로 정확성을 증명하는 방법도 있지만, 비용과 시간이 크며 완벽한 보안을 보장하지도 않는다. - 따라서 취약점 예방과 함께, 취약점이 악용되었을 때 영향 범위를 줄이는 격리 전략을 병행한다. ## VM, 컨테이너, seccomp - 글에서는 서버 측 샌드박싱의 대표적인 구현 방식으로 다음 세 가지를 소개한다. - **가상 머신(VM)**: 하이퍼바이저 위에서 각 게스트 운영체제를 실행한다. 인프라와 작업 사이에 하이퍼바이저 및 게스트 OS 계층이 존재한다. - **컨테이너**: 호스트 운영체제의 기능과 커널을 공유하면서 컨테이너 엔진을 통해 작업을 분리한다. VM보다 가볍게 실행할 수 있지만 격리 특성이 다르다. - **seccomp**: 프로세스가 호출할 수 있는 시스템 콜을 제한하는 Linux 보안 기능이다. - 각 방식은 격리 강도, 실행 비용, 성능, 시작 시간, 운영 편의성, 설정 복잡도 등에서 서로 다른 특성을 가진다. - 실제 환경에서는 단일 기술만 사용하기보다 workload의 신뢰 수준과 필요한 권한에 따라 여러 샌드박싱 primitive를 조합할 수 있다. ## 샌드박싱 선택 시 고려할 점 - 샌드박싱은 보안 취약점을 없애는 기술이 아니라, 취약점이 발생했을 때 공격의 범위와 피해를 제한하는 방어 계층이다. - 선택 과정에서는 다음을 함께 검토해야 한다. - 처리 대상이 사용자 입력인지, 내부에서 신뢰할 수 있는 데이터인지 - 작업이 필요한 파일·네트워크·시스템 콜의 범위 - 강한 격리에 필요한 성능 및 비용 - 샌드박스의 생성·폐기와 패치·모니터링 운영 부담 - 샌드박스 자체의 탈출 가능성과 호스트에 미치는 영향 - 샌드박싱 기술은 과거보다 안정적이고 실용적으로 발전했지만, 여전히 보안 연구와 운영 경험이 중요한 분야다. 악성 입력을 처리하는 기능은 메모리 안전 언어로의 전환만으로 보호하려 하기보다, 최소 권한과 강한 실행 격리를 함께 적용하는 것이 현실적이다. 특히 사용자 데이터를 다루는 고위험 작업은 VM이나 컨테이너 격리를 검토하고, 불필요한 시스템 콜은 seccomp로 추가 제한하는 접근이 유용하다.

원문 읽기(새 탭에서 열림)
figma4분 읽기큐레이션 요약

Figma를 빠르게 유지하기

Figma는 2018년 한 대의 MacBook으로 운영하던 성능 테스트 체계가 제품과 조직의 성장으로 한계에 이르자 전면적인 개편을 추진했습니다. 플러그인, FigJam, Dev Mode 등 기능이 늘고 코드베이스가 복잡해지면서 기존의 소수 대형 파일 테스트만으로는 성능 회귀를 조기에 발견하기 어려워졌습니다. 이에 Figma는 모든 코드 변경을 대상으로 실제 하드웨어에서 병렬 성능 테스트를 실행하고, 10분 이내에 결과를 제공하는 확장 가능한 시스템을 목표로 삼았습니다. ## 한 대의 MacBook으로 시작한 성능 테스트 - 2018년 Figma는 한 대의 MacBook에서 동일한 테스트 시나리오를 반복 실행했습니다. - 테스트 결과와 실행 시간은 약 한 시간 간격으로 공유 대시보드에 기록됐습니다. - 당시에는 소수의 대형 디자인 파일만으로도 주요 성능 문제를 확인할 수 있었습니다. - 문서 렌더러 구조를 개선하고 WebAssembly 관련 버그를 해결하면서 Figma의 성능을 약 3배 향상시킨 사례도 있었습니다. - 작은 조직에서 단일 컴퓨터로 테스트하는 방식은 단순하고 비용이 낮다는 장점이 있었습니다. ## 제품과 조직의 성장으로 드러난 한계 - 5년 동안 코드베이스가 커지고 다음과 같은 기능이 추가됐습니다. - 플러그인 - Community 기능 - FigJam - Dev Mode - 수많은 제품 업데이트 - 기존에 사용하던 몇 개의 대형 디자인 파일은 늘어나는 기능과 예외 상황을 충분히 대표하지 못했습니다. - 기능별로 세밀한 성능 테스트를 작성하는 것이 이상적이었지만, 엔지니어와 매니저가 400명 이상으로 늘면서 모든 변경 사항을 한 사람이 추적하기 어려워졌습니다. - 성능 테스트 대상과 코드 변경이 많아지면서 단일 노트북만으로는 출시 전 성능 회귀를 안정적으로 발견할 수 없었습니다. - 원격 근무가 시작된 뒤에도 사무실에 있던 MacBook은 계속 테스트를 실행했고, 결국 2020년 10월 과열됐습니다. - 다른 노트북으로 같은 환경을 재현하려 했지만 테스트가 원활하게 실행되지 않아 새로운 시스템이 필요해졌습니다. ## 세밀한 성능 테스트의 필요성 - **세밀한 성능 테스트(granular performance test)**는 특정 기능이나 사용 패턴을 대규모 조건에서 검증하는 테스트입니다. - 예를 들어 Figma는 다음과 같은 상황을 시뮬레이션할 수 있습니다. - 100명의 협업 편집자가 동시에 파일을 편집 - 여러 사용자가 레이어를 이동 - 동시에 새로운 텍스트 입력 - 사용자가 빠르게 화면을 패닝 - 이런 테스트는 특정 기능의 성능 영향을 정확하게 파악하는 데 유용합니다. - 하지만 기능 수와 엣지 케이스가 계속 증가하면 모든 기능을 수동으로 테스트하는 방식은 조직 규모에 맞게 확장되지 않습니다. ## 새 성능 테스트 시스템의 목표 - Figma는 시스템을 처음부터 다시 설계하며 세 가지 문제를 해결하려 했습니다. - 성능에 영향을 줄 수 있는 기능의 증가 - 테스트 하드웨어 운영의 어려움 - 신뢰할 수 있는 성능 지표의 부족 - 메인 모노레포에 제출되는 **모든 코드 변경**을 테스트해 성능 회귀를 개발 초기에 발견하는 것을 목표로 삼았습니다. - 사용자가 버그를 보고한 뒤 대응하는 대신, 기능이 배포되기 전에 성능 문제를 예방하려 했습니다. - Figma 사용자는 하루에도 여러 시간 제품을 사용하기 때문에 작은 지연도 작업 흐름에 큰 영향을 줄 수 있다고 판단했습니다. - 성능을 기능 개발 이후의 사후 대응이 아니라 개발 과정에 포함되는 품질 기준으로 다루려 했습니다. ## 병렬 실행과 10분 성능 가드레일 - 테스트 대기 시간을 줄이기 위해 여러 테스트를 동시에 실행하는 **병렬 실행(parallel runs)**을 핵심 전략으로 채택했습니다. - 기존 CI에서도 클라우드 러너를 이용한 병렬 테스트를 이미 활용하고 있었습니다. - 성능 테스트 역시 수십 개의 스트레스 시나리오를 동시에 실행해야 목표 시간을 달성할 수 있었습니다. - 성능 가드레일 검사는 개발 흐름을 방해하지 않도록 **10분 이내**에 완료되어야 한다는 기준을 세웠습니다. - 모든 풀 리퀘스트를 실제 하드웨어에서 테스트하려면 피크 시점에 동일한 성능의 테스트 러너 약 100대가 필요했습니다. - 따라서 새로운 체계는 단순히 테스트 수를 늘리는 것이 아니라, 하드웨어를 효율적으로 운영하고 결과를 빠르게 수집하는 구조여야 했습니다. ## 실용적인 결론 성능 테스트는 제품과 조직이 작을 때는 단일 장비와 소수의 대표 시나리오만으로도 충분할 수 있지만, 기능·코드·팀 규모가 커지면 자동화와 병렬화가 필수입니다. 특히 사용자 경험에 직접 영향을 주는 성능은 출시 후 모니터링하는 것보다 모든 코드 변경 단계에서 회귀를 차단하는 가드레일로 운영하는 편이 효과적입니다.

원문 읽기(새 탭에서 열림)
figma3분 읽기큐레이션 요약

역할이 규칙이 아닌 이유

제품 개발이 협업 중심으로 바뀌면서 엔지니어의 역할은 정해진 요구사항을 구현하는 데서 벗어나, 무엇을 만들지까지 함께 결정하는 방향으로 확장되고 있다. 하지만 협업과 피드백을 항상 늘리는 것이 정답은 아니며, 다양한 의견을 탐색하는 단계와 결정을 내리고 추진하는 단계 사이의 균형이 중요하다. Figma는 역할을 고정된 규칙으로 보지 않고, 초기부터 폭넓게 협업하되 마일스톤을 통해 적절한 시점에 수렴하는 방식을 택한다. ## 엔지니어 역할의 확장 - 웹 기술의 발전과 Google Docs 같은 협업 도구의 확산, 원격·하이브리드 근무의 증가로 제품은 점점 “멀티플레이어 기본값”이 되었다. - 제품 개발은 더 이상 디자인에서 시작해 엔지니어링으로 끝나는 선형적인 과정이 아니다. - 엔지니어는 단순히 구현 방법(how)을 결정하는 사람이 아니라, 고객 피드백과 제품·디자인 동료의 의견을 바탕으로 만들 대상(what)도 함께 정의한다. - 따라서 직무의 경계를 엄격한 규칙으로 보기보다, 필요한 순간 서로의 영역을 넘나드는 협업 방식이 요구된다. ## 협업과 독립성 사이의 균형 - 다른 사람의 지식을 활용하는 것과, 방향 없이 계속 논의만 반복하는 것은 다르다. - 모든 작업에서 항상 최대한 많은 사람의 피드백을 받으면 품질이 높아질 것 같지만, 오히려 결정이 늦어지고 프로젝트가 제자리걸음할 수 있다. - 독립적으로 집중해야 하는 시기와 적극적으로 다른 팀을 끌어들여야 하는 시기는 작업마다 다르다. - 적절한 균형은 조직 문화, 제품의 특성, 팀이 최적화하려는 목표에 따라 달라진다. ## 초기 아이디어를 공개하는 방식 - 새로운 업무를 시작할 때는 가능한 한 이른 시점부터 다양한 관점을 반영한다. - 엔지니어는 완성된 설계가 아니라 초기의 생각과 가설을 문서로 작성해야 한다. - 문서는 “완성 후 검토”를 위한 산출물이 아니라, 작업 중인 상태에서 피드백을 받기 위한 협업 도구다. - 대부분의 프로젝트에 초기 생각을 기록하는 문서가 존재하지만, 각 팀이 바쁜 상황에서도 빠르게 피드백을 주고받을 수 있는 구조가 필요하다. ## 엔지니어링 크리트: 승인보다 피드백 - Figma는 디자인·엔지니어링 조직 간 정기적인 엔지니어링 크리트(crit)를 운영한다. - 크리트의 목적은 다음과 같다. - 기술 설계를 초기에 공유한다. - 다른 팀으로부터 자주 피드백을 받는다. - 전문적인 기술 지원과 문제 제기를 얻는다. - 크리트는 승인 회의가 아니다. - 회의에서 최종 결정을 내리거나 작업을 확정하지 않는다. - 작업 중인 상태(WIP)를 전제로 문제를 지적한다. - 설계 자체가 충분히 발전해 별도의 승인을 필요로 하지 않도록 돕는다. - Figma는 FigJam을 사용해 실시간으로 참여하고 의견을 시각적으로 공유한다. ## 너무 많은 의견이 만드는 정체 - 다양한 의견은 유용하지만, 입력이 지나치게 많거나 서로 충돌하면 프로젝트가 방향을 잃을 수 있다. - 특히 가격 정책처럼 불확실성과 중요한 트레이드오프가 많은 문제에서는 아이디어가 계속 추가되면서 결정을 내리지 못할 위험이 크다. - 탐색적이고 생성적인 논의만 계속하면 프로젝트가 앞으로 나아가지 못한다. - 협업의 목표는 모든 의견을 반영하는 것이 아니라, 더 나은 결정을 내릴 수 있을 만큼 설계를 발전시키는 데 있다. ## 마일스톤을 통한 수렴 - Figma는 프로젝트를 여러 마일스톤으로 나누어 탐색과 실행의 시점을 구분한다. - 마일스톤을 명확히 정의하고 공유하면 다음과 같은 효과가 있다. - 이해관계자의 기대치를 관리할 수 있다. - 현재 단계에서 무엇을 결정해야 하는지 분명해진다. - 계속 확장하기보다 수렴해야 할 시점을 알 수 있다. - 프로젝트가 진전되려면 다양한 가능성을 열어두는 단계와, 하나의 방향을 선택해 추진하는 단계가 모두 필요하다. - 추진력(momentum)이 유지되면 목표에 가까워지고 있다는 감각을 얻지만, 추진력을 잃으면 프로젝트의 방향과 목적 자체를 다시 의심하게 된다. ## 실용적인 적용 - 초기 설계와 가설을 완성되기 전에 문서로 공유한다. - 피드백 회의는 승인 절차가 아니라 문제를 조기에 발견하는 자리로 운영한다. - 모든 의견을 반영하려 하지 말고, 마일스톤마다 탐색을 멈추고 결정을 내릴 시점을 명확히 한다. - 역할과 책임을 고정된 경계로 보지 않되, 최종적으로는 누가 어떤 결정을 내리고 실행할지 분명히 해야 한다.

원문 읽기(새 탭에서 열림)
figma원문

피그마가 게임 세계에서 (새 탭에서 열림)

피그마는 단순한 웹 애플리케이션을 넘어 고성능 게임 엔진과 유사한 기술적 아키텍처를 기반으로 구축된 창의적 협업 도구입니다. 이 글은 피그마가 실시간 멀티플레이어 시스템, 물리 기반 애니메이션, 그리고 C++와 WebAssembly, Rust와 같은 고성능 스택을 통해 어떻게 디지털 세계를 구축하는지 설명합니다. 결과적으로 피그마는 게임 개발의 복잡한 시스템 상호작용 원리를 차용하여 사용자들에게 몰입감 있고 매끄러운 디자인 경험을 제공하고 있습니다. ## 디지털 세계를 구축하는 엔진으로서의 피그마 * 피그마의 핵심은 웹 기반의 2D 그래픽 및 렌더링 시스템으로, 이는 마인크래프트와 같은 게임 엔진의 근간과 동일한 구조를 가집니다. * 사용자가 생성하는 모든 텍스트, 도형, 선을 브라우저에서 실시간으로 구현하며, 방대한 캔버스에서의 팬(pan)과 줌(zoom) 조작 시에도 정확한 위치에 객체를 렌더링합니다. * 실시간 동시 편집 기능을 게임의 개념에서 착안한 '멀티플레이어(multiplayer)' 엔진이라고 명명하여 협업의 핵심 시스템으로 발전시켰습니다. * 브라우저 및 모바일 앱의 메모리와 성능 제약을 극복하기 위해 일반적인 웹 스택 대신 C++로 캔버스를 구축한 후 WebAssembly로 컴파일하여 로딩 속도를 3배 개선했으며, 서버 측 성능 향상을 위해 Rust 언어를 도입했습니다. ## 시스템 기반의 창의적 협업과 상호작용 * 게임 스튜디오에서 엔지니어와 아티스트가 협업하듯, 피그마 엔지니어들은 시스템의 한계를 밀어붙이기 위해 디자이너, PM, 데이터 과학자들과 긴밀하게 소통합니다. * '젤다의 전설: 브레스 오브 더 와일드'의 불(fire) 시스템이 빛, 온기, 공격 수단 등 다양한 방식으로 상호작용하는 것처럼, 피그마의 오토세이브, 멀티플레이어, 렌더링 시스템도 서로 유기적으로 연결되어 작동합니다. * 단순한 도구 기능을 넘어 스프링 물리 법칙을 적용한 애니메이션 시스템, 커서 채팅, 하이파이브 기능 등을 통해 사용자가 도구 내에서 살아있는 피드백을 느낄 수 있도록 설계했습니다. * 베리언트(Variants) 기능과 플러그인/위젯 시스템을 통해 디자인 컴포넌트와 코드를 긴밀하게 연결하고, 사용자가 직접 생태계를 확장할 수 있는 개방형 플랫폼을 지향합니다. 웹 환경에서 복잡하고 성능 집약적인 도구를 개발해야 한다면, 전통적인 웹 프레임워크의 틀을 벗어나 게임 엔진의 설계 방식과 고성능 언어(WASM, Rust) 도입을 검토해야 합니다. 기술적 한계를 극복하는 열쇠는 도구를 하나의 살아있는 '시스템'들의 집합으로 바라보고, 각 요소 간의 상호작용이 사용자 경험에 미치는 영향을 정교하게 설계하는 데 있습니다.

figma4분 읽기큐레이션 요약

기능 비하인드:

Figma의 오토세이브는 단순히 파일 전체를 주기적으로 디스크에 저장하는 기능이 아니다. 브라우저 기반 실시간 협업 환경에서는 대용량 문서, 단일 스레드 실행, 동시 편집, 충돌 해결이 서로 얽히기 때문에 전체 파일 대신 오프라인 이후의 변경분(delta)을 저장하는 방식이 적합하다. Figma는 이 변경분을 IndexedDB에 보관했다가 문서를 다시 열 때 최신 문서에 적용하고 서버로 업로드한다. ## 오프라인 작업에서 발생하는 데이터 손실 - 온라인 상태에서는 Figma가 변경 사항을 서버로 즉시 전송한다. - 하지만 인터넷 연결이 끊기면 변경 사항을 서버와 동기화할 수 없다. - 기존에는 브라우저 탭이나 컴퓨터가 종료되면 오프라인 상태에서 작업한 내용이 사라질 위험이 있었다. - 확장된 오토세이브 시스템은 문서가 서버와 연결되지 않은 시점부터 변경 사항을 디스크에 저장한다. - 이후 문서를 새 탭에서 열면 저장된 변경 사항을 복원하고 서버에 업로드한다. ## 전체 파일 저장 방식의 한계 - 가장 단순한 방법은 메모리에 있는 scenegraph 전체를 직렬화해 백업 파일로 저장하는 것이다. - Figma 문서는 레이어 노드 트리인 **scenegraph**로 표현된다. - 큰 파일은 압축된 바이너리 기준 수십 MB, 메모리상에서는 수백 MB까지 커질 수 있다. - 전체 scenegraph를 직렬화하는 데 수 초가 걸릴 수 있으며, 저장할 때마다 사용자가 지연을 경험하게 된다. - 직렬화 시간을 10~20배 줄여 100ms 수준으로 만들어도 브라우저 환경에서는 충분하지 않다. - JavaScript와 WASM이 기본적으로 단일 스레드에서 실행되기 때문이다. - 사용자는 저장 시마다 약 100ms의 끊김을 느낄 수 있다. - 작업을 여러 프레임에 나누어 실행할 수 있지만, 직렬화 중 사용자가 문서를 수정하면 어떤 상태를 저장해야 하는지 문제가 발생한다. - 변경되지 않는 immutable scenegraph를 사용하면 해결할 수 있지만, 애플리케이션 전반의 대규모 구조 변경이 필요하고 메모리 사용량과 쓰기 성능 저하라는 비용도 따른다. ## 실시간 협업이 만드는 저장 문제 - Figma 파일은 클라우드에 저장되고 여러 사용자가 동시에 편집할 수 있다. - 오프라인 백업 파일 전체를 서버의 기존 파일로 덮어쓰면, 다른 사용자가 이후에 만든 최신 변경 사항을 잃을 수 있다. - 백업 파일을 별도 복사본으로 남기는 방법도 충분하지 않다. - 일부 파일은 디자인 시스템의 버튼이나 모달 같은 공유 컴포넌트의 원본이기 때문이다. - 복사본을 만드는 순간 공유 자산의 원본과 실제 작업 내용이 분리될 수 있다. - 따라서 오토세이브는 단순한 파일 복구가 아니라, 협업 시스템의 변경 병합 및 충돌 해결 방식과 함께 설계되어야 한다. ## 전체 문서가 아닌 변경분 저장 - Figma는 전체 파일 대신 사용자가 오프라인이 된 이후 발생한 변경 사항만 저장한다. - 이 변경분은 기존 멀티플레이어 편집 시스템에서 이미 서버 전송 및 확인을 위해 관리하던 데이터다. - 전형적인 흐름은 다음과 같다. - 사용자가 문서를 불러온다. - 서버 연결이 끊긴다. - 사용자의 변경 사항이 메모리의 pending changes buffer에 쌓인다. - 일정한 간격으로 버퍼의 내용이 디스크에 저장된다. - 문서나 브라우저 탭이 예기치 않게 종료된다. - 사용자가 문서를 다시 연다. - 저장된 변경분을 역직렬화한다. - 최신 문서 위에 변경분을 적용한다. - 복원된 변경 사항을 서버에 업로드한다. - 최신 문서에 변경분을 적용하므로, 오래된 전체 백업으로 최신 서버 상태를 덮어쓰는 문제를 피할 수 있다. ## IndexedDB를 활용한 브라우저 저장 - 브라우저에서 대용량 데이터를 저장하기 위해 IndexedDB를 사용한다. - IndexedDB는 다음 요구사항에 적합하다. - 많은 양의 데이터 저장 - 데이터를 작은 단위로 나누어 저장 - 인덱스를 통한 빠른 접근 - 트랜잭션을 통한 데이터 무결성 보장 - pending changes는 파일별, 노드 또는 레이어별 속성 변경 집합으로 저장된다. - 노드 단위의 세분화는 저장 공간과 불필요한 입출력 사이의 균형을 맞춘다. - 변경 사항을 지나치게 잘게 나누면 각 레코드의 관리 오버헤드가 커진다. - 반대로 모든 노드의 변경 사항을 하나의 객체에 넣으면 일부 변경만 발생해도 전체 변경 집합을 다시 기록해야 하므로 불필요한 I/O가 증가한다. ## 실용적인 결론 대용량 실시간 협업 애플리케이션의 오토세이브는 전체 상태를 반복 저장하기보다 변경 로그나 delta를 안정적으로 보관하는 방식이 효과적이다. 특히 저장 데이터의 단위, 트랜잭션 처리, 최신 서버 상태에 변경분을 재적용하는 복구 절차를 함께 설계해야 성능 저하와 협업 데이터 덮어쓰기를 모두 줄일 수 있다.

원문 읽기(새 탭에서 열림)
figma3분 읽기큐레이션 요약

60fps의 React: Figma

Figma는 캔버스에서 댓글 핀이 이동할 때 발생하던 불필요한 React 렌더링을 줄여 스크롤 성능을 약 3배 개선했다. 댓글 수와 무관하게 편집기를 60fps에 가깝게 동작시키는 것이 목표였으며, Chrome Performance 도구와 React Profiler로 병목이 JavaScript 실행과 컴포넌트 재렌더링에 있음을 확인했다. 핵심 해결책은 뷰포트 변화에 실제로 영향을 받는 댓글 컴포넌트만 업데이트하고, 댓글 위치 계산과 변환 처리를 최적화하는 것이었다. ## 60fps를 목표로 한 댓글 스크롤 - Figma의 댓글은 캔버스 위 특정 위치에 고정된 “댓글 핀”으로 표시된다. - 사용자가 캔버스를 이동하거나 확대·축소하면 댓글 핀도 뷰포트에 맞춰 계속 위치를 다시 계산해야 한다. - 15fps나 30fps보다 60fps가 훨씬 부드러운 사용자 경험을 제공하므로, 댓글과 스레드가 많아져도 일정한 성능을 유지하는 것이 목표였다. - 댓글 사용량이 증가하면서 대규모 팀과 파일에서 캔버스 반응성이 저하되기 시작했다. ## WebGL 캔버스와 React 댓글 UI의 구조 - Figma 편집기는 WebGL과 WebAssembly를 사용하는 “브라우저 안의 브라우저”에 가까운 구조다. - 일부 사용자 인터페이스는 TypeScript와 React로 구현되어 있지만, 일반적인 정적 React 화면과 달리 댓글은 캔버스의 이동과 확대·축소에 따라 동적으로 움직인다. - 편집기의 뷰포트 정보는 Redux에 저장된다. - 댓글 핀 컴포넌트는 Redux에서 뷰포트 정보를 가져와 캔버스 좌표를 화면에 표시할 위치로 변환한다. - 뷰포트가 변경될 때마다 React 컴포넌트 트리 일부가 업데이트되므로, 업데이트 범위가 성능에 직접적인 영향을 준다. ## 성능 분석에서 발견한 병목 - Chrome Performance 도구에서 대부분의 프레임 시간이 렌더링이나 페인팅이 아니라 JavaScript 실행에 사용되는 것으로 나타났다. - 댓글 30개인 화면에서 프레임당 약 68ms가 JavaScript에 소비되었고, 실제 화면은 약 19fps로 렌더링됐다. - React Profiler에서는 댓글 화면 자체의 렌더링에는 약 1.8ms만 사용되고 있었다. - 대신 뷰포트 변화와 직접 관련 없는 다음 컴포넌트들이 함께 재렌더링됐다. - 왼쪽 패널 - 툴바 - 속성 패널 - 기타 고정 위치 UI - 즉, 댓글 내용 렌더링보다 “변화가 없는 컴포넌트까지 다시 렌더링하는 것”이 더 큰 비효율이었다. ## 불필요한 재렌더링 줄이기 - 뷰포트 업데이트는 댓글 핀의 위치에는 필요하지만, 화면에 고정된 패널이나 툴바에는 필요하지 않다. - 따라서 뷰포트 상태를 사용하는 컴포넌트의 범위를 댓글 영역으로 제한해야 한다. - React 애플리케이션이 커질수록 상위 컴포넌트의 상태 변화가 하위 전체로 전파되면서 불필요한 렌더링이 발생하기 쉽다. - React Profiler로 실제로 다시 렌더링되는 컴포넌트를 확인하면, 직관만으로 찾기 어려운 병목을 구체적으로 식별할 수 있다. - 성능 개선은 댓글 컴포넌트 자체를 빠르게 만드는 것뿐 아니라, 댓글과 무관한 컴포넌트가 업데이트되지 않도록 컴포넌트 구조와 상태 구독 방식을 조정하는 데서 시작됐다. ## 댓글 핀 위치 변환 최적화 - 댓글 핀은 뷰포트가 바뀔 때마다 캔버스 좌표를 화면 좌표로 변환해야 한다. - 이 변환 과정이 매 업데이트마다 React 렌더링과 결합되면 JavaScript 실행 비용이 커질 수 있다. - Figma는 불필요한 컴포넌트 업데이트를 제거한 뒤 댓글 핀의 변환 처리도 최적화해, 캔버스 이동 중 위치 계산과 화면 반영 비용을 줄였다. - 결과적으로 댓글 스크롤 FPS가 기존보다 약 3배 향상됐다. ## 실용적인 결론 - React 성능 문제에서는 먼저 “컴포넌트 하나의 렌더링 속도”보다 “불필요하게 다시 렌더링되는 컴포넌트가 무엇인지”를 확인하는 것이 효과적이다. - Chrome Performance 도구로 프레임별 JavaScript 비용을 확인하고, React Profiler로 재렌더링 범위를 분석하는 조합이 유용하다. - 자주 변하는 상태는 실제로 그 상태가 필요한 컴포넌트 가까이에 두고, 고정 UI가 동적 상태 변화에 구독되지 않도록 설계하는 것이 좋다.

원문 읽기(새 탭에서 열림)
figma3분 읽기큐레이션 요약

플러그인 보안 업데이트

Figma는 플러그인 보안을 위해 사용하던 Realms shim에서 샌드박스 탈출 취약점이 발견되자 즉시 플러그인 게시와 업데이트를 중단하고 패치를 적용했다. 공개된 플러그인들을 감사한 결과 실제 악용 흔적은 발견되지 않았으며, 재발 방지를 위해 JavaScript 실행 환경을 Realms shim에서 WebAssembly로 컴파일한 QuickJS VM으로 교체했다. ## 플러그인 보안의 기본 원칙 Figma는 플러그인이 다음 작업을 수행할 수 있도록 설계했다. - 사용자가 명시적으로 실행한 경우에만 동작 - 플러그인 전용 대화상자 안에서 UI 표시 - 현재 열어 실행한 Figma 문서의 데이터 읽기 - 해당 문서의 데이터 수정 - 인터넷상의 서버와 통신 반대로 플러그인은 다음 작업을 할 수 없어야 한다. - 사용자 동의 없이 스스로 실행 - 파일을 소유한 프로젝트나 팀 정보 접근 - 실행 중이 아닐 때 데이터 접근 - 실행된 파일 이외의 다른 파일 데이터 접근 - 플러그인 UI 대화상자 외부의 Figma UI 변경 또한 Organization 요금제에서는 관리자가 허용된 플러그인 목록을 지정해 조직 내에서 신뢰할 수 없는 플러그인의 실행을 차단할 수 있다. ## Realms shim 취약점 발견 - Figma는 웹에서 서드파티 JavaScript를 안전하게 실행하기 위해 Realms shim을 사용했다. - 2019년 9월경 Realms shim에서 여러 독립적인 취약점이 발견됐다. - 취약점이 악용되면 샌드박스 내부 코드가 외부로 탈출해 Figma가 설정한 플러그인 보안 제한을 우회할 수 있었다. - 첫 번째 취약점은 공개 GitHub 이슈로 보고됐고, 나머지는 제한된 보안 권고를 통해 비공개로 공유됐다. - Figma는 공개 플러그인을 감사했지만 실제 플러그인이 취약점을 악용한 증거는 찾지 못했다. ## 취약점에 대한 대응 Figma는 위험 확산을 막기 위해 플러그인 배포 체계를 일시적으로 제한했다. - 신규 플러그인의 커뮤니티 허브 게시 중단 - 기존 플러그인의 업데이트 게시도 완전히 중단 - 공개 취약점에 대해서는 Realms shim 패치를 당일 적용 - 비공개 취약점은 관련 업체들과 공개 일정을 조율한 뒤 다음 주에 해결 - 모든 수정 사항이 배포된 후 취약점 세부 내용을 공개 - 게시된 플러그인 코드를 감사해 실제 공격 여부 확인 Figma의 수동 리뷰는 주로 사용자 경험과 품질을 검토하는 절차이며, 보안 경계를 사람이 직접 검증하는 방식은 아니다. 보안은 샌드박스가 강제하도록 설계했기 때문에, 리뷰만으로 악성 코드나 취약점을 완전히 차단할 수 있다고 보지 않았다. ## 실시간 업데이트가 만드는 위험 - Figma 플러그인 코드는 라이브 방식으로 배포된다. - 개발자가 기존 플러그인을 업데이트하면 변경 사항이 열려 있는 클라이언트에도 즉시 전파된다. - 따라서 취약점을 알고 있는 플러그인 개발자가 업데이트를 통해 악성 코드를 배포할 가능성을 차단하기 위해 기존 플러그인 업데이트까지 중단했다. - 이는 플러그인 생태계의 신속한 배포 기능이 보안 사고 시 위험 요소가 될 수 있음을 보여준다. ## QuickJS 기반 실행 환경으로 전환 Figma는 사고 대응 과정에서 Realms shim을 완전히 제거하고 QuickJS를 도입했다. - QuickJS는 C로 작성된 JavaScript 가상 머신이다. - Figma는 이를 WebAssembly로 크로스 컴파일해 플러그인 실행에 사용했다. - Realms shim의 객체 경계 혼동에서 비롯된 이번 취약점 유형은 새 구현에서는 발생하지 않는다. - 기존 구조가 교체 가능한 아키텍처로 설계되어 있었기 때문에 백업 계획이었던 QuickJS로 빠르게 전환할 수 있었다. ## 실용적인 결론 서드파티 코드를 실행할 때는 사람의 코드 리뷰보다 강제 가능한 샌드박스 경계가 핵심이다. 또한 배포 중단, 신속한 패치, 비공개 정보 공개 일정 조율, 실행 환경 교체 계획을 사전에 마련해 두면 취약점 발견 시 피해 확산을 효과적으로 줄일 수 있다.

원문 읽기(새 탭에서 열림)
figma5분 읽기큐레이션 요약

웹에서 플러그인 시스템

Figma는 서드파티 플러그인을 브라우저 기반 디자인 편집기 안에서 실행하면서도 보안·안정성·성능을 모두 확보해야 했다. 단순히 `eval(PLUGIN_CODE)`를 사용하는 것은 위험하고, 기존 플러그인처럼 플랫폼 성능을 저하시키거나 업데이트 때마다 깨지는 문제도 피해야 했다. 여러 접근을 검토한 결과, 당시에는 JavaScript `Realm` 기반 샌드박스를 선택했지만, 이후 보안 취약점 공개를 계기로 C로 작성된 JavaScript VM을 WebAssembly로 컴파일하는 방식으로 변경했다. ## 플러그인 시스템이 해결해야 할 제약 - 플러그인은 접근성 검사, 번역, 색상 도구, 이미지 가져오기 등 사용자가 작성한 임의의 코드를 실행한다. - 플러그인이 Figma 편집기의 내부 데이터와 기능을 사용해야 하므로, 단순한 외부 웹페이지처럼 완전히 격리할 수는 없다. - 동시에 플러그인이 다음 영역에 영향을 주면 안 된다. - Figma 문서나 다른 사용자의 데이터에 대한 무단 접근 - 편집기 UI와 실행 환경의 안정성 - CPU·메모리 등 시스템 자원의 과도한 사용 - Figma 업데이트에 따른 플러그인 호환성 저하 - Figma는 WebGL, WebAssembly, TypeScript, React, 실시간 협업 기능을 함께 사용하는 구조라 일반적인 웹 애플리케이션보다 실행 환경이 복잡했다. ## 시도 1: `<iframe>` 샌드박스 - 가장 표준적이고 검증된 웹 보안 기능인 `<iframe>`을 플러그인 실행 환경으로 검토했다. - iframe은 별도의 문서와 JavaScript 실행 컨텍스트를 제공해 플러그인 코드가 Figma의 전역 객체나 DOM에 직접 접근하지 못하게 할 수 있다. - `sandbox` 속성과 출처(origin) 분리를 사용하면 플러그인과 호스트 애플리케이션 사이의 경계를 강화할 수 있다. - 플러그인과 Figma 사이의 통신은 `postMessage` 같은 명시적인 메시지 전달 방식으로 제한할 수 있다. - 그러나 iframe 방식에는 중요한 한계가 있었다. - Figma 내부 데이터 구조에 대한 빠르고 자연스러운 접근이 어렵다. - 플러그인 API 호출을 위해 많은 객체와 요청을 직렬화·전달해야 한다. - 별도 브라우저 컨텍스트를 만들기 때문에 성능과 메모리 비용이 발생한다. - iframe 자체가 안전하더라도 플러그인이 CPU나 메모리를 과도하게 사용해 편집기를 느리게 만들 가능성은 남는다. - 따라서 일반적인 웹 위젯에는 적합하지만, Figma처럼 고성능 편집기와 긴밀하게 상호작용해야 하는 플러그인 환경에는 충분하지 않았다. ## 시도 2: JavaScript 인터프리터를 WebAssembly로 컴파일 - 두 번째 접근은 플러그인 코드를 브라우저의 JavaScript 엔진에서 직접 실행하지 않고, 별도의 JavaScript 인터프리터 안에서 실행하는 방식이었다. - 인터프리터를 WebAssembly로 컴파일하면 플러그인 코드는 Figma의 실제 JavaScript 환경과 분리된 가상 실행 환경에서 동작한다. - 이 방식의 장점은 다음과 같다. - 플러그인이 브라우저의 전역 객체, DOM, Figma 내부 구현에 직접 접근할 수 없다. - 노출할 API를 명시적으로 선택할 수 있다. - 실행 환경을 통제하고 향후 브라우저 변경의 영향을 줄일 수 있다. - 반면 별도의 JavaScript 인터프리터를 실행해야 하므로 일반 JavaScript보다 느릴 수 있다. - 표준 JavaScript 기능과 내장 객체를 정확하게 구현해야 하며, 언어 호환성 문제도 발생한다. - 인터프리터 자체의 구현 오류나 보안 취약점이 샌드박스를 무너뜨릴 가능성도 고려해야 했다. - 당시에는 성능과 구현 복잡성이 주요 장애물이었다. ## 시도 3: JavaScript Realm - 세 번째 접근은 별도의 전역 환경과 객체 영역을 만드는 `Realm` 개념이었다. - Realm은 플러그인이 Figma의 전역 객체와 분리된 JavaScript 환경에서 실행되도록 하면서도, 필요한 API만 선택적으로 제공할 수 있게 한다. - iframe보다 가볍고, 별도 JavaScript 인터프리터를 내장하는 방식보다 브라우저의 기본 실행 성능을 더 많이 활용할 수 있다. - Figma는 다음과 같은 형태의 경계를 구성할 수 있었다. - 플러그인에 필요한 API만 노출 - 호스트 객체와 플러그인 객체 사이의 직접 참조 제한 - 허용된 요청만 Figma 내부 기능으로 전달 - 플러그인 전역 환경과 Figma 전역 환경의 분리 - 이 접근은 보안, 성능, API 사용성 사이의 균형이 가장 좋다고 판단되어 원래 구현에 채택됐다. - 다만 Realm은 당시 표준 기능으로 완전히 지원된 것이 아니라 shim에 의존해야 했다. - JavaScript 객체 모델과 프로토타입 체인을 완벽하게 격리하는 것은 매우 어려워, shim의 작은 결함도 보안 취약점으로 이어질 수 있었다. ## 운영 과정에서 드러난 보안 문제와 변경 - 글 게시 후 Realm shim에서 보안 취약점이 비공개로 제보됐다. - 취약점은 공개되기 전에 shim 팀에 의해 수정됐고, Figma는 실제 악용 증거를 발견하지 못했다고 밝혔다. - 그러나 샌드박스의 핵심이 외부 라이브러리의 복잡한 JavaScript 격리에 의존한다는 점은 중요한 위험 요소였다. - Figma는 이후 C로 작성된 JavaScript VM을 WebAssembly로 컴파일하는 대안으로 구현을 변경했다. - 이 방식은 브라우저의 JavaScript 객체와 실행 컨텍스트를 더 강하게 분리해, Realm shim에 의존하는 공격 표면을 줄이는 방향이다. ## 설계에서 얻은 교훈 - 서드파티 코드를 안전하게 실행하는 문제는 단순히 `eval`을 다른 API로 바꾸는 문제가 아니다. - 격리 수준, API 호출 비용, 실행 성능, 자원 제한, 유지보수성을 함께 평가해야 한다. - “브라우저 기능을 사용하므로 자동으로 안전하다”거나 “샌드박스이므로 모든 문제가 해결된다”고 볼 수 없다. - 특히 샌드박스 구현 자체가 복잡한 경우, 해당 구현의 취약점과 업데이트 정책까지 시스템의 보안 경계로 봐야 한다. - 가장 현실적인 설계는 플러그인에 필요한 최소 API만 노출하고, 실행 환경과 호스트 애플리케이션 사이의 통신을 명확한 경계로 제한하는 것이다. 플러그인 시스템을 설계할 때는 iframe, 별도 인터프리터, Realm 같은 선택지를 보안·성능·호환성 관점에서 비교해야 한다. 또한 외부 샌드박스 라이브러리에 의존한다면 정기적인 보안 검토와 교체 가능한 구조를 마련하고, 높은 보안 수준이 필요할 경우 WebAssembly 기반 독립 VM처럼 더 강한 실행 격리를 고려하는 것이 바람직하다.

원문 읽기(새 탭에서 열림)
figma3분 읽기큐레이션 요약

파일 로딩, 드래

Figma는 디자인 도구가 사용자의 사고와 손동작을 즉시 따라가려면 성능이 핵심이라고 강조한다. 문서 렌더러 재구성, WebAssembly 최적화 등을 통해 대용량 파일 로딩과 줌·드래그 성능을 최대 3배 개선했으며, 실제 사용자 문서와 프레임 시간 지표를 기반으로 효과를 측정했다. 특히 단순한 평균 속도뿐 아니라 화면 피드백의 끊김까지 함께 줄이는 것을 목표로 삼았다. ## 성능이 창작 경험을 좌우하는 이유 - 디자인 도구의 지연은 사용자의 사고와 화면 조작 사이에 간극을 만든다. - Figma는 제품이 사용자의 창의적 사고와 신체 움직임을 자연스럽게 확장해야 한다고 본다. - 따라서 기능을 추가하는 것뿐 아니라, 실제 사용 문서에서 성능 병목을 지속적으로 찾아 개선하는 과정이 필요하다. - 이번 작업에서는 문서 렌더러 구조를 재설계하고 WebAssembly 관련 문제를 해결해 큰 폭의 성능 향상을 얻었다. ## WebAssembly와 렌더러 최적화로 파일 로딩 개선 - 대규모 조직에서는 여러 겹으로 중첩된 컴포넌트와 다양한 상태를 하나의 복잡한 문서에 담는다. - Microsoft Fluent Design 팀은 Windows 기본 컨트롤의 모든 상태와 조합을 하나의 Figma 파일에 구성해 로딩 성능의 한계를 시험했다. - 실제 대형 문서의 로딩 시간이 **29초에서 8초 이하**로 감소했다. - WebAssembly 최적화와 여러 렌더링 관련 변경 사항이 약 두 달 동안 적용됐다. - WebAssembly는 macOS·Windows의 데스크톱 앱과 Chrome, Firefox, Safari 등 주요 환경에 활성화됐다. - 결과적으로 복잡한 파일의 로딩뿐 아니라 전반적인 문서 처리 성능도 향상됐다. ## 줌과 드래그의 연속적인 상호작용 - 파일을 연 뒤에도 줌인·줌아웃과 레이어 드래그처럼 반복적으로 수행하는 작업의 반응성이 중요하다. - Figma는 화면이 순간적으로 멈추는 ‘hitch’를 줄이는 데 집중했다. - 줌 성능과 드래그 성능이 모두 최대 **3배** 개선됐다. - 이미지가 많이 포함된 복잡한 파일을 사용하는 N3TWORK는 새 렌더러 적용 후 컴포넌트 게시와 파일 로딩 속도의 변화를 즉시 체감했다. - 이 사례는 일반적인 벤치마크뿐 아니라 특정 사용자의 실제 문서와 작업 방식에 맞춘 최적화도 큰 효과를 낼 수 있음을 보여준다. ## 평균 프레임 시간과 최대 프레임 시간을 함께 측정하는 이유 - 연속적인 조작에서는 작업이 끝나는 총 시간보다 조작 중 화면이 얼마나 부드럽게 갱신되는지가 중요하다. - 같은 500ms가 걸리는 작업이라도 매 순간 화면을 보여주는 경우와 끝날 때까지 아무 피드백이 없는 경우의 체감 품질은 크게 다르다. - Figma는 직접 조작감을 중시하기 때문에 다음 두 지표를 함께 추적한다. - **평균 프레임 시간**: 전반적인 화면 갱신 속도와 부드러움을 나타낸다. - **최대 프레임 시간**: 특정 순간에 발생하는 긴 지연과 끊김을 나타낸다. - 최대 프레임 시간이 커지면 드래그 중 갑자기 멈추는 듯한 ‘걸림’이 발생한다. - 평균 프레임 시간이 커지면 화면 전체가 낮은 프레임 레이트로 동작해 지속적으로 버벅거린다. - 예를 들어 평균 프레임 레이트가 같아도 일부 프레임만 200ms 또는 367ms 지연되면 움직임이 불규칙하게 끊긴다. - 반대로 최대 지연 시간이 같더라도 긴 프레임 지연이 자주 발생하면 전체 화면이 더 심하게 끊겨 보인다. - 이상적으로는 항상 60fps 이상을 유지해야 하지만, 현실적인 개선 과정에서는 두 지표를 시간에 따라 추적하는 것이 유용하다. ## 실제 사용자 문서를 기준으로 한 성능 개선 - Figma는 인위적인 테스트 파일만이 아니라 사용자가 공유한 실제 문서를 분석해 최적화 대상을 찾았다. - 문서의 컴포넌트 중첩 수준, 비트맵 이미지 수, 디자인 시스템의 복잡도 등 현실적인 조건을 반영했다. - 사용자와 직접 문제를 진단하고 새 렌더러를 적용해 즉각적인 성능 변화를 확인하는 방식도 활용했다. - 이는 성능 최적화가 단일 벤치마크 수치를 높이는 작업이 아니라, 실제 작업 흐름에서 지연과 불연속적인 피드백을 제거하는 과정임을 보여준다. 복잡한 디자인 문서를 주로 다룬다면 파일 크기 자체보다 컴포넌트 중첩, 이미지 밀도, 렌더링 구조가 성능에 미치는 영향을 살펴보는 것이 중요하다. 성능을 평가할 때도 평균 속도만 보지 말고, 순간적인 프레임 지연과 사용자 조작 중 발생하는 끊김을 함께 측정해야 한다.

원문 읽기(새 탭에서 열림)