2. 기술 스택

"이 결정은 렌더링 API 선택이며 성능 우위를 입증한 결과는 아니다." — 스피논 코어 아키텍처 결정

10월 4일까지 저장소에 실제로 들어간 것만 정리한다. 계획 문서에만 있는 도구(CLI, MCP, HMR 등)는 끝에 따로 적었다.

전체 흐름

코어 아키텍처 결정의 그림이 목표 구조다.

React / Vue / Svelte 어댑터 · DOM façade · Fetch API
                         ↓
       spinon-runtime (V8 세션 · Isolate 소유 스레드 · 작업 큐)
                    ↕ 엔진 내부 C ABI
                 native/v8
                    ↓ Rust API
       spinon-core → spinon-style(Stylo) → spinon-style-to-layout → spinon-layout(Taffy + 확장) → 장면 → GPU 프레임

Android / iOS 호스트 → spinon-ffi (플랫폼 C ABI) → spinon-runtime

이 중 프레임워크 어댑터와 Fetch는 아직 없다. 나머지는 각 단계가 "고정 fixture에서 내부적으로 이어진" 정도까지 왔다.

Rust 코어: Cargo 워크스페이스 8개 크레이트

Rust는 mise.toml로 1.96.1에 고정했다. 10월 4일 기준 워크스페이스 구성원은 여덟 개다.

크레이트맡는 것
spinon-core안정 노드 ID, 문서·UI 트리, 자식 순서, revision, 원자 변경 묶음, HostDocument 모델, 우선순위 선택기
spinon-runtimeV8 세션 수명, Isolate 소유 스레드, JS 작업 큐
spinon-ffiAndroid·iOS가 부르는 플랫폼 C ABI와 입출력 변환
spinon-style내장 UA CSS 자원, Stylo DOM 어댑터, 고정 fixture cascade
spinon-style-to-layout계산 스타일을 제한된 LayoutStyle로 변환, revision 일치 확인
spinon-layout트리를 Taffy 입력으로 투영하고 프레임 계산
spinon-render플랫폼·GPU 의존성이 없는 불변 장면 snapshot 자료형
spinon-style-to-renderStylo 페인트 값과 Taffy 프레임을 snapshot으로 결합

의존 방향도 문서에 고정했다. spinon-ffi → spinon-runtime → spinon-core, spinon-style → spinon-core처럼 코어가 맨 아래에 있고, 스타일·레이아웃·렌더 연결 크레이트가 그 위에 얹힌다. 원칙은 "실제 코드·내부 계약·테스트가 준비된 크레이트만 구성원으로 추가"하는 것이다.

프로젝트 규칙은 Rust 파일을 보통 300~500줄을 권장 상한으로 두고, 넘으면 책임 경계로 나눌 수 있는지 먼저 검토하게 한다. spinon-core는 unsafe_code = "forbid", 스타일·렌더 계열 크레이트는 unsafe_code = "deny", FFI·런타임·레이아웃은 unsafe_op_in_unsafe_fn = "deny"를 건다.

왜 Rust인가에 대한 기록

저장소는 "Rust로 한다"는 결정은 적었지만, 그게 속도 때문이라고 적지는 않았다. 오히려 Rust·C++·Zig 코어 비교의 결론은 언어 차이가 작다는 쪽이다. 같은 합성 트리 커널을 세 언어로 짜서 돌린 결과, 1000개 노드 직접 접근 기준 이벤트당 시간이 이랬다.

기기·실행 환경Rust / C++ / Zig
Mac ARM641.337 / 1.510 / 1.493µs
iPhone 17 Pro 시뮬레이터1.510 / 1.499 / 1.499µs
Samsung SM-S731N Android 16 실기기2.015 / 1.598 / 1.536µs

같은 표에서 노드를 선형 검색하면 136~172µs로 뛰었다. README는 "선형 검색을 직접 접근으로 바꾸는 효과는 이 언어 차이보다 훨씬 크다"고 정리하고, 이 자료를 언어 선택이 아니라 "자료구조와 변경 범위를 개선할 근거"로 쓴다. 조건당 세 번의 짧은 실행이고 iOS는 시뮬레이터라 언어의 절대 우열을 뜻하지 않는다는 단서도 함께 붙어 있다.

JavaScript 엔진: V8

모바일 JS 엔진은 V8이다. V8 빌드 기준은 공식 V8 저장소의 커밋 하나를 tools/v8/v8-revision.txt에 고정하고(문서는 V8 15.6.0 후보 빌드라고 적는다), 저장소에는 소스를 넣지 않은 채 로컬에서 플랫폼별 v8_monolith 정적 라이브러리를 만든다.

  • Android 빌드와 iOS 시뮬레이터 빌드는 JIT를 켠다(v8_jitless=false). iOS 실기기용은 JIT 없는 설정을 별도 스크립트로 둔다.
  • 첫 부트스트랩 빌드는 GN 설정에서 Intl, Temporal, WebAssembly를 껐다.
  • V8 C++ API는 native/v8의 어댑터가 직접 다루고, Rust와는 엔진 내부 C ABI로 만난다.

