TRENDSHIFT · 2026.06.15

swc 딥다이브
— Rust로 다시 쓴 "초고속 웹 컴파일러" 플랫폼의 설계

swc(Speedy Web Compiler)는 "JavaScript / TypeScript를 파싱·변환·압축·번들링"하는 일을 전부 Rust로 다시 짠 컴파일러 플랫폼이다. Babel·Terser 같은 JS 기반 도구가 하던 일을 네이티브 속도로 처리해, Next.js·Deno·Parcel·Vite 생태계의 빌드 속도를 끌어올렸다. 단일 도구가 아니라 120여 개의 Rust crate로 쪼개진 거대한 Cargo 워크스페이스이며, 그 위에 napi-rs(Node)·wasm 바인딩을 얹어 JS에서도 그대로 쓸 수 있게 한 "라이브러리이자 도구"다. (저장소: swc-project/swc · Rust · Apache-2.0 · GitHub 스타 32k+ · @swc/core 월 수천만 다운로드)

이 문서는 "대규모 컴파일러를 Rust 멀티-crate로 어떻게 설계하는가"에 초점을 둔 학습 해설이다. 파서 → AST → visitor/fold 변환 → 코드젠으로 이어지는 컴파일러 파이프라인과, 그것을 Node/Wasm으로 노출하는 바인딩 구조를 해부한다.
목차
  1. 프로젝트 한줄 요약
  2. 왜 주목받는가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 분석
  5. 디렉토리 구조 해부
  6. 학습 포인트 (기술별)
  7. 시스템 / 환경 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한줄 요약

이 레포가 무엇을 하는 물건인가.

핵심 메시지

"브라우저가 못 읽는 최신 JS/TS 코드를,
읽을 수 있는 옛 코드로 — Babel이 하던 일을 Rust 속도로."

당신이 const x = a ?? b 같은 최신 문법이나 TypeScript 타입을 쓰면, 구형 브라우저는 그걸 이해하지 못한다. 그래서 "코드를 읽어서(parse) → 다른 모양으로 바꾸고(transform) → 다시 코드로 출력(codegen)"하는 변환기가 필요하다. 지금까지 이 일은 JavaScript로 짠 Babel·tsc·Terser가 했는데, JS로 JS를 변환하다 보니 큰 프로젝트에서 빌드가 느렸다.

swc는 이 변환기 전체를 Rust로 다시 작성해 수십 배 빠르게 만들었다. 그리고 단순한 "빠른 Babel 대체품"을 넘어, 파서·AST·변환·압축(minify)·번들러·CSS/HTML 처리·Wasm 플러그인까지 묶은 컴파일러 인프라 플랫폼으로 자랐다. Next.js가 내부적으로 swc를 쓰는 것이 대표 사례다.

swc컴파일러 + 라이브러리다. Rust 사용자에게는 swc_ecma_parser 같은 crate로, JS 사용자에게는 @swc/core npm 패키지로 동시에 제공된다. 핵심 철학은 "작게 쪼갠 crate를 조합"이다 — 파서만, 코드젠만, 압축기만 따로 골라 쓸 수 있다. 현재 워크스페이스에는 약 120개의 crate가 있고, 각각 파싱·AST·visitor·변환(ES2015~ES2022)·모듈·React·TypeScript·CSS·HTML·XML·플러그인 런타임을 담당한다.

용어
컴파일러 (트랜스파일러)
소스 코드를 입력받아 다른 형태의 코드로 바꿔 출력하는 프로그램. swc처럼 "한 언어(최신 JS/TS) → 같은/유사 언어(구형 JS)"로 바꾸는 것을 특히 트랜스파일러(transpiler)라 부른다. 내부는 보통 파서 → 변환 → 코드 생성의 3단 파이프라인이다.
용어
AST (Abstract Syntax Tree, 추상 구문 트리)
코드를 트리 구조의 데이터로 표현한 것. 1 + 2 * 3은 "더하기 노드 아래에 1과 (곱하기 노드)"로 표현된다. swc의 모든 변환은 텍스트가 아니라 이 AST를 조작하는 방식으로 이뤄진다. crate swc_ecma_ast가 JS/TS의 모든 노드 타입을 정의한다.

