
요약
2020년 글은 W3C 권고(1.0) 직후의 WebAssembly(이하 'wasm')를 다뤘습니다. '미래 버전에 포함될 작업'으로 스레드, SIMD(single instruction, multiple data), 참조 타입, 꼬리 호출, ESM(ECMAScript modules) 통합을 소개했고, Bytecode Alliance와 WASI(WebAssembly System Interface)가 그리는 브라우저 밖의 그림을 살펴봤습니다. 그리고 "아직까지 실서비스에서의 활발한 사용이 눈에 띄지는 않는다"라는 말과 함께, Sass가 wasm으로 플랫폼별 빌드 문제를 풀 수 있을지 모른다는 기대로 글을 맺었습니다.
이번 연재는 다음 네 편으로 구성됩니다.
- 1편: JavaScript(ECMAScript)
- 2편: 브라우저 밖의 JavaScript
- 3편: WebAssembly
- 4편: 라이브러리와 프레임워크
이 글에서는 2020년 글 이후 6년을 이어서 정리합니다. 2020년에 예고했던 기능들의 결말, wasm 2.0과 3.0, 지금 진행 중인 제안, WasmGC 덕분에 웹에서 쓸 수 있게 된 언어들, Component Model과 WASI가 바꾼 브라우저 밖의 생태계, JavaScript 개발자가 알게 모르게 쓰고 있는 wasm 패키지들을 살펴봅니다. 마지막으로 AI 도구와 에이전트가 wasm의 쓰임새를 어떻게 바꾸고 있는지도 정리합니다.
이 글의 내용은 2026년 10월을 기준으로 작성되었습니다. 글을 읽는 시점에 따라 글의 내용과 사실이 다를 수 있습니다.
2020년 이후 6년
2020년 글을 쓸 당시 wasm은 숫자 타입 네 가지(i32, i64, f32, f64)와 연속된 바이트 배열인 선형 메모리(linear memory)만 가진, 말 그대로 '컴파일 타깃'이었습니다. JavaScript와 문자열 하나를 주고받으려 해도 메모리에 바이트를 쓰고 그 위치를 숫자로 넘기는 글루 코드(glue code)가 필요했습니다.
6년이 지난 지금 wasm은 GC(가비지 컬렉션), 예외 처리, 64비트 주소 공간, 꼬리 호출을 명세에 담은 3.0이 되었습니다. Java, Kotlin, Dart 같은 GC 언어가 자체 런타임을 통째로 싣지 않고 wasm으로 컴파일됩니다. 브라우저 밖에서는 WASI가 Component Model 위로 옮겨 가며 0.2, 0.3이 나왔고, CDN 업체들이 wasm 기반 엣지 플랫폼을 운영하고 있습니다.
표 1 2019년 이후 주요 사건
| 시기 | 사건 |
|---|---|
| 2019년 12월 | wasm 1.0 W3C 권고 |
| 2020년 10월 | LibSass, Node Sass 비권장(deprecated) 지정 |
| 2023년 11월 | V8 팀, WasmGC 포팅 가이드 공개(Chrome 119부터 WasmGC 지원) |
| 2024년 1월 | WASI 0.2 출시, WASI가 Component Model 기반으로 전환 |
| 2024년 6월 | Google Sheets 계산 엔진의 WasmGC 전환 사례 공개 |
| 2024년 7월 | Node Sass 지원 종료(end-of-life) |
| 2024년 12월 | W3C, wasm 2.0 후보 권고안 스냅샷 구현 요청 |
| 2025년 9월 | wasm 3.0 발표 |
| 2025년 12월 | Akamai, Fermyon 인수 발표 |
| 2026년 6월 | WASI 0.3 출시, Safari 27 베타에서 JSPI(JS Promise Integration) 지원 |
Bytecode Alliance가 2026년 1월 발행한 10주년 회고에 따르면, wasm은 2015년 6월 17일 4개 브라우저 벤더가 동시에 발표했고 2017년 말에 4대 브라우저가 모두 지원했습니다. 2019년 3월에 WASI가 발표되었고, 2024년 1월 WASI 0.2가 Component Model과 함께 나왔습니다.
2020년 '미래 버전에 포함될 작업'의 결말
2020년 글은 WebAssembly WG(Working Group)와 CG(Community Group)가 진행하던 작업으로 다섯 가지를 꼽았습니다. 6년이 지난 지금 다섯 항목 가운데 네 가지는 모든 주요 브라우저가 지원하지만, 명세상의 위치는 제각각입니다.
표 2 2020년에 소개한 기능의 현재 상태(2026년 10월 기준)
| 기능 | 명세상 위치 | Chrome | Firefox | Safari |
|---|---|---|---|---|
| 스레드 | 별도 제안(Phase 4) | 74 | 79 | 14.1 |
| 128비트 SIMD | wasm 2.0 | 91 | 89 | 16.4 |
| 참조 타입 | wasm 2.0 | 96 | 79 | 15 |
| 꼬리 호출 | wasm 3.0 | 112 | 121 | 18.2 |
| ESM 통합 | 제안(Phase 3) | 미지원 | 미지원 | 미지원 |
표 2의 브라우저 버전은 WebAssembly Feature Status를, 제안의 단계는 WebAssembly/proposals를 따랐습니다.
스레드
2020년 글은 Chrome 74부터 wasm 스레드가 기본 활성 상태라고 소개했습니다. 지금은 Firefox 79, Safari 14.1까지 지원 범위가 넓어졌습니다. 공유 메모리(SharedArrayBuffer)의 원자적 읽기와 쓰기, wait/notify 명령어로 구성된 이 제안은 Phase 4(표준화 단계)에 있으며, 핵심 명세에는 아직 병합되지 않았습니다.
이 제안은 스레드 생성을 Web Workers에 맡기고, Emscripten 같은 툴체인이 이를 import로 노출하는 방식을 택했습니다. 공유 대상으로 정의한 것도 선형 메모리뿐이어서, 테이블, 전역 변수, WasmGC 객체는 스레드 사이에 공유할 수 없습니다. 이 한계를 다루는 후속 제안이 뒤에서 설명할 Shared-Everything Threads입니다.
128비트 SIMD
2020년 글에서 배열 4개 요소의 덧셈을 한 번의 CPU 명령으로 줄이는 예로 소개한 SIMD는 wasm 2.0에 Vector Instructions라는 이름으로 포함되었습니다. Chrome 91, Firefox 89, Safari 16.4부터 지원합니다(참고: Change History, WebAssembly Feature Status).
wasm 3.0에는 SIMD를 확장한 relaxed SIMD가 추가되었습니다. 플랫폼별로 결과가 조금씩 다를 수 있는 연산(예: 융합 곱셈 덧셈)을 명세가 허용한 범위 안에서 하드웨어에 맞게 실행하도록 해 성능을 더 끌어냅니다. Chrome 114, Firefox 145에서 지원하고 Safari는 아직 플래그 뒤에 있습니다(참고: WebAssembly Feature Status).
참조 타입
wasm 코드가 호스트 객체를 직접 참조하게 하는 참조 타입(reference types)은 wasm 2.0에 포함되었습니다. funcref와 externref 두 가지 값 타입이 추가되었고, externref로 JavaScript 객체를 wasm 쪽에서 숫자 인덱스로 바꾸지 않고 그대로 들고 있을 수 있게 되었습니다(참고: Change History).
참조 타입은 이후 wasm 3.0의 typed function references와 GC의 바탕이 되었습니다.
꼬리 호출
추가 스택 공간을 쓰지 않고 함수를 호출하는 꼬리 호출(tail calls)은 wasm 3.0에 return_call, return_call_indirect 명령어로 포함되었습니다. 함수형 언어(Scheme, OCaml 등)의 컴파일러가 재귀를 반복문으로 바꾸는 우회 기법 없이 wasm을 생성할 수 있게 되었습니다(참고: Change History).
V8은 v11.2에서 꼬리 호출을 출시했고, Emscripten으로 C++ 코루틴을 구현하는 사례를 함께 소개했습니다.
그림 1 꼬리 호출 시 호출자 프레임을 정리한 뒤 피호출 함수를 실행하는 스택 변화(출처: WebAssembly tail calls)
ESM 통합
다섯 항목 가운데 유일하게 아직 브라우저에 기본 탑재되지 않은 기능입니다. 제안은 Phase 3(구현 단계)에 있고, JavaScript 쪽 문법인 TC39의 Source Phase Imports도 Stage 3입니다(참고: WebAssembly/proposals, TC39 proposals). 진행 상황은 뒤의 '진행 중인 제안' 절에서 자세히 설명합니다.
wasm 2.0과 3.0
wasm 2.0: 권고 대신 '살아 있는 표준'으로
2020년 글은 W3C가 wasm 1.0을 웹의 네 번째 언어로 공식 권고(Recommendation)했다는 소식으로 시작했습니다. 같은 흐름이라면 2.0도 권고가 되었어야 하지만, 실제로는 다른 길을 택했습니다.
W3C는 2024년 12월 17일 wasm 2.0의 Core, JavaScript Interface, Web API 세 문서를 후보 권고안 스냅샷(Candidate Recommendation Snapshot)으로 발행하고 구현을 요청했습니다. 그 뒤 WebAssembly WG는 문서 상태 절에 다음 방침을 명시했습니다.
The WebAssembly Working group intends to maintain this publication in the Candidate Recommendation state and continually update it as a "living standard" rather than transition to Recommendation state.
즉 wasm 2.0 이후의 명세는 W3C 권고로 확정되지 않고, 후보 권고안 상태를 유지하며 계속 갱신됩니다. 2026년 10월 13일자로 발행된 W3C 문서(주소는 wasm-core-2)의 초록은 이미 "release 3.0"을 기술하고 있습니다. webassembly.org의 명세 페이지는 wasm 1.0만 W3C Recommendation으로, wasm 2.0은 2025년 6월 16일자 후보 권고안 초안으로 표기합니다.
wasm 2.0에는 부호 확장(sign extension) 명령어, 트랩 없는 실수-정수 변환, 다중 반환값(multiple values), 참조 타입과 다중 테이블, 대량 메모리 명령어(bulk memory), 128비트 SIMD가 포함되었습니다(참고: Change History). 대부분 2020년 무렵 이미 브라우저가 지원하던 기능입니다.
wasm 3.0
2025년 9월 17일, 명세 편집자 Andreas Rossberg는 webassembly.org에 wasm 3.0 완료를 알렸습니다. 발표문은 3.0이 "이미 대부분의 주요 웹 브라우저에 출시되어 있고, Wasmtime 같은 독립 엔진의 지원도 마무리 단계"라고 설명합니다.
그림 2 wasm 3.0 명세 표지(출처: Wasm 3.0 Completed)
표 3 wasm 3.0에 포함된 기능(출처: Wasm 3.0 Completed)
| 기능 | 내용 |
|---|---|
| Memory64 | 주소 공간을 4GB에서 이론상 16엑사바이트로 확장(웹에서는 최대 16GB) |
| multiple memories | 한 모듈이 여러 메모리를 선언하고 접근 |
| GC | 런타임이 관리하는 구조체(struct)와 배열(array) 타입 |
| typed function references | 서브타입과 재귀 타입을 갖는 참조, call_ref로 안전한 간접 호출 |
| 꼬리 호출 | return_call, return_call_indirect |
| 예외 처리 | 예외 태그와 exnref, try_table, throw_ref |
| relaxed SIMD | 구현에 따라 결과가 달라질 수 있는 SIMD 변형 |
| deterministic profile | 비결정적 연산의 결정적 기본 동작(블록체인 등 재현성이 필요한 환경용) |
| custom annotation syntax | 텍스트 포맷의 범용 주석 문법 |
| JS string builtins | wasm 모듈에서 JavaScript 문자열을 직접 다루는 내장 함수 |
GC
2020년 글의 'wasm은 JavaScript에 비해 왜 더 빠를까' 절에서는 GC 항목에 "wasm은 C/C++ 등의 언어에서와 같이 메모리는 수동으로 관리된다"라고 썼습니다. wasm 3.0에서는 이 설명에 예외가 생겼습니다.
GC 제안은 wasm에 선형 메모리와 별개의 저장 공간을 추가합니다. 컴파일러는 struct와 array 타입을 선언하고, 그 객체의 생존과 해제는 wasm 런타임(브라우저에서는 JavaScript 엔진의 가비지 컬렉터)이 맡습니다. 언어마다 다른 객체 모델을 wasm에 넣지 않고, 저수준 구성 요소만 제공한다는 점이 설계의 핵심입니다.
(module
(type $point (struct (field $x (mut f64)) (field $y (mut f64))))
(func (export "make") (param f64 f64) (result (ref $point))
(struct.new $point (local.get 0) (local.get 1)))
(func (export "x") (param (ref $point)) (result f64)
(struct.get $point $x (local.get 0))))
이 모듈이 반환한 $point 객체는 JavaScript 쪽에서는 내부를 들여다볼 수 없는 참조로 보이지만, JavaScript 객체와 같은 가비지 컬렉터가 관리하므로 둘 사이에 순환 참조가 생겨도 회수됩니다. GC 지원은 Chrome 119, Firefox 120, Safari 18.2, Node.js 22.0부터입니다(참고: WebAssembly Feature Status). V8과 Chrome 문서는 이 기능을 WasmGC라고 부르고, 이 글도 이 이름을 씁니다. 이 기능이 어떤 언어를 웹에서 쓸 수 있게 했는지는 'WasmGC가 연 언어들' 절에서 정리합니다.
Memory64와 multiple memories
2020년 글은 V8이 wasm 메모리 제한을 2GB에서 4GB로 늘렸다는 소식과 함께 "wasm64(명세명 memory64)에 대한 작업도 진행 중"이라고 소개했습니다. 그 memory64가 3.0에 포함되었습니다. 이론상 주소 공간은 16엑사바이트지만 웹에서는 16GB로 제한됩니다.
Memory64는 Chrome 133, Firefox 134, Node.js 24.0, Wasmtime 30부터 지원합니다. Safari는 Technology Preview 251에서 플래그로 제공하는 단계입니다(참고: WebAssembly Feature Status). 2020년 글에서 JP Morgan의 wasm 사례로 소개한 데이터 시각화 컴포넌트 Perspective는 2026년 7월 공개한 5.0에서 wasm64 빌드를 추가했습니다.
multiple memories는 한 모듈 안에서 여러 개의 독립된 선형 메모리를 쓰게 합니다. 여러 모듈을 하나로 정적 링크하는 도구, 민감한 데이터를 별도 메모리에 격리하는 용도 등을 염두에 둔 기능입니다. Chrome 120, Firefox 125에서 지원하고 Safari는 아직 지원하지 않습니다(참고: WebAssembly Feature Status).
예외 처리(exnref)
예외 처리 제안은 먼저 한 차례 브라우저에 출시되었지만(Chrome 95, Firefox 100, Safari 15.2), 이후 설계가 바뀌었습니다. 최종안은 예외를 값으로 다루는 exnref 타입과 try_table, throw_ref 명령어를 씁니다. webassembly.org의 기능 표는 앞의 것을 Legacy Exception Handling으로, 최종안을 Exception Handling with exnref로 구분합니다(참고: WebAssembly Feature Status).
최종안(exnref)은 Chrome 137, Firefox 131, Safari 18.4, Node.js 24.15, Wasmtime 47부터 지원합니다(참고: WebAssembly Feature Status). Kotlin/Wasm 문서가 여전히 "legacy exception handling"을 지원하는 브라우저를 요구 조건으로 명시하는 것처럼, 툴체인 쪽 전환은 진행 중입니다.
JS string builtins
GC 언어를 wasm으로 컴파일하면 문자열을 어떻게 다룰지가 문제가 됩니다. wasm 배열로 문자열을 따로 구현하면 DOM이나 JavaScript API에 넘길 때마다 변환 비용이 듭니다. JS string builtins는 wasm 모듈이 wasm:js-string 네임스페이스의 함수를 import해 JavaScript 문자열을 그대로 다루게 합니다.
다음 개념도는 문자열 "hello" 하나를 JavaScript로 넘기는 두 방식을 비교합니다.
그림 3 wasm과 JavaScript가 문자열을 주고받을 때 선형 메모리와 글루 코드를 거치는 방식과 JS string builtins 방식(개념도)
const compileOptions = {
builtins: ["js-string"],
importedStringConstants: "string_constants",
};
const bytes = await (await fetch("log-concat.wasm")).arrayBuffer();
const { instance } = await WebAssembly.instantiate(
bytes,
{ m: { log: console.log } },
compileOptions,
);
// wasm 모듈은 "wasm:js-string"의 concat 등을 import해 JS 문자열을 직접 다룬다.
instance.exports.main();
wasm:js-string에는 concat 외에 length, charCodeAt, codePointAt, substring, equals, compare, wasm i16 배열과 문자열을 오가는 fromCharCodeArray, intoCharCodeArray 등이 있습니다. Chrome 130, Firefox 134, Safari 26.2부터 지원하고, Node.js에서는 호출 경로에 따라 구분해야 합니다. wasm ESM import에서는 24.5.0과 22.19.0부터 String Builtins가 자동 활성화되므로 별도 플래그가 필요 없습니다. 직접 컴파일, 인스턴스화 API의 옵션과 플래그 지원은 이 ESM 경로와 별도로 확인해야 합니다(참고: Node.js: JavaScript String Builtins).
브라우저 간 호환성 개선 프로젝트인 Interop 2025도 wasm 영역의 과제로 Resizable Buffers 통합과 JS string builtins 개선을 꼽았습니다.
브라우저와 런타임 지원
wasm 3.0 기능의 지원 현황을 정리하면 다음과 같습니다. Chrome과 Firefox는 3.0 전체를 기본 지원하고, Safari는 Memory64, multiple memories, relaxed SIMD가 남아 있습니다.
표 4 wasm 3.0 주요 기능의 지원 버전(2026년 10월 기준)
| 기능 | Chrome | Firefox | Safari | Node.js | Wasmtime |
|---|---|---|---|---|---|
| GC | 119 | 120 | 18.2 | 22.0 | 47 |
| typed function references | 119 | 120 | 18 | 22.0 | 47 |
| 꼬리 호출 | 112 | 121 | 18.2 | 20.0 | 22 |
| 예외 처리(exnref) | 137 | 131 | 18.4 | 24.15 | 47 |
| Memory64 | 133 | 134 | 플래그 | 24.0 | 30 |
| multiple memories | 120 | 125 | 미지원 | 22.0 | 15 |
| relaxed SIMD | 114 | 145 | 플래그 | 21.0 | 15 |
| JS string builtins | 130 | 134 | 26.2 | 24.5 / 22.19(ESM import에서 자동 활성) | 해당 없음 |
표 4의 지원 버전은 WebAssembly Feature Status를 따랐고, Node.js의 JS string builtins는 Node.js ESM 문서에 나온 지원 조건을 함께 반영했습니다.
그림 4 wasm 기능 지원표의 Phase 5 기능 일부(2026년 10월 1일 캡처, 출처: WebAssembly Feature Status)
브라우저 밖 런타임에서는 Wasmtime이 2024년 11월 27.0에서 GC를 완전히 구현했고, 2026년 7월 47.0에서 GC와 예외 처리를 기본 활성화했습니다. 기능 감지가 필요하다면 2020년 글에서 소개한 wasm-feature-detect를 지금도 쓸 수 있습니다.
진행 중인 제안
wasm 제안은 Phase 0(사전 제안)부터 Phase 5(표준화 완료)까지 여섯 단계를 거칩니다(참고: WebAssembly W3C Process). 2026년 10월 기준 주요 제안의 단계는 다음과 같습니다.
표 5 진행 중인 주요 제안(2026년 10월 기준, 출처: WebAssembly/proposals)
| 단계 | 제안 |
|---|---|
| Phase 5(표준화 완료, 명세 병합 전) | JSPI, Web Content Security Policy |
| Phase 4(표준화) | Threads, Compact Import Section, Wide Arithmetic |
| Phase 3(구현) | ESM Integration, Stack Switching, Custom Page Sizes, Custom Descriptors and JS Interop |
| Phase 2(명세 초안) | JS Primitive Builtins, FP16, Compilation Hints, Acquire-Release Atomics 등 |
| Phase 1(기능 제안) | Component Model, Shared-Everything Threads, Type Imports, Profiles, JS Text Encoding Builtins 등 |
JSPI
C, C++, Rust로 작성된 기존 코드는 대개 파일 읽기나 네트워크 요청을 동기 함수로 호출한다고 가정합니다. 웹의 같은 기능은 Promise를 반환하는 비동기 API입니다. JSPI는 엔진이 wasm 실행 스택을 통째로 일시 중단했다가 Promise가 이행되면 재개하도록 합니다. import 쪽은 WebAssembly.Suspending으로, export 쪽은 WebAssembly.promising으로 감쌉니다(참고: WebAssembly JSPI has a new API).
다음 개념도는 C의 동기 함수가 웹의 비동기 API를 호출할 때 JSPI가 하는 일을 보여 줍니다.
그림 5 JSPI가 wasm 실행 스택을 중단했다가 Promise가 이행되면 재개하는 과정(개념도)
const compute_delta = () =>
fetch("https://example.com/data.txt")
.then((res) => res.text())
.then((txt) => parseFloat(txt));
const importObj = {
js: {
init_state: () => 2.71,
// 이 import를 호출하면 wasm 실행이 Promise 이행까지 중단된다.
compute_delta: new WebAssembly.Suspending(compute_delta),
},
};
// demoModule은 update_state를 export하는 wasm 모듈을 미리 컴파일한 WebAssembly.Module이다.
const instance = new WebAssembly.Instance(demoModule, importObj);
// wasm 쪽에서는 동기 함수인 update_state를 Promise를 반환하는 함수로 감싼다.
const update = WebAssembly.promising(instance.exports.update_state);
await update();
Emscripten에서는 -s JSPI 옵션 하나로 같은 효과를 낼 수 있습니다. Chrome 137에서 기본 활성화되었고, Safari는 2026년 6월 WWDC에서 공개한 Safari 27 베타에 JSPI를 추가했습니다. Firefox는 153부터 기본 활성화되었습니다(참고: Firefox 153 release notes for developers). Node.js는 26.0에서 지원하고, 2026년 8월 LTS(장기 지원) 버전인 24.20.0에도 백포트되었습니다(참고: WebAssembly Feature Status, Node.js 24.20.0 (LTS)).
ESM 통합: 두 가지 import 방식
2020년 글이 "ESM과 같이 JavaScript와 상호 작용하는 실행 가능한 wasm 모듈을 로딩하고 사용할 수 있게 한다"라고 한 줄로 소개한 제안입니다. 지금은 두 가지 import 방식을 정의합니다.
// 1. 인스턴스 단계(instance phase) import: 인스턴스화된 export를 바로 쓴다.
import { foo } from "./myModule.wasm";
foo();
// 2. 소스 단계(source phase) import: 컴파일된 WebAssembly.Module을 받아 직접 인스턴스화한다.
import source myModule from "./myModule.wasm";
const instance = new WebAssembly.Instance(myModule, imports1);
const { foo: foo1 } = instance.exports;
foo1();
두 번째 방식의 import source 문법은 TC39의 Source Phase Imports 제안으로, 2024년 10월 Stage 3에 올랐습니다. 제안 챔피언 Guy Bedford가 2026년 3월 TC39 회의에서 정리한 현황에 따르면, wasm ESM 통합은 2024년 4월 WebAssembly CG Phase 3에 올랐고 HTML 명세에도 반영되었습니다. Node.js는 두 방식을 모두 지원하며, 2025년 7월 31일 릴리스된 24.5.0에서 --experimental-wasm-modules 플래그를 해제했습니다. Deno는 인스턴스 단계를, Cloudflare Workers는 소스 단계를 지원합니다. 브라우저는 아직 기본 지원하지 않아 ES Module Shims 폴리필로 써야 합니다.
빌드 도구도 따라가고 있습니다. Vite는 2026년 6월 8.1에서 import { add } from "./add.wasm" 형태의 wasm ESM 통합을 정식 지원했습니다. vite-plugin-wasm을 관리하던 Menci가 이 기능을 Vite 본체에 기여했습니다.
Stack Switching
JSPI가 JavaScript Promise와 wasm 스택 사이를 잇는 기능이라면, Stack Switching은 wasm 안에서 여러 실행 스택을 직접 관리하게 합니다. 코루틴, async/await, 제너레이터, 경량 스레드처럼 실행을 중단했다가 재개하는 언어 기능을 언어마다 따로 명세에 넣지 않고, 연속(continuation)이라는 하나의 메커니즘으로 표현하는 것이 목표입니다. Phase 3이며, Chrome은 chrome://flags의 실험 플래그로 제공하고 Wasmer는 7.0에서 구현했습니다(참고: WebAssembly Feature Status).
Shared-Everything Threads
앞에서 설명한 Threads 제안은 선형 메모리만 공유합니다. GC 언어를 멀티스레드로 실행하려면 WasmGC 객체, 테이블, 전역 변수도 스레드 사이에서 공유할 수 있어야 합니다. Shared-Everything Threads는 이를 위한 Phase 1 제안입니다. 테이블, 함수, 전역 변수 등에 shared 표기를 붙여 공유 가능한 데이터만 공유되도록 검증하고, 스레드 로컬 전역 변수, 공유 WasmGC 데이터의 원자적 접근, Component Model의 스레드 생명주기 내장 함수를 추가합니다(참고: WebAssembly/proposals). 제안 문서 첫머리에 "자주, 크게 바뀔 수 있다"라는 경고가 붙어 있을 만큼 초기 단계입니다.
WasmGC가 연 언어들
런타임을 싣는 방식과 WasmGC 방식
2020년 글에서 소개한 Blazor WebAssembly는 .NET 런타임 자체를 wasm으로 컴파일해 브라우저에 내려보내는 방식이었습니다. GC 언어를 최초 표준 기능 집합인 wasm MVP(minimum viable product)로 옮기는 방법은 사실상 이것뿐이었습니다. 언어의 GC와 객체 모델을 선형 메모리 위에 직접 구현하고, 그 런타임 전체를 함께 배포해야 했습니다.
그림 6 언어 런타임 전체(파서, 최적화기, 가비지 컬렉터, 라이브러리 등)를 wasm으로 이식하는 방식의 구성(출처: A new way to bring garbage collected programming languages efficiently to WebAssembly)
V8 팀의 Alon Zakai는 2023년 11월 글에서 두 방식을 비교했습니다. WasmGC로 컴파일하면 언어는 자기 객체를 wasm의 struct와 array로 표현하고, 메모리 관리는 브라우저의 GC 기능에 맡깁니다. 글이 꼽은 장점은 다음과 같습니다.
- 코드 크기:
fannkuch벤치마크에서 WasmGC 버전은 2.3KB로, C나 Rust로 작성해 선형 메모리를 쓰는 버전(6.1~9.6KB)보다 작습니다. - 순환 참조: JavaScript와 wasm 사이의 순환 참조를 수집할 수 있습니다. 런타임을 따로 싣는 방식에서는 두 가비지 컬렉터가 서로의 객체를 알지 못합니다.
- 브라우저 GC 기능: 메모리 압축(compaction), 힙 프로파일러 같은 브라우저 GC의 기능을 그대로 씁니다.
그림 7 JavaScript 객체와 wasm 객체 사이의 참조와 순환 참조(출처: A new way to bring garbage collected programming languages efficiently to WebAssembly)
같은 글은 WasmGC를 지원하는 툴체인으로 Dart, J2Wasm(Java), Kotlin, OCaml, Hoot(Scheme)를 들었습니다. wasm 3.0 발표문은 Java, OCaml, Scala, Kotlin, Scheme, Dart를 GC 기능을 쓰는 언어로 소개합니다.
Google Sheets 계산 엔진
WasmGC의 첫 대규모 실사용 사례는 Google Sheets입니다. Sheets의 계산 엔진은 Java로 작성되어 있고, 2013년부터 브라우저의 Web Worker에서 JavaScript로 변환한 코드로 실행되어 왔습니다. 변환 도구는 처음에는 GWT, 이후에는 J2CL이었습니다. 2024년 6월 web.dev에 공개된 사례 연구에 따르면 JavaScript 버전은 Java 버전보다 3배 이상 오래 걸렸습니다.
Sheets 팀은 Chrome 팀과 함께 Java를 WasmGC로 컴파일하는 도구를 만들었습니다. 처음 만든 WasmGC 프로토타입은 오히려 JavaScript보다 2배가량 오래 걸렸습니다. 이후 최적화를 거친 최종 버전은 JavaScript보다 약 2배 빨라졌습니다. 출발점 대비 4배 개선입니다. 사례 연구가 꼽은 주요 개선은 세 가지입니다.
- 추측 인라이닝(speculative inlining)과 가상 호출 제거(devirtualization)로 약 40% 빨라졌습니다.
- Java 정규식 구현 대신 브라우저의
RegExp를 써서 정규식 연산이 약 100배 빨라졌습니다. - JavaScript 전용으로 짜인 코드 패턴을 제거해 플랫폼 중립적인 코드로 정리했습니다.
이 Java-to-WasmGC 컴파일러는 J2CL 저장소에 J2Wasm이라는 이름의 실험적 백엔드로 공개되어 있습니다.
엔진 쪽도 이 사례를 바탕으로 최적화를 이어 갔습니다. V8은 Chrome 137에서 wasm에 추측 기반 call_indirect 인라이닝과 역최적화(deoptimization)를 도입했습니다. 정적인 타입 정보가 풍부한 C/C++ 계열 wasm에는 필요가 적었던 기법이지만, 가상 호출이 많은 WasmGC 코드에는 효과가 컸습니다. Dart 마이크로벤치마크에서는 평균 50% 이상, 실제 애플리케이션에서는 1~8%(Google Sheets 최대 7%) 빨라졌습니다.
그림 8 추측 기반 call_indirect 인라이닝과 역최적화를 적용한 애플리케이션 벤치마크 결과(출처: Speculative Optimizations for WebAssembly using Deopts and Inlining)
Kotlin/Wasm과 Compose Multiplatform
JetBrains는 2025년 9월 Compose Multiplatform 1.9.0에서 wasm 기반의 웹 지원을 베타로 올렸습니다. 발표문은 "실험 단계를 넘어 얼리어답터가 실사용할 수 있는 상태"라고 설명하고, 구형 브라우저를 위한 대체 실행 수단(fallback)을 포함한 브라우저 간 호환성도 함께 내세웠습니다. Kotlin/Wasm 자체도 베타 상태이며, 브라우저용 wasm-js 타깃과 WASI 기반의 wasm-wasi 타깃을 제공합니다.
그림 9 Compose Multiplatform으로 만든 웹 앱 예시(출처: Compose Multiplatform 1.9.0 Released: Compose Multiplatform for Web Goes Beta)
Dart와 Flutter 웹
Flutter는 2024년 5월 3.22에서 wasm 컴파일을 안정화했습니다. Dart 코드는 WasmGC 모듈로 컴파일됩니다. 단, wasm으로 컴파일하려면 새 JavaScript 상호 운용 방식(dart:js_interop, package:web)으로 옮겨야 하고, 출력물은 JavaScript 환경을 전제로 하므로 Wasmtime 같은 독립 런타임에서는 실행되지 않습니다.
# Dart 웹 앱을 wasm으로 컴파일 dart compile wasm web/main.dart -o web/main.wasm # Flutter 웹 앱을 wasm으로 빌드 flutter build web --wasm
Flutter 팀이 2026년 8월 진행한 "Flutter WebAssembly Week" 글에 따르면, 기존 Flutter 웹 앱의 58% 이상이 코드 수정 없이 wasm으로 컴파일되고, Dart DevTools는 wasm 빌드를 기본으로 배포해 사용자의 97%가 wasm 버전을 씁니다. 프레임 시간은 최대 2배 빨라졌고, 압축 후 번들 크기 증가는 5% 이하입니다. 그런데도 flutter build web의 기본 출력은 아직 JavaScript입니다. pub.dev 패키지 일부가 예전 JavaScript 상호 운용 코드에 의존하기 때문입니다. Flutter 팀은 이 행사에서 패키지 의존성 문제를 함께 해결해 wasm을 기본 컴파일 대상으로 바꿀 기반을 마련하겠다고 밝혔습니다. 전환 시점은 제시하지 않았습니다.
그림 10 Flutter 웹 앱의 wasm 빌드와 JavaScript 빌드 프레임 시간 비교(출처: Try Flutter Web with WebAssembly Week)
벤치마크의 변화
WasmGC 언어가 늘면서 벤치마크도 바뀌었습니다. 2020년 글은 wasm 성능을 직접 측정해 볼 도구로 JetStream 2를 소개했습니다. 2026년 3월 WebKit, Google, Mozilla가 함께 공개한 JetStream 3은 wasm 측정 방식을 바꿨습니다. 시작 시간과 실행 시간을 따로 점수화하던 방식 대신 컴파일 비용과 처리량을 함께 반영하는 수명 주기 점수를 쓰고, Dart, Java, Kotlin으로 작성한 WasmGC 워크로드를 새로 포함했습니다.
브라우저 밖의 wasm: Component Model과 WASI
Bytecode Alliance의 6년
2020년 글은 2019년 말 출범한 Bytecode Alliance를 소개하며, 런타임으로 Wasmtime과 Lucet을 꼽았습니다. 두 프로젝트의 행보는 엇갈렸습니다. Lucet 팀은 2020년 중반 Wasmtime 개발로 합류하며 사전 컴파일(ahead-of-time compilation, AOT) 같은 기능을 Wasmtime에 옮겼고, Lucet은 지원을 종료했습니다.
그림 11 Bytecode Alliance 로고(출처: Bytecode Alliance)
Wasmtime은 2025년 4월 보안 수정을 2년간 제공하는 LTS 릴리스 정책을 도입했고, 같은 달 Bytecode Alliance의 첫 번째 핵심 프로젝트(Core Project)가 되었습니다.
Interface Types에서 Component Model로
2020년 글의 nanoprocess(모듈마다 메모리와 권한을 격리하는 경량 실행 단위) 보안 모델 설명에는 'Interface Types' 항목이 있었습니다. 당시 wasm에는 숫자 타입만 있었으므로, 모듈 사이에 문자열, 레코드, 배리언트 같은 복잡한 타입을 주고받으려면 별도 명세가 필요하다는 내용이었습니다. 이 작업은 이후 Component Model로 발전했습니다.
Component Model은 기존 wasm 모듈(core module)을 감싸는 상위 형식인 컴포넌트를 정의합니다. 컴포넌트는 WIT(Wasm Interface Type)이라는 인터페이스 정의 언어로 자신이 가져다 쓰는 것(import)과 내보내는 것(export)을 선언하고, 문자열이나 리스트 같은 값을 언어와 무관한 ABI(application binary interface)로 주고받습니다. Rust로 만든 컴포넌트를 JavaScript나 Python으로 만든 컴포넌트와 조합할 수 있습니다.
package example:greeter;
interface greet {
greet: func(name: string) -> string;
}
world greeter {
export greet;
}
다음 개념도는 위 greet 함수를 Rust 컴포넌트가 호출하고 JavaScript 컴포넌트가 구현할 때 값이 전달되는 방식을 보여 줍니다.
그림 12 메모리를 공유하지 않는 두 컴포넌트가 WIT 인터페이스로 문자열을 주고받는 방식(개념도)
Component Model은 WebAssembly CG에서는 아직 Phase 1 제안이지만, 브라우저 밖 생태계는 이미 이를 기반으로 움직이고 있습니다(참고: WebAssembly/proposals).
WASI 0.2와 0.3
2024년 1월 25일 출시된 WASI 0.2(Preview 2)는 WASI를 Component Model 위로 옮겼습니다. 발표문은 "WASI is now officially based on the Wasm component model, which makes it cross-language and virtualizable"이라고 설명합니다. 0.2의 API는 컴포넌트가 import, export하는 인터페이스 묶음인 월드(world)로 정의되며, POSIX와 비슷한 wasi:cli 월드(파일, 소켓, 시계, 난수)와 요청/응답 기반의 wasi:http 월드로 구성되었습니다.
Rust는 1.82(2024년 10월)부터 wasm32-wasip2 타깃을 tier 2로 지원해, rustup target add wasm32-wasip2만으로 WASI 0.2 컴포넌트를 빌드할 수 있습니다. 그 전에는 WASI 0.1로 빌드한 뒤 후처리로 컴포넌트를 만들어야 했습니다.
2026년 6월 11일에는 WASI 0.3이 출시되었습니다. 가장 큰 변화는 비동기 지원입니다. Component Model에 stream<T>, future<T>, async 함수가 1급 구성 요소로 추가되었고, 각 컴포넌트가 따로 이벤트 루프를 돌리지 않고 호스트가 하나의 이벤트 루프를 관리합니다. wasi:http는 이전의 프록시 구조 대신 service와 middleware 두 월드로 재편되었습니다. 발표 당시 Wasmtime 45가 릴리스 후보를 실행하고 있었고 Wasmtime 46에서 Component Model 비동기와 함께 WASI 0.3.0을 기본 활성화할 계획이었습니다. JavaScript용 도구 jco는 0.3 전체를 지원합니다. Rust, Go, JavaScript(componentize-js), Python(componentize-py), C#, C의 게스트 툴체인 지원은 진행 중입니다.
Component Model 1.0으로 가는 길
Bytecode Alliance는 WASI 0.3 출시 직전인 2026년 6월 8일, Component Model 1.0까지의 과제를 정리했습니다. 날짜는 제시하지 않았고, 작업 영역으로 다음 다섯 가지를 꼽았습니다.
- ABI 개선: 호스트가 메모리를 미리 할당하는 대신 피호출자가 지연 값 핸들을 돌려주는 방식
- 최소 두 개 브라우저 엔진의 네이티브 지원
- 게스트와 호스트용 C ABI를 통한 구현 단순화
- 툴체인과 도구 확충
- WIT 표현력 보강: 선택적 import, 콜백, map 타입 등
그림 13 게스트와 호스트용 C ABI로 툴체인과 런타임 구현을 공유하는 구조(출처: The Road to Component Model 1.0)
jco: JavaScript에서 컴포넌트 다루기
jco는 Bytecode Alliance가 만든 JavaScript용 컴포넌트 도구로, 2024년 2월 1.0이 나왔습니다. 2026년 3월 Bytecode Alliance 글은 jco를 "JS WebAssembly 생태계의 멀티툴"이라고 소개합니다. jco transpile은 어떤 언어로 만든 컴포넌트든 core wasm 모듈과 JavaScript 글루 코드로 변환해, Component Model을 지원하지 않는 Node.js와 브라우저에서도 실행할 수 있게 합니다. 이때 파일 시스템, HTTP 같은 WASI API는 JavaScript로 작성한 대체 구현(shim)이 제공합니다. jco componentize는 반대로, JavaScript 소스를 wasm으로 컴파일된 JavaScript 런타임 StarlingMonkey와 묶어 컴포넌트로 만듭니다.
그림 14 jco transpile이 컴포넌트를 ESM과 core wasm으로 변환하는 구조(출처: Five ways of looking at Jco, Part 1)
# 컴포넌트를 JavaScript ESM과 core wasm으로 변환 npx jco transpile component.wasm --out-dir ./output
브라우저의 Component Model
브라우저에서 wasm은 여전히 JavaScript를 거쳐야만 웹 API에 닿습니다. Mozilla의 Ryan Hunt는 2026년 2월 Mozilla Hacks 글에서 이 상황을 "WebAssembly is a second-class language on the web"이라고 표현했습니다. console.log 하나를 호출하려 해도 wasm 메모리의 문자열을 JavaScript 문자열로 디코딩하고, 전역 객체에서 함수를 찾는 글루 코드가 필요합니다. 글은 2020년의 TodoMVC 측정에서 글루 코드를 걷어냈을 때 DOM 변경 적용 시간이 45% 줄었다는 결과를 인용합니다.
그림 15 wasm이 JavaScript를 거쳐 웹 플랫폼에 접근하는 현재 계층 구조(출처: Why is WebAssembly a second-class language on the web?)
Hunt가 제안하는 해법이 브라우저의 Component Model 지원입니다. WebIDL에서 파생한 WIT 인터페이스로 웹 API를 선언하고, 컴포넌트가 이를 직접 import합니다. 로딩도 스크립트 태그 하나로 끝납니다.
<script type="module" src="component.wasm"></script>
글에 따르면 Mozilla는 최근 WebAssembly CG 대면 회의에서 성능 측정 결과를 발표했고, Chrome V8 팀은 Component Model 구현을 검토하는 이슈를 열었습니다. 글은 이를 약속이 아닌 의미 있는 신호로 봅니다. Component Model 1.0 로드맵이 "최소 두 개 브라우저 엔진의 네이티브 지원"을 조건으로 건 것과 같은 방향입니다.
어디에서 사용되고 있나
웹에서의 사용률
2020년 글은 "아직까지 실서비스에서의 활발한 사용이 눈에 띄지는 않는다"라고 썼습니다. HTTP Archive의 Web Almanac 2025 WebAssembly 장은 그 뒤의 변화를 숫자로 보여 줍니다.
- wasm 모듈을 쓰는 사이트는 데스크톱 0.35%, 모바일 0.28%로 약 4만 3천 개입니다. 2021년 0.04%에서 늘었지만, 2024년(데스크톱 0.36%)과 비교하면 정체 상태입니다.
- 상위 1,000개 사이트로 좁히면 데스크톱 2%, 모바일 1.27%로 사용률이 높아집니다.
- 언어와 툴체인으로는 .NET/Mono와 Blazor가 데스크톱 모듈의 41.7%로 가장 많고, Microsoft 계열 라이브러리까지 합치면 약 79%입니다. Emscripten(C++)은 데스크톱 10.1%입니다.
그림 16 연도별 wasm 사용 사이트 비율(출처: WebAssembly | 2025 | The Web Almanac by HTTP Archive)
2020년 글은 Blazor WebAssembly를 "지난 5월 19일 발표"된 새 기능으로 소개했는데, 지금은 Blazor가 웹에서 발견되는 wasm 모듈 가운데 가장 큰 비중을 차지합니다. 동시에 wasm을 쓰는 사이트가 전체의 1%에 못 미친다는 사실은 wasm이 모든 사이트에 퍼지는 기술이라기보다 일부 애플리케이션에 집중적으로 쓰이는 기술임을 보여 줍니다.
엣지와 서버리스
브라우저 밖에서는 CDN과 엣지 플랫폼이 wasm의 대표적인 사용처가 되었습니다. 공통적으로 내세우는 이유는 샌드박스 격리와 빠른 콜드 스타트입니다.
- Fastly Compute: 문서에 "보안과 이식성은 코드를 WebAssembly로 컴파일해 확보하고, Wasmtime으로 실행한다"라고 명시합니다. 공식 SDK는 Rust, JavaScript, Go, C++입니다.
- Cloudflare Workers: Rust로 Worker 전체를 작성하거나
.wasm모듈을 import해 쓸 수 있습니다. Python Workers는 wasm으로 컴파일한 CPython인 Pyodide로 동작하며, 배포 시점에 top-level import까지 실행한 wasm 메모리의 스냅숏을 저장해 두고 그 상태에서 시작해 콜드 스타트를 줄입니다. 스레드는 지원하지 않고, WASI는 일부 시스템 콜만 구현된 실험 단계입니다. - Spin: Fermyon이 만든 wasm 컴포넌트 기반의 이벤트 구동 마이크로서비스 프레임워크로, CNCF(Cloud Native Computing Foundation)의 샌드박스 프로젝트입니다. Akamai는 2025년 12월 1일 Fermyon 인수를 발표했습니다. 발표문에 따르면 Fermyon은 Spin과 SpinKube를 계속 유지하며, 두 공동 창업자는 Akamai의 Cloud Technology Group에 합류합니다.
플러그인 시스템
신뢰할 수 없는 서드파티 코드를 애플리케이션 안에서 실행해야 하는 플러그인 시스템도 wasm이 자리 잡은 분야입니다. 2020년 글의 nanoprocess 모델이 겨냥한 문제와 같습니다. Extism은 "WebAssembly로 만드는 언어 간 프레임워크"를 표방하며, Python, Node.js, Ruby, Go, Java, .NET 등 15개 이상의 호스트 SDK를 제공합니다. 플러그인은 기본적으로 샌드박스 안에서 실행됩니다.
Component Model 쪽에서는 같은 인터페이스를 import하고 export하는 컴포넌트를 호출자와 피호출자 사이에 두어, 양쪽을 수정하지 않고 인증, 계측, 실행 기록 같은 기능을 추가하는 개입(interposition) 방식이 소개되고 있습니다.
JavaScript 생태계 속의 wasm
JavaScript 개발자 대부분은 wasm 모듈을 직접 작성하지 않습니다. 대신 npm 패키지 안에 들어 있는 wasm을 알게 모르게 쓰고 있습니다.
표 6 wasm 기반 주요 npm 패키지의 주간 다운로드(출처: npm registry API, 패키지별 조회, 2026년 9월 28일~10월 4일)
| 패키지 | 내용 | 주간 다운로드 |
|---|---|---|
@electric-sql/pglite |
wasm으로 빌드한 PostgreSQL | 26,162,912 |
onnxruntime-web |
브라우저용 ONNX 추론 엔진 | 6,991,493 |
@huggingface/transformers |
Transformers.js | 4,717,799 |
@sqlite.org/sqlite-wasm |
SQLite 공식 wasm 빌드 | 1,064,448 |
@ffmpeg/ffmpeg |
ffmpeg.wasm | 934,161 |
pyodide |
wasm으로 빌드한 CPython | 874,940 |
@bytecodealliance/jco |
컴포넌트 도구 | 693,873 |
네이티브 애드온의 wasm 폴백
2020년 글은 Node Sass가 릴리스마다 OS, 아키텍처, Node.js 버전별로 100개 이상의 빌드를 제공해야 한다는 점을 wasm이 풀 수 있는 문제로 꼽았습니다.
Rust로 Node.js 애드온을 만드는 프레임워크 NAPI-RS는 2025년 7월 v3에서 wasm32-wasip1-threads 타깃을 지원했습니다. 패키지의 로더는 먼저 플랫폼에 맞는 네이티브 바이너리를 찾고, 없으면 @scope/pkg-wasm32-wasi 형태로 함께 배포된 wasm 패키지를 씁니다. 같은 빌드로 브라우저나 WebContainers에서도 실행할 수 있습니다.
2026년 3월 Vite 8에서 Rollup과 esbuild를 대체한 번들러 Rolldown이 이 방식을 씁니다. 프리빌드 바이너리를 쓸 수 없으면 wasm 바이너리로 자동 전환하고, NAPI_RS_FORCE_WASI 환경 변수로 wasm 빌드를 강제할 수도 있습니다(참고: NAPI-RS: WebAssembly).
다음 개념도는 Rolldown 패키지의 로더가 실행 환경에 따라 바이너리를 고르는 순서를 보여 줍니다.
그림 17 NAPI-RS 로더가 플랫폼에 맞는 네이티브 바이너리를 찾고, 없으면 wasm 빌드를 불러오는 순서(개념도)
# Rolldown의 wasm 빌드 설치(npm) npm install --cpu wasm32 --os wasip1-threads # 네이티브 바이너리가 있어도 wasm 빌드를 쓰도록 강제 NAPI_RS_FORCE_WASI=error npx rolldown -c
Rspack도 2025년 6월 1.4에서 wasm 빌드를 추가해 StackBlitz의 WebContainers 같은 브라우저 환경에서 실행할 수 있게 되었습니다.
한편 2020년 글이 Rust 개발 참고 자료로 소개한 rustwasm 조직은 2025년 7월 21일 운영 종료 공지에 따라 아카이브되었습니다. wasm-bindgen은 새 전용 조직으로 옮겨 유지보수를 이어 가고, wasm-pack을 비롯한 나머지 저장소는 아카이브되거나 희망하는 관리자에게 이관되었습니다.
브라우저 속 Node.js: WebContainers
StackBlitz는 2021년 5월 브라우저 탭 안에서 Node.js를 실행하는 WebContainers를 공개했습니다. 발표 당시 "WebAssembly 기반 운영체제"로 소개했고, 빌드는 로컬 환경보다 최대 20% 빠르고 패키지 설치는 Yarn, npm보다 5배 이상 빠르다고 밝혔습니다. 지금은 StackBlitz뿐 아니라 Svelte, Angular, Nuxt의 대화형 튜토리얼이 WebContainers 위에서 동작합니다. 뒤의 'AI 도구로 인한 변화' 절에서 다룰 bolt.new도 이 기술을 씁니다.
그림 18 WebContainers 공개 당시 소개한 StackBlitz 화면(출처: Introducing WebContainers: Run Node.js natively in your browser)
브라우저 속 데이터베이스
wasm 덕분에 서버용 데이터베이스를 브라우저와 JavaScript 런타임에서 실행할 수 있게 되었습니다.
- PGlite: PostgreSQL 전체를 wasm으로 빌드한 TypeScript 라이브러리로, gzip 기준 3MB 미만입니다. 브라우저, Node.js, Bun, Deno에서 실행되고 IndexedDB나 OPFS(origin private file system)에 데이터를 영속화합니다. pgvector, PostGIS 확장도 동적으로 불러올 수 있습니다. 2026년 6월 0.5에서 PostgreSQL 18.3으로 올라갔습니다.
- SQLite wasm: SQLite 프로젝트가 직접 관리하는 공식 wasm 빌드로, OPFS를 저장소로 쓰는 가상 파일 시스템(virtual file system, VFS) 계층을 제공합니다.
- DuckDB-Wasm: 분석용 내장 데이터베이스 DuckDB의 wasm 빌드입니다. 2026년 9월 DuckDB 블로그는 OPFS로 새로 고침 후에도 유지되는 브라우저 데이터베이스를 만드는 방법을 소개했습니다.
import { PGlite } from "@electric-sql/pglite";
const db = await PGlite.create("idb://my-app"); // IndexedDB에 영속화
await db.query("CREATE TABLE IF NOT EXISTS items (id SERIAL, name TEXT)");
await db.query("INSERT INTO items (name) VALUES ($1)", ["wasm"]);
const { rows } = await db.query("SELECT * FROM items");
다른 언어의 런타임과 라이브러리
- Pyodide: CPython을 Emscripten으로 wasm에 이식한 프로젝트로, 브라우저와 Node.js에서 Python을 실행합니다. 순수 Python 패키지는 micropip로 설치하고, NumPy, pandas, SciPy, scikit-learn처럼 C나 Rust 확장을 가진 패키지도 미리 빌드해 제공합니다. 2026년 6월 314.0부터는 PEP 783(Emscripten 패키징) 채택에 따라 패키지 개발자가 Pyodide용 wheel을 PyPI에 직접 올리고, 사용자는 이를 런타임에 설치할 수 있습니다. 2026년 10월 기준 안정판은 314.0.7입니다.
- ffmpeg.wasm: FFmpeg의 wasm/JavaScript 이식판으로, 브라우저 안에서 영상과 음성을 녹화, 변환, 스트리밍합니다. GitHub 별은 1만 7,800개를 넘습니다.
wasm이 항상 빠르지는 않다
2019년 글의 'WebAssembly 성능 테스트 사례'가 소개한 것처럼, wasm이 기대만큼 빠르지 않은 경우는 지금도 있습니다. OpenUI 팀은 2026년 3월, Rust로 작성해 wasm으로 쓰던 파서를 TypeScript로 다시 작성했더니 호출당 2.24.6배 빨라졌다고 밝혔습니다. 원인은 계산이 아니라 경계였습니다. 문자열을 wasm 메모리로 복사하고, 결과를 JSON 문자열로 직렬화해 다시 복사해 오고, V8이 그것을 JavaScript 객체로 역직렬화하는 과정 전체가 비용이었습니다. serde-wasm-bindgen으로 JavaScript 객체를 직접 만들어 넘기는 방법은 필드마다 경계를 넘나들어 호출당 시간이 오히려 929% 더 걸렸습니다.
이 사례는 앞에서 설명한 JS string builtins나 브라우저의 Component Model 지원이 왜 필요한지를 거꾸로 보여 줍니다. JavaScript와 데이터를 자주 주고받는 작업이라면 wasm으로 옮기기 전에 경계 비용부터 측정해 보는 것이 좋습니다.
AI 도구로 인한 변화
LLM(대규모 언어 모델)과 AI 에이전트는 wasm의 쓰임새를 세 방향에서 바꾸고 있습니다. 브라우저 안에서 모델을 실행하는 추론 엔진, AI가 생성한 코드를 실행하는 샌드박스, 그리고 에이전트가 쓰는 도구를 배포하는 형식입니다.
브라우저 내 AI 추론
브라우저에서 모델을 실행하는 JavaScript 라이브러리들은 대부분 wasm과 WebGPU 두 가지 백엔드를 함께 씁니다. wasm은 연산자 지원 범위가 넓어 폴백 역할을 하고, WebGPU는 GPU로 속도를 냅니다.
- ONNX Runtime Web: 실행 공급자(execution provider)로 wasm(CPU, SIMD, 멀티스레드), WebGPU, WebNN을 지원합니다. 문서는 "모든 ONNX 연산자는 WASM에서 지원되지만 WebGL, WebGPU, WebNN은 일부만 지원한다"라고 설명합니다.
- Transformers.js: Hugging Face의 JavaScript용 Transformers로, ONNX Runtime Web 위에서 동작합니다. 2024년 10월 v3에서 WebGPU를 지원하며 "WASM 대비 최대 100배"의 속도를 내세웠습니다. 2026년 2월 v4에서는 ONNX Runtime 팀과 함께 C++로 다시 작성한 WebGPU 런타임을 도입해 Node.js, Bun, Deno에서도 WebGPU 가속을 쓸 수 있게 했고, wasm 런타임 파일을 캐시해 첫 로드 이후 오프라인으로 동작하는
env.useWasmCache설정을 추가했습니다. - WebLLM: LLM을 브라우저에서 실행하는 엔진으로, 연산은 WebGPU로 하고 런타임 계층은 Apache TVM이 컴파일한 wasm(TVMjs)을 씁니다. OpenAI API와 호환되는 인터페이스를 제공합니다.
그림 19 Transformers.js v4의 WebGPU 지원 환경(브라우저, 런타임, 프레임워크)(출처: Transformers.js v4: Now Available on NPM!)
import * as ort from "onnxruntime-web/webgpu";
// WebGPU를 먼저 시도하고, 지원하지 않는 연산자나 환경에서는 wasm으로 실행
const session = await ort.InferenceSession.create("model.onnx", {
executionProviders: ["webgpu", "wasm"],
});
반대 방향의 사례도 있습니다. Mozilla는 2025년 8월, Firefox 내장 AI 기능(PDF 이미지 대체 텍스트 생성, 탭 그룹 제안 등)이 Transformers.js를 통해 쓰던 wasm 기반 onnxruntime-web을 Firefox에 내장한 네이티브 C++ ONNX Runtime으로 교체했습니다. 추론은 2~10배 빨라졌고 wasm 준비(warm-up) 시간이 사라졌으며, PDF 대체 텍스트 생성 지연은 3.5초에서 350밀리초로 줄었습니다. Transformers.js의 백엔드만 바꿨기 때문에 기능 코드는 고치지 않았습니다. 웹 페이지라면 누구나 쓸 수 있는 공통 실행 환경이라는 wasm의 역할과 그 성능의 한계를 함께 보여 주는 사례입니다.
AI가 생성한 코드를 실행하는 샌드박스
에이전트가 사용자 요청을 코드로 바꿔 즉시 실행하는 구조가 흔해지면서, 그 코드를 어디서 실행할지가 보안 문제가 되었습니다. NVIDIA의 Joseph Lucas는 2024년 12월 글에서 LLM이 생성한 코드는 프롬프트 주입(prompt injection)과 생성 오류의 위험이 있으므로 안전한 환경에서 실행해야 한다고 지적했습니다. 그리고 "정규식이나 제한된 런타임으로 Python을 걸러내는 방식은 충분하지 않다"라며, Pyodide로 브라우저 안에서 실행하는 방식을 제안했습니다. 컨테이너나 가상 머신보다 가볍고, 악성 코드의 영향을 사용자 한 명의 브라우저 샌드박스로 제한한다는 이유입니다.
그림 20 LLM이 생성한 Python 코드를 Pyodide로 사용자 브라우저에서 실행하는 흐름(출처: Sandboxing Agentic AI Workflows with WebAssembly)
브라우저 안에서 개발 환경 전체를 돌리는 쪽으로는 StackBlitz의 bolt.new가 있습니다. 앞에서 설명한 WebContainers 위에서 동작하며, README는 AI 모델에 "파일 시스템, Node 서버, 패키지 관리자, 터미널, 브라우저 콘솔을 포함한 환경 전체에 대한 제어권"을 준다고 설명합니다. 생성된 앱을 설치하고 실행하는 작업이 서버가 아니라 사용자의 브라우저 탭 안에서 일어납니다. 2026년 2월 Leaning Technologies가 공개한 BrowserPod 1.0도 같은 문제를 겨냥합니다. wasm으로 컴파일한 언어 런타임을 브라우저 안에서 실행하는 코드 샌드박스로, 첫 릴리스는 Node.js 22를 지원하고 Python, Ruby, Go, Rust를 2026년 중에 추가할 계획입니다. 발표문은 용도로 "AI가 생성한 코드를 사용자 곁의 샌드박스에서 실행하는 것"을 첫머리에 둡니다.
서버 쪽에서는 Wasmer가 2026년 3월 Edge.js를 공개했습니다. Node.js 24와 호환되는 런타임으로, --safe 모드에서는 시스템 콜과 네이티브 코드를 WASIX(Wasmer가 WASI에 스레드, 소켓 같은 POSIX 기능을 더한 확장) 기반 wasm 샌드박스로 격리합니다. Node.js 테스트 3,626개 중 3,592개(99.1%)를 통과하고, 현재 Node.js와 비교해 속도가 네이티브로 실행하면 520%, Wasmer로 완전히 샌드박스화해 실행하면 약 30% 떨어진다고 밝혔습니다. 발표문은 AI와 엣지 워크로드를 안전하게 실행하는 것을 목표로 듭니다. 발표문에 따르면 개발에는 주로 GPT-5.4를 썼고, 최소 12년이 걸렸을 개발을 몇 주로 줄였습니다. OpenAI도 이 과정을 Codex 사례로 공개했습니다.
그림 21 Edge.js의 --safe 모드 샌드박스 구조(출처: Edge.js: Running Node apps inside a WebAssembly Sandbox)
반면 한계를 인정한 사례도 있습니다. Pydantic은 Deno 안에서 Pyodide로 LLM 생성 Python 코드를 실행하는 MCP(Model Context Protocol) 서버 mcp-run-python을 만들었지만, 2026년 1월 30일 저장소를 아카이브했습니다. README에는 "Pyodide 안에서 Python을 합리적인 지연 시간으로 안전하게 실행할 방법이 없다"라는 판단과 함께 "Pyodide에서 실행되는 Python 코드는 임의의 JavaScript를 실행할 수 있다"라는 경고가 남아 있습니다. 후속 프로젝트 Monty는 wasm 기반이 아니라 Rust로 작성한 Python 인터프리터로, 파일 시스템, 환경 변수, 네트워크가 아예 없는 샌드박스를 택했습니다. wasm 샌드박스는 모듈이 가진 import의 범위만큼만 안전합니다. Pyodide처럼 JavaScript 전체에 접근하는 경로를 기본으로 제공하는 런타임에서는 그 경로가 곧 공격 경로가 됩니다.
MCP 도구를 wasm 컴포넌트로
로컬에서 실행하는 MCP 서버는 별도 격리 장치가 없으면 실행한 사용자의 권한으로 동작합니다. Microsoft는 2025년 8월 이 문제에 wasm 컴포넌트로 접근하는 Wassette를 공개했습니다. Wassette는 Wasmtime 위에서 동작하는 MCP 서버로, 에이전트가 OCI(Open Container Initiative)의 컨테이너 이미지 저장소 규격을 따르는 레지스트리에서 wasm 컴포넌트를 내려받아 실행하게 합니다. 컴포넌트의 WIT 인터페이스를 읽어 MCP 도구로 노출하므로, 컴포넌트 자체에는 MCP 전용 코드가 필요 없습니다.
권한 모델은 브라우저처럼 기본 거부(deny-by-default)입니다. 컴포넌트가 네트워크나 파일 시스템에 접근하려 하면 Wassette가 사용자에게 허용 여부를 묻습니다. 소개 글은 VS Code의 GitHub Copilot에 다음과 같이 요청해 컴포넌트를 불러오는 예를 보여 줍니다.
Please load the time component from oci://ghcr.io/yoshuawuyts/time:latest
그림 22 Wassette가 컴포넌트에 네트워크 권한을 부여하기 전에 사용자 확인을 요청하는 화면(출처: Introducing Wassette: WebAssembly-based tools for AI agents)
Wassette는 GitHub Copilot, Claude Code, Cursor, Gemini CLI, Codex CLI와 연동하는 방법을 문서로 제공합니다. 저장소에는 아직 초기 개발 단계이며 프로덕션 용도가 아니라는 안내가 붙어 있습니다. 2020년 글에서 nanoprocess 모델로 설명한 '모듈별로 필요한 권한만 부여한다'라는 생각이, npm 패키지가 아니라 에이전트 도구에 먼저 적용되고 있습니다.
마치며
2020년 글은 Sass 이야기로 끝났습니다. Ruby로 시작해 C++ LibSass로 옮겨 간 Sass가 Node Sass라는 래퍼 때문에 릴리스마다 100개 넘는 빌드를 만들어야 했고, wasm의 이식성이 이 문제를 단순하게 만들 수 있을 것이라는 기대였습니다. 결말은 예상과 달랐습니다. Sass 팀은 2020년 10월 LibSass와 Node Sass를 비권장으로 지정하고 Dart Sass를 권장했으며, 2024년 7월에는 Node Sass의 지원 종료를 선언하고 npm 패키지를 비권장으로, GitHub 저장소를 아카이브로 바꿨습니다. 지금 npm의 sass 패키지는 Dart 코드를 컴파일한 순수 JavaScript이고, 속도가 필요한 쪽을 위한 sass-embedded는 네이티브 Dart 실행 파일을 호출하는 래퍼입니다. 어느 쪽도 wasm이 아닙니다. 2026년 10월 첫 주 기준으로 sass는 주간 약 3,797만 회, sass-embedded는 약 795만 회 다운로드되었고, 지원이 끝난 node-sass도 여전히 약 107만 회 다운로드됩니다.
그렇다고 2020년의 기대가 틀린 것은 아니었습니다. 플랫폼별 네이티브 빌드 문제는 Rust로 작성한 JavaScript 도구들이 NAPI-RS의 wasm 폴백으로 풀고 있습니다. Sass가 택한 길과 Rolldown이 택한 길은 다르지만, '하나의 바이너리로 어디서든 실행한다'라는 목표는 같습니다.
6년 동안 wasm에서 가장 크게 바뀐 점은 'C/C++와 Rust를 위한 컴파일 타깃'에서 'GC 언어를 포함한 여러 언어의 공통 실행 형식'으로 범위가 넓어졌다는 것입니다. Google Sheets, Flutter, Kotlin의 사례가 이를 보여 줍니다. 반면 Web Almanac의 수치가 보여 주듯 웹 전체에서 wasm을 쓰는 사이트는 1%가 안 되고, wasm이 JavaScript를 대신하는 일은 일어나지 않았습니다. 2020년 글에서 "wasm은 JavaScript를 대신하거나 JavaScript 내에서 실행되지 않고, JavaScript와 같이 JavaScript 엔진 내에서 나란히 실행된다"라고 썼는데, 이 설명은 지금도 유효합니다.
앞으로 지켜볼 지점은 두 가지입니다. 하나는 브라우저가 Component Model을 받아들여 wasm이 JavaScript를 거치지 않고 웹 API를 직접 부를 수 있게 되는지입니다. 다른 하나는 AI 에이전트가 생성하고 실행하는 코드가 늘어나는 상황에서, wasm의 샌드박스와 권한 모델이 그 실행 환경의 기본값이 될 수 있는지입니다. 두 가지 모두 2020년 글의 nanoprocess 모델이 그렸던 '기본적으로 안전하고 조합 가능한' 소프트웨어의 연장선에 있습니다.





