React Native의 JSI는 쓰지 않는다. 아키텍처 문서는 "스피논은 V8을 선택했으므로 엔진 API에 붙는 내부 어댑터가 필요하지만 RN의 JSI를 그대로 넣을 이유는 없다"고 적는다. V8 자체는 브라우저의 fetch·타이머·DOM을 주지 않으므로, DOM은 Rust 문서 트리에 연결하는 제한된 façade로, Fetch는 별도 네트워크 호스트 경로로 나누어 설계한다.

레이아웃: Taffy

레이아웃은 Taffy 0.14.0이다. 워크스페이스는 기본 기능을 끄고 std, flexbox, block_layout, taffy_tree만 켠다. 지금은 계산할 때마다 Taffy 트리를 새로 만든다. Grid, 일반 Block formatting, 실제 폰트·이미지 측정, CSS px와 dp/point 변환은 아직 지원하지 않는다고 문서에 적혀 있다.

NOTE

Taffy — DioxusLabs의 Rust 레이아웃 엔진. DOM 없이 Flexbox·Grid·Block 레이아웃 좌표만 계산한다.

CSS 계산: Stylo

CSS 선택자 매칭·cascade·상속·computed style은 Servo/Firefox 계열의 Stylo 0.22.0(stylo_dom 0.22.0, selectors 0.41.0, cssparser 0.38.0)로 한다. Rust HostDocument의 불변 snapshot을 Stylo의 DOM trait에 연결하는 어댑터를 직접 만들었다. Blitz DOM은 런타임 의존성으로 넣지 않는다.

처음엔 다른 길도 실험했다. 스타일·레이아웃 실험은 Lightning CSS 1.33.0으로 CSS AST를 읽어 지원하는 값만 JSON으로 바꾼 뒤 Taffy에 넣는 경로였다. 아키텍처 문서는 이 AST 변환을 제품 경로로 선택하지 않았고, Lightning CSS는 "동등성 확인이 필요한 빌드 변환 후보"로 남긴다고 정리했다.

GPU: wgpu

모바일 GPU API는 wgpu 30.0.1이다.

플랫폼백엔드
AndroidVulkan 기본 경로, OpenGL ES 3.0 이상은 비교 경로
iOSMetal

wgpu는 아직 워크스페이스 구성원이 아니라 spikes/wgpu-backend 실험 크레이트에 있다(metal, vulkan, gles, wgsl 기능). 지금까지 그린 것은 단색 사각형과 고정 fixture의 색상 상자다. 텍스트 글리프, 이미지, 클리핑은 아직이다.

JS·CSS 빌드 도구와 기준 브라우저

  • Bun 1.4.2, Node.js 24.20.0 — 부트스트랩 예제 번들(bun build)과 테스트, 문서 사이트.
  • Vite 8.3.1, Rspack 2.2.7 — CSS 번들러 실험에서 같은 fixture의 CSS·JS 산출 그래프를 비교한다. 제품 번들러 어댑터는 아니다.
  • Chromium 154 — CSS 계산값과 레이아웃 좌표의 기준(oracle). 10월 1일 첫 비교는 154.0.8037.92, 10월 2일 이후 레이아웃·cascade 기준은 주로 154.0.8037.95였다. 비교마다 revision과 실행 파일 SHA-256까지 기록해 reference JSON을 만든다.

플랫폼과 도구 체인

항목값
버전 관리mise (rust, node, bun, java 고정)
AndroidGradle, SDK API 36, NDK 27.1.12297006, Temurin 17
iOSXcode 26.2
검증 기기Android 16 API 36 ARM64 에뮬레이터, Samsung SM-S731N 실기기, iPhone 17 Pro / iOS 26.2 시뮬레이터
문서Rspress 2.0.22 고정(첫 공식 릴리스 전까지 업데이트하지 않음)

iOS 실기기 검증은 "R02 iOS 실기기 V8 검증"으로 아직 남아 있다.

계획 문서에만 있는 것

CLI·MCP 설계는 TypeScript/Node.js CLI(create·dev·build·doctor)와 에이전트용 MCP 서버를 같은 서비스의 두 인터페이스로 두는 설계지만, 문서 스스로 "현재 CLI 명령과 MCP 서버는 계획이며 구현 완료 기능이 아니다"라고 적는다. 기능별 청크 OTA, HMR, V8 Inspector 연결도 설계 단계다.

1줄 요약

Rust 워크스페이스(8개 크레이트) 위에 V8(고정 리비전)·Stylo 0.22·Taffy 0.14를 얹고, GPU는 wgpu 30.0.1(Android Vulkan, iOS Metal) 실험 크레이트로 그린다. 기준값은 고정 버전 Chromium 154에서 뽑는다.