2왜 주목받는가

트렌딩 이유 · Babel·esbuild 등 경쟁 대비 장점.

JS 빌드 도구 시장은 오래 Babel(변환)+Terser(압축)+tsc(타입) 조합이 표준이었다. swc가 꾸준히 주목받는 이유는, 이 셋이 모두 JS로 작성돼 대형 프로젝트에서 느리다는 고질병을 Rust 네이티브로 정면 돌파했기 때문이다. 첫째 압도적 속도, 둘째 Babel 호환 변환 + TypeScript + 압축 + 번들 + 플러그인을 한 플랫폼에, 셋째 Next.js 채택으로 인한 사실상의 표준화다.

비교 항목Babel + Terseresbuildswc
구현 언어JavaScriptGoRust
변환 속도기준(느림)매우 빠름매우 빠름(멀티스레드)
Babel 호환성원조제한적높음(plugin-* 대응)
TypeScriptpreset 필요strip만네이티브 + 타입 검사 일부
압축(minify)Terser 별도내장내장(swc_ecma_minifier)
플러그인JS 플러그인Go 플러그인Wasm 플러그인(언어 무관)
라이브러리화npmnpm/Gocrate(Rust) + npm + wasm
대표 채택처전 생태계Vite(일부)Next.js · Deno · Parcel · Turbopack
기존 방식의 한계
"JS로 JS를 컴파일"의 구조적 병목

Babel은 강력하지만 JS 런타임 위에서 돈다. JS는 단일 스레드 기반이라 여러 파일을 진짜 병렬로 변환하기 어렵고, AST를 객체로 다루는 비용·GC 부담이 크다. 프로젝트가 수천 파일이 되면 빌드가 분 단위로 늘어진다. Terser 압축, tsc 타입 검사까지 각각 다른 도구로 AST를 매번 새로 파싱하는 중복도 컸다.

swc의 해결
"Rust 네이티브 + 단일 AST + crate 조합"

swc는 한 번 파싱한 하나의 AST를 파싱→변환→압축→코드젠 전 과정에서 재사용한다. Rust의 무비용 추상화·진짜 멀티스레딩으로 파일들을 병렬 처리하고, 문자열은 swc_atoms로 인터닝해 메모리를 아낀다. 무엇보다 모든 단계를 독립 crate로 쪼개 필요한 부품만 골라 쓰게 했다 — "거대한 모놀리식 도구"가 아니라 "컴파일러 부품 상점"인 셈이다.

3기술 스택 전체 지도

코어(Rust)·바인딩(Node/Wasm)·인프라 각각.

① 코어 — Rust 컴파일러 엔진

swc의 본체는 100% Rust이며, 하나의 Cargo 워크스페이스 안에 약 120개 crate가 들어 있다. crate 이름이 곧 역할을 말해 준다 — swc_ecma_*는 JavaScript/TypeScript(ECMAScript), swc_css_*는 CSS, swc_html_*는 HTML 처리다.

요소역할
Rust (edition 2021, MSRV 1.73)전체 코어 언어. 무비용 추상화·소유권으로 빠르고 안전.
swc_ecma_parser / _lexerJS·TS·JSX 소스 → AST. 손으로 작성한 재귀 하강 파서.
swc_ecma_astJS/TS의 모든 AST 노드 타입 정의.
swc_ecma_visit / swc_visitVisit(읽기)·VisitMut·Fold(변환) 방문자 트레이트 — 매크로로 자동 생성.
swc_ecma_transforms_*변환 모음: base(resolver·hygiene·fixer)·compat(ES3~ES2022)·module·react·typescript·optimization·proposal.
swc_ecma_codegenAST → JS 텍스트 + 소스맵 생성.
swc_ecma_minifierTerser 호환 압축기(트리 셰이킹·이름 단축).
swc_bundler모듈 그래프 분석 후 단일 번들로 묶기.
swc_commonspan·hygiene·에러 리포팅·소스맵 등 공통 인프라.
swc_atoms (hstr)문자열 인터닝 — 같은 식별자를 한 번만 저장해 메모리·비교 비용 절감.

