zerovoids.dev
zerovoids.dev

Ø NEWS

컴파일러 · 오픈소스

Vercel Labs, TypeScript를 V8 없이 네이티브 실행 파일로 컴파일하는 scriptc 공개

Vercel Labs가 TypeScript를 Node나 V8 없이 네이티브 실행 파일로 찍어내는 실험적 컴파일러 scriptc를 내놨다. 코드는 대부분 정적으로 컴파일하고, 타입이 흐려지는 부분만 내장된 620KB짜리 quickjs-ng 엔진에 떠넘긴다.

키워드typescriptcompilerjavascriptnodeperformance

같은 TypeScript CLI를 켜는 데 Node 24.18.0은 61.78ms, Bun 1.3.12는 21.29ms가 걸린다. Vercel Labs가 내놓은 실험적 컴파일러 scriptc로 네이티브 실행 파일을 만들면 1.78ms다. 이 바이너리 안에는 Node도 V8도, 자바스크립트 엔진 자체도 없다.

scriptc는 Apache 2.0 라이선스로 공개된 오픈소스 프로젝트다. 파싱과 타입 체크는 실제 TypeScript 컴파일러가 그대로 맡고, 그 결과를 타입이 붙은 중간 표현으로 낮춘 뒤 C, LLVM IR, 어셈블리, 오브젝트 파일, 네이티브 실행 파일, 또는 WASI Preview 1을 통한 WebAssembly로 찍어낸다.

세 갈래로 갈리는 코드

컴파일러는 모든 구문을 세 갈래 중 하나로 떨어뜨린다. 기본은 정적 컴파일이다. --dynamic 플래그를 주면 npm 패키지나 타입이 흐려지는 코드를 내장된 620KB짜리 quickjs-ng 엔진에서 동적으로 돌린다. 그것도 안 되면 컴파일 시점에 SC 코드와 고쳐 쓸 방법을 알려주며 그냥 거부한다.

quickjs-ng로 넘어가는 순간, 정적 컴파일이 주는 이득은 빠르게 줄어든다.

Hono를 올리는 데는 --dynamic이 필요했고, 그 결과 서버 코드의 62%가 QuickJS 안으로 들어갔다. 처리량은 Bun의 초당 7만 500건에서 초당 1만 8400건으로 떨어졌다. 해커뉴스에는 바이트 배열 벤치마크에서 scriptc가 Node 24보다 7.5배 느렸다는 사용자 보고도 올라왔다 — 클로드로 scriptc 전용 최적화까지 시도한 뒤였다. 대신 실행 파일은 12배 빨리 뜨고(1.5ms 대 18.6ms), 메모리는 72배 덜 쓰고(2.5MiB 대 181MiB), 런타임 의존성 없는 370KB 단일 파일로 나왔다.

왜 이렇게 갈리나

Filip Pizlo는 모든 수를 실수(float)로 다루고 정수 추론을 뒤로 미루는 방식이 '빠른 JS의 절반을 건너뛴다'고 지적했다. quickjs-ng에 의존하는 구조도 성능을 노리는 프로젝트치고는 궁합이 나쁘다고 봤다 — any 타입은 실제 코드에 흔해서, 결국 QuickJS라는 섬으로 계속 떨어지게 된다는 뜻이다. 다른 사용자는 로컬에 있는 프로젝트마다 커버리지를 돌려봤는데 전부 수백 개의 에러가 나서 실질적으로 못 쓰겠다고 썼다. 처음부터 서드파티 라이브러리 없이 새로 짠 코드만 컴파일된다면, Rust·Go·Zig·Swift처럼 원래 컴파일 언어를 쓰지 않을 이유가 없다는 반론이다.

반복해서 나오는 걱정은 지속 가능성이다. 댓글들은 Vercel의 또 다른 프로젝트 zerolang이 공개 몇 주 만에 커밋이 끊긴 사례를 든다. Remo Jansen은 TypeScript 6 컴파일러 자체를 scriptc로 컴파일해보려다 내부 컴파일러 오류로 실패했다. 그래도 콜드 스타트는 3.6ms로 Node의 48.9ms보다 빨랐고, 반대로 연산이 몰리는 구간에서는 2.33초로 훨씬 느렸다.

Simon Willison은 최근 한 주 동안 코딩 에이전트들이 91만 8000줄을 쳐냈다는 점을 짚으면서, C나 Rust를 직접 쓰지 않고도 작고 빠른 바이너리를 만들 수 있게 된 건 '가치 있는 능력'이라고 썼다.