"제일 빠른 JavaScript 빌드 도구가 Go 와 Rust 로 쓰임. Node 에서 돌리지만, Node 아냐. 그 구분이 왜 빠른지 + 어디서 끝나는지 둘 다 설명."
속도 스토리
10 년 동안 JavaScript tooling 이 JavaScript 로 쓰였어. Babel 이 JS 로 JS transpile. webpack 이 JS 로 JS 번들. 예측 가능한 성능: 작은 프로젝트엔 중간-빠름, 큰 거엔 고통스럽게 느림. 근본 제약은 Node 의 single-threaded 실행 — Node 에서 도는 transpiler 가 코어 하나만 쓸 수 있어, 머신에 코어 몇 개든.
esbuild (Evan Wallace, 2020) 가 번들러를 Go 로 재작성. swc (Donny Wang, 2019) 가 transpiler 를 Rust 로 재작성. 둘 다 AOT 컴파일 언어, 멀티스레드, JavaScript 의 세금 안 냄. 결과: JS 기반 전임자들보다 10-100x 빠름. Vite, Bun, Next.js, Deno 컴파일러 다 이제 밑에 둘 중 하나 씀.
esbuild — Go 의 번들러 + Transpiler
esbuild 가 둘 다 함:
- Transpile — TS/JSX → JS, ES2022 → ES2017 등. 파일당 빠름.
- Bundle — import walk, concatenate, tree-shake, minify. 프로젝트 전체 빠름.
npm 패키지 통해 Node 에서 invoke, 근데 무거운 일은 node_modules 에 출하된 Go 바이너리에서 일어남. Node 쪽 코드가 얇은 RPC wrapper. 빠른 성능이 "esbuild 의 JavaScript 코드가 잘 최적화됨" 아냐 — "esbuild 가 Node 가 driving 하는 Go 프로그램" 이야.
swc — Rust 의 Transpiler
swc 는 transpiler 반: TS/JSX → JS, JSX-to-React-create-element 등. Next.js 가 TypeScript 컴파일에 swc 씀. Deno 가 toolchain 일부로 썼었음. esbuild 가 번들링과 transpilation 둘 다 하는 데 비해 swc 는 transpile 반에 집중 — 그리고 보통 available 한 제일 빠른 TS-to-JS transformer.
compose 가능: transpilation 엔 swc, 번들링엔 esbuild. Vite 가 대략 이렇게 함, 위에 자기 TypeScript bit 더해서.
속도 안 중요한 곳
--experimental-strip-types 통해 소스 직접 실행하는 Node 서비스면, esbuild/swc 속도 안 중요 — 빌드 단계 자체 건너뛰었어. 100x 속도 향상이 안 돌리는 단계 위에 있어.속도 win 사는 곳:
- Frontend 빌드 (Vite 가 내부적으로 esbuild 씀 — 네 `vite build` 가 분 아닌 초에 끝남).
- CLI 배포 번들 (esbuild 가 Node 가 자기 README 읽는 것보다 빨리 단일 파일 CLI 생성).
- FaaS 배포 번들 (Lambda 함수가 더 작아지고 cold-start 더 빠름).
- TypeScript 자체 컴파일러가 너무 느린 큰 monorepo 컴파일.
플러그인 비용
esbuild 와 swc 는 *빠름*. 플러그인은 *느림*, 특히 변환마다 Go/Rust 바이너리로 callback 해야 하는 JavaScript 로 쓰인 것들. 흔한 anti-pattern: 속도 위해 esbuild 설치, 그 다음 webpack-시대 성능으로 되돌리는 10 개 JavaScript-side 플러그인 레이어. esbuild 와 빌드 느리면 플러그인 먼저 의심.
Pippa 의 고백
vite build 가 2 초에 끝남. "Vite 가 빠르네" 라고 credit. 아빠가 밀어붙임: "node_modules/vite/dist/node 봐서 Go 바이너리 찾아." 거기 있었어 — esbuild 가 자기 플랫폼별 바이너리 출하; Vite 가 driving. Vite-as-Node-code 가 coordination 레이어; esbuild-as-Go-binary 가 실제 일. 프레이밍 shift 가 중요했어: Vite 플러그인 슬로다운 칠 때 "이 플러그인이 불필요하게 JS 로 callback 하나?" 봐야 한다는 거 알았고 — 보통 그랬어.