② 바인딩 — JS에서 쓰게 만드는 다리

Rust 코어를 JavaScript 세계로 노출하는 층이다. bindings/ 디렉토리에 모여 있고, 네이티브(Node)와 WebAssembly 두 길이 있다.

요소역할
napi-rs (binding_core_node)Node.js 네이티브 애드온 생성. @swc/core가 OS·CPU별로 빌드된 .node 바이너리를 동적 로드.
WebAssembly (binding_core_wasm)crate-type=["cdylib"].wasm 빌드 — 브라우저·Deno·엣지 런타임에서 실행.
swc_cli (bindings)swc 커맨드라인 실행 파일.
전용 바인딩minifier·html·react-compiler·typescript(ts_fast_strip)별 Node/Wasm 바인딩 분리.
@swc/core 외 npm@swc/helpers(런타임 헬퍼)·@swc/types(TS 타입 정의) 등 packages/에 위치.

③ 플러그인 · 인프라 · 빌드

요소역할
Wasm 플러그인swc_plugin_runner + wasmtime/wasmer 백엔드. 사용자가 Rust로 짠 변환을 .wasm으로 컴파일해 swc에 끼움 — 언어·버전 독립.
swc_core플러그인 작성자용 통합 진입 crate. 기능별 feature flag로 필요한 모듈만 활성화.
모노레포 관리Rust는 Cargo workspace, JS는 pnpm workspace. 둘이 한 레포에 공존.
코드 생성tools/generate-code·proc 매크로(ast_node·string_enum)로 보일러플레이트 자동 생성.
릴리스changesets·git-cliff(CHANGELOG)·swc-releaser. crate는 "최신 버전끼리 항상 호환" 정책.
테스트test262(ECMAScript 공식 적합성 스위트) + 골든 픽스처 비교.
용어
napi-rs / Node 네이티브 애드온
Rust로 작성한 코드를 Node.js에서 일반 JS 함수처럼 import할 수 있게 컴파일하는 도구. swc는 OS·아키텍처마다(macOS arm64, Windows x64 등) 미리 빌드한 바이너리를 npm으로 배포해, 사용자가 Rust 툴체인 없이도 npm install @swc/core 한 줄로 네이티브 속도를 얻게 한다.
용어
Cargo 워크스페이스 (workspace)
여러 Rust crate(패키지)를 하나의 레포·하나의 Cargo.lock으로 함께 관리하는 구조. [workspace] members = ["crates/*", ...]로 선언한다. swc처럼 부품이 많은 프로젝트가 버전·의존성을 일관되게 유지하는 표준 방법이다.

4아키텍처 심화 분석

소스 → 파서 → AST → visitor/fold 변환 → 코드젠, 그리고 바인딩.

컴파일러 파이프라인 한눈에

swc는 전형적인 3단계 컴파일러 파이프라인이다. ① 소스 텍스트를 토큰으로 쪼개고(lexer) 트리로 조립한다(parser → AST). ② AST를 여러 변환 패스(pass)에 차례로 통과시킨다. ③ 최종 AST를 다시 코드 텍스트로 출력한다(codegen). JS 사용자가 @swc/core를 호출하면 napi/wasm 바인딩을 거쳐 이 Rust 파이프라인이 돈다.

JS/TS/JSX 소스 텍스트 (예: const x: number = 1) │ ┌────────────── napi-rs / wasm 바인딩 ───────────────┐ │ @swc/core (Node) · .wasm (브라우저/Deno/엣지) │ └────────────────────────┬───────────────────────────┘ │ (여기부터 100% Rust 코어) ┌───────────────▼────────────────┐ │ swc_common: SourceMap 등록 │ │ 파일·span(위치) 추적 시작 │ └───────────────┬────────────────┘ ▼ ┌─────────── 1) 파싱 (swc_ecma_parser) ────────────┐ │ Lexer: 텍스트 → 토큰 스트림 │ │ Parser: 토큰 → AST (재귀 하강) │ └────────────────────────┬─────────────────────────┘ ▼ AST (swc_ecma_ast) Program = Module | Script │ ┌─────────── 2) 변환 패스 체인 (transforms) ────────┐ │ ┌── resolver : 모든 식별자에 hygiene 마크 부여 │ │ ├── 사용자 패스: react / typescript / es2015... │ │ │ (각 패스 = Visit / VisitMut / Fold 구현체) │ │ ├── Wasm 플러그인: 외부 .wasm 변환 삽입 │ │ ├── hygiene : 충돌 식별자 실제 이름 변경 │ │ └── fixer : 괄호·우선순위 등 AST 정합성 보정 │ └────────────────────────┬─────────────────────────┘ ▼ ┌─────────── 3) 코드 생성 (swc_ecma_codegen) ───────┐ │ AST → JS 텍스트 + 소스맵(source map) │ │ (선택) swc_ecma_minifier 로 압축 │ └────────────────────────┬─────────────────────────┘ ▼ 변환된 JS 코드 + .map 반환 (바인딩을 통해 JS 세계로)

visitor / fold 패턴 — swc 변환의 심장

모든 변환은 AST를 재귀적으로 순회(traverse)하며 노드를 건드리는 방식으로 동작한다. swc는 이를 세 종류의 트레이트로 제공한다. 직접 트리를 재귀하는 코드를 짤 필요 없이, "내가 관심 있는 노드 타입의 메서드만 오버라이드"하면 나머지 순회는 자동으로 처리된다(swc_ecma_visit의 매크로가 생성).

트레이트성격쓰임
Visit읽기 전용(불변 참조)린트·분석·통계 — AST를 바꾸지 않고 정보만 수집.
VisitMut제자리 수정(&mut)대부분의 현대 변환. 노드를 그 자리에서 고침.
Fold값 소유·교체(소비 후 반환)노드를 통째로 다른 노드로 바꿀 때. node.fold_with(&mut pass).
VisitMut 변환 예시 (개념 코드)
// 모든 식별자 `foo`를 `bar`로 바꾸는 변환 패스
use swc_ecma_ast::Ident;
use swc_ecma_visit::{VisitMut, VisitMutWith};

struct RenameFoo;

impl VisitMut for RenameFoo {
    // Ident 노드를 만날 때만 개입 — 나머지 순회는 자동
    fn visit_mut_ident(&mut self, n: &mut Ident) {
        n.visit_mut_children_with(self); // 자식 먼저 순회
        if &*n.sym == "foo" {
            n.sym = "bar".into();
        }
    }
}

핵심은 visit_mut_ident만 정의했다는 점이다. 함수·블록·표현식 등 다른 모든 노드의 순회는 매크로가 생성한 기본 구현이 알아서 한다. 이것이 swc가 수백 개 변환을 짧은 코드로 유지하는 비결이다.

resolver · hygiene · fixer — 3대 기반 패스

swc_ecma_transforms_base에는 다른 모든 변환이 의존하는 세 가지 핵심 패스가 있다. 이름이 같은 변수라도 스코프가 다르면 다른 변수로 구분해야 하는 위생(hygiene) 문제를 푸는 장치다.

패스하는 일
resolver모든 식별자에 hygiene 마크(번호)를 붙인다. let a(바깥)과 let a(블록 안)을 a#0·a#1로 구분.
hygiene마크가 다른 동명 식별자를 실제 다른 이름으로 바꾼다. a#1a1.
fixer변환 후 깨진 AST를 보정. 연산자 우선순위에 맞춰 괄호를 자동 삽입 — (1+2)*3.
비유 — hygiene

큰 회사에 "김민수"가 여러 명 있다고 하자. 부서를 옮기며 코드를 합치다 보면 누가 누군지 헷갈린다. resolver는 모든 김민수에게 사원번호(#0, #1)를 붙여 일단 구분해 두는 단계다. 그 뒤 변환들이 마음껏 코드를 섞은 다음, hygiene이 마지막에 "김민수1, 김민수2"처럼 진짜 다른 이름으로 개명해 최종 코드에서 충돌이 없게 만든다. swc 변환이 안전하게 합성되는 토대다.

핵심 설계 패턴 정리

패턴swc에서의 구현
잘게 쪼갠 crate파서·AST·visit·변환·codegen·minify를 각각 독립 crate로. 필요한 것만 의존.
방문자 패턴 + 코드 생성Visit/VisitMut/Fold를 매크로로 자동 생성 → 변환은 관심 노드만 작성.
단일 AST 재사용한 번 파싱한 AST를 변환·압축·codegen이 공유 (중복 파싱 제거).
문자열 인터닝swc_atoms로 식별자를 한 번만 저장, 비교는 포인터로.
플러그인 = Wasm변환을 .wasm으로 격리 — 언어·버전 독립, 샌드박스 안전.
이중 바인딩같은 코어를 napi(네이티브)와 wasm(이식성) 두 경로로 노출.
proc 매크로 활용ast_node·string_enum으로 노드·열거형 보일러플레이트 제거.

5디렉토리 구조 해부

Cargo 워크스페이스(crates/) + JS 바인딩(bindings/·packages/).

swc/ ├── Cargo.toml ★ 워크스페이스 루트 — members = [crates/*, bindings/*, ...] ├── ARCHITECTURE.md ★ 공식 내부 구조 설명 (필독) ├── crates/ ★★ 약 120개 Rust crate (코어 전체) │ ├── swc 통합 컴파일러 (Compiler 진입점, v66+) │ ├── swc_core ★ 플러그인 작성자용 통합 진입 crate │ │ │ ├─ [JS/TS 코어] ─────────────────────────────────── │ ├── swc_ecma_ast AST 노드 타입 정의 │ ├── swc_ecma_lexer 텍스트 → 토큰 │ ├── swc_ecma_parser ★ 토큰 → AST (재귀 하강 파서) │ ├── swc_ecma_visit ★ Visit / VisitMut / Fold 트레이트 (매크로 생성) │ ├── swc_ecma_codegen AST → JS 텍스트 + 소스맵 │ ├── swc_ecma_minifier Terser 호환 압축기 │ │ │ ├─ [변환 모음] ──────────────────────────────────── │ ├── swc_ecma_transforms_base ★ resolver·hygiene·fixer (3대 기반 패스) │ ├── swc_ecma_compat_es2015..2022 버전별 다운레벨 변환 │ ├── swc_ecma_transforms_module CommonJS/AMD/UMD/SystemJS │ ├── swc_ecma_transforms_react JSX → JS, react-refresh │ ├── swc_ecma_transforms_typescript TS 타입 제거 │ ├── swc_ts_fast_strip ★ 타입만 빠르게 벗기는 경량 경로(Node --experimental) │ ├── swc_ecma_react_compiler React Compiler 연동 │ ├── swc_ecma_preset_env browserslist 기반 자동 폴리필/변환 │ │ │ ├─ [번들·플러그인·공통] ─────────────────────────── │ ├── swc_bundler 모듈 그래프 → 단일 번들 │ ├── swc_plugin_runner ★ Wasm 플러그인 실행기 (wasmtime/wasmer) │ ├── swc_common ★ span·hygiene·소스맵·에러 리포팅 │ ├── swc_atoms (hstr) 문자열 인터닝 │ │ │ ├─ [CSS / HTML / XML] ───────────────────────────── │ ├── swc_css_* CSS 파서·codegen·minifier·modules·prefixer │ ├── swc_html_* HTML 파서·codegen·minifier │ └── swc_xml_* XML 파서·codegen │ ├── bindings/ ★ JS 세계로 노출하는 바인딩 │ ├── binding_core_node ★ napi-rs → @swc/core 네이티브 애드온 │ ├── binding_core_wasm ★ cdylib → .wasm (브라우저/Deno/엣지) │ ├── binding_minifier_node/_wasm 압축기 전용 바인딩 │ ├── binding_html_node/_wasm HTML 전용 바인딩 │ ├── binding_react_compiler_node React Compiler 바인딩 │ ├── binding_typescript_wasm ts strip 전용 wasm │ └── swc_cli swc 커맨드라인 실행 파일 │ ├── packages/ ★ pnpm 워크스페이스 (npm 배포물) │ ├── core @swc/core (napi 바이너리 + JS 래퍼·타입) │ ├── helpers @swc/helpers (런타임 헬퍼) │ ├── types @swc/types (TS 타입 정의) │ ├── minifier / html / react-compiler 각 전용 패키지 │ ├── tools/ generate-code · swc-releaser ├── xtask/ cargo xtask (워크스페이스 작업 자동화) ├── data/ wasm 플러그인 ABI 호환 범위 JSON ├── package.json pnpm 루트 (build/test 스크립트) ├── pnpm-workspace.yaml JS 워크스페이스 정의 └── rust-toolchain 고정 Rust 버전
읽는 순서 추천

ARCHITECTURE.md로 전체 그림 → ② swc_ecma_ast에서 AST 노드가 어떻게 생겼나 → ③ swc_ecma_visit/src/lib.rs로 Visit/Fold 트레이트의 형태 → ④ swc_ecma_transforms_base의 resolver·hygiene·fixer로 기반 패스 → ⑤ bindings/binding_core_node/src/lib.rs로 Rust 코어가 어떻게 Node로 노출되는지. 코어의 통찰은 거의 이 다섯 곳에 있다.

6학습 포인트 (기술별)

대규모 컴파일러를 Rust로 설계하는 법에서 무엇을 배우나.

A. 컴파일러 3단 파이프라인 — 파서 / 변환 / codegen

swc는 "텍스트 → AST → 변환 → 텍스트"라는 컴파일러의 정석 흐름을 가장 깔끔하게 보여 주는 실전 코드다. 각 단계가 독립 crate라 경계가 명확하다. swc_ecma_parser로 입력을, swc_ecma_codegen으로 출력을 다루고, 그 사이를 변환 패스가 채운다. 컴파일러 강의의 추상 개념을 실제로 수백만 명이 쓰는 코드로 확인할 수 있다.

실습: 온라인 play.swc.rs 플레이그라운드에서 같은 TS 코드를 target만 바꿔(es5 vs es2022) 출력 차이를 비교하고, "어느 변환 패스가 그 차이를 만들었을지" 추론해 보라.

B. 방문자 패턴 — 트리를 안전하게 순회·변환

AST 같은 트리를 다룰 때 매번 재귀 코드를 짜면 실수가 잦다. swc의 Visit/VisitMut/Fold"순회는 프레임워크가, 개입은 내가"를 구현한 모범 사례다. 읽기/제자리 수정/소유 교체의 세 변형이 각각 언제 적합한지를 코드로 익히면, 다른 언어의 AST·DOM·설정 트리 작업에도 그대로 옮길 수 있다.

실습: swc_ecma_visit를 의존성으로 추가해, "코드 안의 console.log 호출 개수를 세는" Visit 구현체를 30줄로 작성해 보라(읽기 전용이므로 Visit 사용).

C. Rust 모노레포 / 워크스페이스 설계

120개 crate를 하나의 워크스페이스로 묶고, [workspace.dependencies]에서 버전을 중앙 관리하는 방식은 대형 Rust 프로젝트의 표준 청사진이다. 더해 같은 레포에서 pnpm 워크스페이스(JS)와 Cargo 워크스페이스(Rust)가 공존하는 폴리글랏 모노레포 구성도 배울 점이다.

실습: crate 2개(라이브러리 + 그걸 쓰는 바이너리)로 미니 워크스페이스를 만들고, 공통 의존성 버전을 루트 [workspace.dependencies]로 끌어올려 보라.

D. 네이티브 코어를 다언어로 노출하기 (napi + wasm)

swc의 가장 실용적인 교훈은 "빠른 코어는 Rust로, 사용성은 바인딩으로"다. 같은 코어 로직을 napi-rs로는 Node 네이티브 애드온, wasm으로는 브라우저/엣지용으로 동시에 내보낸다. 성능이 중요한 부분만 Rust로 짜고 나머지 생태계는 그대로 쓰는 이 전략은 esbuild·Biome·Rolldown 등 차세대 도구의 공통 패턴이다.

실습: napi-rs 튜토리얼로 "두 숫자를 더하는" Rust 함수를 Node에서 require해 호출하는 최소 애드온을 만들어 보라. 그다음 wasm-pack으로 같은 함수를 브라우저용 wasm으로도 빌드.

7시스템 / 환경 요구사항

"쓰는 것"과 "빌드/기여하는 것"의 요구사항이 다르다.

항목요구사항
JS 사용(소비)Node v10+ (개발은 v20+). npm install @swc/core — 미리 빌드된 네이티브 바이너리 자동 설치.
Rust 사용(crate)Rust 툴체인. MSRV 1.73. cargo add swc_ecma_parser 등으로 부품만 선택.
코어 빌드/기여Rust(rust-toolchain 고정 버전) + pnpm 10 + Node 20+. cargo build + pnpm build.
플랫폼(네이티브)napi 타깃 다수: macOS(x64·arm64)·Windows(x64·arm64·x86)·Linux(gnu·musl, arm 포함) 등.
Wasm 런타임브라우저·Deno·Cloudflare Workers 등 wasm 지원 환경에서 바이너리 없이 실행.
플러그인 빌드플러그인은 wasm32-wasip1 타깃으로 컴파일. swc_core 버전을 호스트와 맞춰야 함.
라이선스Apache-2.0 — 상용 포함 자유롭게 사용 가능.
참고 · 플러그인 버전 호환
swc_core ABI 호환성
Wasm 플러그인은 호스트 swc와 같은 swc_core ABI로 빌드돼야 동작한다. 버전이 어긋나면 플러그인이 로드되지 않으므로, Next.js 등에서 플러그인을 쓸 때는 swc_core 버전 매칭이 가장 흔한 트러블 지점이다(공식 "selecting-swc-core" 문서 참고).

8직접 해볼 수 있는 실습 과제

난이도별 5단계 — 플레이그라운드부터 Wasm 플러그인까지.

과제 1 난이도 ★☆☆☆☆

플레이그라운드로 변환 관찰

play.swc.rs에서 TypeScript + JSX 코드를 붙여 넣고, target·jsx·minify 옵션을 바꿔 출력이 어떻게 달라지는지 본다. "타입은 어디서 사라지고", "JSX는 무엇으로 바뀌는지"를 눈으로 확인하는 것이 목표.

과제 2 난이도 ★★☆☆☆

@swc/core 로 프로그램에서 변환 호출

Node 프로젝트에서 @swc/core를 설치하고 transformSync(code, options)로 TS 문자열을 JS로 변환하는 20줄 스크립트를 작성한다. .code.map(소스맵)을 모두 출력해 본다.

과제 3 난이도 ★★★☆☆

Rust에서 파서 직접 쓰기

Rust 프로젝트에 swc_ecma_parser·swc_common을 추가하고, JS 문자열을 파싱해 얻은 Module AST를 {:#?}로 디버그 출력한다. 간단한 표현식(a + b * c)이 어떤 트리로 표현되는지 관찰.

과제 4 난이도 ★★★★☆

나만의 VisitMut 변환 패스

swc_ecma_visit로 "모든 console.log를 제거하는" VisitMut 패스를 작성한다. 파서로 AST를 만들고 → 패스를 적용하고 → swc_ecma_codegen으로 다시 코드를 출력하는 전체 파이프라인을 직접 연결해 본다.

과제 5 난이도 ★★★★★

Wasm 플러그인 만들어 swc에 끼우기

공식 플러그인 템플릿(swc_core 기반)으로 간단한 변환(예: 특정 함수명 치환)을 작성해 wasm32-wasip1로 빌드하고, .swcrcjsc.experimental.plugins에 등록해 @swc/core에서 실제로 동작시킨다. swc_core 버전 매칭에 주의.

9관련 기술 심화 학습 로드맵

6주 코스 — Rust 기초부터 컴파일러 플러그인까지.

주차주제실습 · 참고
1주차Rust 기초 — 소유권·트레이트·enum·매크로 개념The Rust Book · 미니 CLI 작성
2주차컴파일러 기초 — 렉서·파서·AST·재귀 하강swc_ecma_ast 구조 읽기 · 과제 3
3주차방문자 패턴 — Visit/VisitMut/Foldswc_ecma_visit 정독 · 과제 4
4주차변환·스코프·hygiene — resolver/hygiene/fixerARCHITECTURE.md · transforms_base 읽기
5주차Rust↔JS 바인딩 — napi-rs·wasm-packnapi-rs 튜토리얼 · binding_core_node 분석
6주차Wasm 플러그인 + 모노레포 운영플러그인 템플릿 · Cargo/pnpm 워크스페이스 구성

10핵심 키워드 사전

본문·소스에 나온 용어 빠른 참조.

용어의미
swcSpeedy Web Compiler. Rust로 작성한 JS/TS 컴파일러·번들러 플랫폼.
트랜스파일한 언어를 같은/유사 언어로 변환(최신 JS/TS → 구형 JS).
AST코드를 트리로 표현한 추상 구문 트리. swc 변환의 작업 대상.
lexer / parser텍스트→토큰 / 토큰→AST. swc_ecma_lexer·swc_ecma_parser.
codegenAST→코드 텍스트 + 소스맵 생성. swc_ecma_codegen.
Visit / VisitMut / FoldAST 순회 트레이트: 읽기 / 제자리 수정 / 소유 교체.
transform passAST를 한 번 순회하며 바꾸는 변환 단위. 여러 개를 체인으로 연결.
resolver식별자에 hygiene 마크를 붙여 스코프를 구분하는 기반 패스.
hygiene동명이지만 다른 변수를 실제 다른 이름으로 만드는 위생 처리.
fixer변환 후 깨진 AST(괄호·우선순위 등)를 보정하는 패스.
minify코드 압축(공백·이름 단축·죽은 코드 제거). swc_ecma_minifier.
bundler여러 모듈을 의존성 그래프로 묶어 한 파일로. swc_bundler.
napi-rsRust 코드를 Node 네이티브 애드온으로 빌드하는 도구.
WebAssembly (wasm)브라우저·엣지에서 도는 이식성 바이너리 포맷.
Cargo workspace여러 Rust crate를 한 레포·한 lock으로 묶는 구조.
swc_core플러그인 작성자용 통합 진입 crate. ABI 버전 매칭 중요.
swc_atoms문자열 인터닝 — 식별자 메모리·비교 비용 절감.
test262ECMAScript 공식 적합성 테스트 스위트.

11참고 링크