Skip to content
Published on

WASI 0.3의 네이티브 async — wasi:io가 사라진 이유, 그리고 아직 안 끝난 일

공유하기
Authors

들어가며 — 6월 11일에 실제로 일어난 일

2026년 6월 11일, WASI 서브그룹이 투표로 WASI 0.3.0을 비준했습니다. 헤드라인은 "WebAssembly 컴포넌트에 async가 네이티브로 들어왔다"입니다.

이 문장은 하이프처럼 들리기 쉽고, 실제로 WASM 진영은 지난 몇 년간 "곧 도착한다"를 여러 번 반복했습니다. 그래서 이 글은 두 가지를 분리해서 보려고 합니다 — 실제로 스펙과 런타임에 들어간 것, 그리고 아직 안 끝났는데 끝난 것처럼 읽히기 쉬운 것.

먼저 결론부터. 이번 릴리스는 진짜입니다. 다만 "진짜"의 의미가 대부분의 사람이 기대하는 것과 다릅니다. 이건 성능 릴리스가 아니라 합성 가능성(composability) 릴리스입니다. 그리고 성능만 놓고 보면, 지금 이 순간에는 오히려 후퇴한 구간이 있습니다. 그 이야기까지 아래에서 하겠습니다.

WASI 0.2의 진짜 문제 — 샌드위치 문제

WASI 0.2에서도 async는 됐습니다. wasi:io 패키지가 pollable 리소스와 input-stream / output-stream, 그리고 poll 함수를 제공했고, 이걸로 논블로킹 I/O를 표현할 수 있었습니다.

문제는 컴포넌트를 조합할 때 터졌습니다. wasi.dev의 0.3 개요 문서가 드는 예가 명확합니다. 컴포넌트 A가 B를 부르고, B가 호스트를 부르는 상황을 생각해 봅시다.

A  →  B  →  Host

여기서 pollable단일 컴포넌트 인스턴스에 묶인 리소스입니다. 그래서 B는 호스트에게서 받은 pollable의 웨이크업을 A에게 그대로 전달할 방법이 없습니다. B가 할 수 있는 건 준비 신호를 중계하려고 자기가 계속 폴링하는 것뿐이고, 문서의 표현으로는 실무에서 그 웨이크업 사슬이 그냥 끊어집니다. 이걸 샌드위치 문제(sandwich problem)라고 부릅니다 — WASI 0.2는 async를 표현할 수는 있었지만, 컴포넌트 경계를 넘어 합성할 수는 없었습니다.

Bytecode Alliance의 발표 글은 이걸 더 직설적으로 정리합니다. WASI 0.2에서는 컴포넌트마다 자기 이벤트 루프 혹은 async 런타임이 필요했고, 그래서 개별 컴포넌트를 호스트 위에서 돌릴 수는 있었지만 그 이벤트 루프들끼리 서로 조율할 방법이 없었다는 것입니다. 결과적으로 컴포넌트가 스트리밍이나 async API를 쓰는 순간, 그 컴포넌트는 다른 컴포넌트와 조합될 수 없었습니다.

이게 왜 아픈지는 컴포넌트 모델의 존재 이유를 생각하면 분명합니다. 컴포넌트 모델의 세일즈 포인트가 "언어를 가리지 않고 조립할 수 있는 바이너리"인데, 실무 코드는 거의 다 I/O를 하고, I/O를 하면 async를 쓰고, async를 쓰면 조립이 안 됐습니다. 즉 가장 중요한 기능이 가장 흔한 경우에 작동하지 않았습니다.

WASI 0.3의 해법은 async를 컴포넌트 모델의 캐노니컬 ABI로 내리는 것입니다. 이제 이벤트 루프는 호스트가 하나만 관리하고, 모든 컴포넌트가 그걸 공유합니다. 스케줄링과 웨이크업 전파를 런타임이 소유하니까, 생산자와 소비자 사이에 컴포넌트가 몇 개 끼어 있든 async가 제대로 동작합니다.

세 가지 새 원시 타입

캐노니컬 ABI에 일급으로 추가된 건 세 가지입니다 — async func, 그리고 스트림과 퓨처 타입입니다.

// 함수 자체를 async로 선언한다
handle: async func(request: request) -> result<response, error-code>;

// stream<T>: 타입이 있는 비동기 데이터 채널
// future<T>: 단일 값의 비동기 완료
read-via-stream: func() -> tuple<stream<u8>, future<result<_, error-code>>>;

몇 가지 설계 결정이 눈여겨볼 만합니다.

소유권. 스트림과 퓨처는 리소스 타입처럼 동작합니다 — 각각 소유된 핸들이고, 컴포넌트 경계를 넘겨주면 소유권이 호출자에서 피호출자로 넘어갑니다. 다만 리소스와 달리 빌려줄 수 없습니다(borrow 불가). 0.2의 input-stream이 인스턴스에 묶인 리소스여서 못 넘겼던 것과 정반대로, 이제 스트림은 넘길 수 있는 입니다. 샌드위치 문제가 풀리는 지점이 정확히 여기입니다.

완료 기반(completion-based) 모델. 이건 준비 기반(readiness-based)이 아닙니다. 발표 글은 이걸 리눅스의 io_uring, 윈도우의 IOCP/IoRing에 비유합니다. epoll이나 kqueue 스타일의 준비 기반 API가 필요하면 이 위에 에뮬레이션할 수 있다고 명시합니다. 이 선택은 취향 문제가 아니라 합성과 직결됩니다 — 준비 신호는 중계해야 하지만, 완료는 런타임이 직접 깨우면 그만이니까요.

스트림 상태 문제 해결. 0.2에서는 스트림의 종료 에러가 매 read 호출에 인라인으로 얹혀 나왔습니다. 그래서 호출자가 끝까지 읽어야만 결과를 알 수 있었고, 중간에 읽기를 멈추면 정상 종료와 에러를 구분할 수 없었습니다. 0.3은 스트림에 퓨처를 하나 더 딸려 보내서, 스트림을 얼마나 소비했는지와 무관하게 결과가 따로 확정되게 했습니다. 위 코드에서 스트림과 퓨처를 튜플로 함께 돌려주는 모양이 바로 그 답입니다.

wasi:io는 어디로 갔나

wasi:io 패키지는 통째로 삭제됐습니다. WASI 0.3 버전의 wasi:io는 존재하지 않습니다. 하던 일은 전부 컴포넌트 모델의 네이티브 원시 타입으로 흡수됐습니다.

릴리스 노트가 제시하는 매핑표가 이 릴리스의 절반을 요약합니다.

WASI 0.2 (wasi:io)              →  WASI 0.3 (Component Model)
--------------------------------------------------------------
resource pollable               →  future<T>
resource input-stream           →  stream<u8>
resource output-stream          →  stream<u8>  (쓰기 방향)
poll(list<pollable>)            →  future를 await (런타임이 처리)
subscribe() on resource         →  호출이 future<...>를 반환
start-foo / finish-foo          →  foo: async func(...)

마지막 줄이 실무에서 체감이 가장 큽니다. 0.2의 start-bind / finish-bind, start-connect / finish-connect 같은 2단계 춤과 그걸 굴리던 subscribe()가 전부 사라지고 async func 하나로 접힙니다.

인터페이스별로 실제로 바뀐 것

릴리스 노트는 대부분의 변경이 기계적이라고 말합니다. 0.2가 async를 흉내 내려고 부렸던 곡예가 필요 없어졌으니, 같은 걸 훨씬 간결하게 쓰게 됐다는 것입니다. 그래도 몇 군데는 기계적이지 않습니다.

wasi:http — 변경 폭이 가장 큽니다. 여기만은 폴링 인터페이스를 async로 기계 변환한 게 아니라 월드와 핵심 추상을 재조직했습니다. incoming-request, outgoing-request, incoming-response, outgoing-response, incoming-body, outgoing-body, future-trailers, future-incoming-response, response-outparam — 이 리소스들이 전부 사라지고, 바디가 스트림이고 트레일러가 퓨처인 통합된 request / response 리소스로 정리됐습니다. 그리고 월드가 둘로 나뉩니다.

// 서비스 월드: HTTP 호출을 하고, 들어오는 요청을 처리한다
world service {
  import client;
  export handler;
}

// 미들웨어 월드: service의 상위 집합
world middleware {
  include service;  // service가 하는 건 다 하고
  import handler;   // 들어온 요청을 다른 handler로 넘길 수도 있다
}

0.2 시절의 proxy 월드는 service로 대체됐고, middleware 월드는 요청 경로 미들웨어를 일급으로 표현합니다.

wasi:sockets — 인터페이스가 7개에서 2개로. network, instance-network, tcp, tcp-create-socket, udp, udp-create-sockettcp-socketudp-socket 리소스를 담은 통합 types 인터페이스로 합쳐지고, DNS는 별도 ip-name-lookup이 맡습니다. 그리고 network 리소스가 사라졌습니다 — 0.2는 네트워크 접근을 모든 bind/connect/lookup 호출에 실어 나르는 capability 리소스로 모델링했는데, 0.3은 이걸 없애고 월드 임포트 수준에서 권한을 줍니다.

wasi:clocks — 대부분 이름 바꾸기. wall-clocksystem-clock으로, datetimeinstant로 바뀌었습니다. 이유는 일관성입니다 — "wall clock"과 "datetime"은 POSIX에도, 러스트의 std::time에도 없는 WASI만의 용어였습니다. instant 레코드의 초는 u64에서 s64로 바뀌어 에포크 이전 타임스탬프를 표현할 수 있게 됐습니다. 다운스트림 영향은 대체로 찾아 바꾸기 수준이지만, 릴리스 노트가 굳이 이걸 플래그한 이유는 이 churn이 테스트 스위트 전반에 나타나기 때문입니다.

wasi:random — 이름 뒤에 의미 변화가 숨어 있습니다. get-random-bytes의 파라미터가 len에서 max-len으로 바뀌었는데, 이건 단순 리네임이 아닙니다. 구현이 요청한 것보다 적은 바이트를 반환해도 됩니다. 호출자가 원하는 만큼 모을 때까지 루프를 돌아야 한다는 뜻입니다. 기계적 변환이라고 믿고 넘어가면 물리기 좋은 지점입니다.

런타임은 준비됐나 — Wasmtime 46이 실제로 한 일

여기가 "약속"과 "출하"를 구분해야 하는 대목입니다.

발표 글(6월 11일)은 Wasmtime 45가 당시 최신 릴리스 후보를 돌리고 있고, Wasmtime 46이 컴포넌트 모델 async를 기본값으로 켜서 WASI 0.3.0을 출하할 것이라고 예고했습니다. 미래형입니다.

그래서 실제로 확인해 보면 — Wasmtime 46.0.0이 2026년 6월 22일에 릴리스됐고, 그 릴리스 노트에 이 줄이 있습니다: Wasmtime이 이제 WASI 0.3.0을 기본으로 지원하며 component-model-async wasm 기능이 기본 활성화됐다(#13612). 예고가 11일 만에 실제로 도착했습니다. 런타임 쪽은 진짜입니다.

한 가지 문서 불일치는 짚어 둘 만합니다. wasi.dev 로드맵은 "WASI 0.3 지원은 Wasmtime 43+에서 가능하다"고 적고 있는데, 발표 글은 45/46을 말합니다. 개요 문서를 보면 이 차이가 설명됩니다 — Wasmtime v44부터 wasmtime serve-Sp3 -W component-model-async=y 플래그를 주면 0.3 컴포넌트를 서빙할 수 있었고(그리고 0.3 service 월드를 익스포트하지 않는 컴포넌트는 자동으로 0.2의 wasi:http/proxy 월드로 폴백), v44는 wasi:tls@0.3.0-draft 초기 지원도 넣었습니다. 즉 플래그 뒤의 지원은 더 일찍 들어왔고, 기본값으로 켜진 건 46입니다. 문서마다 다른 숫자를 말하는 건 이 둘을 구분하지 않아서입니다.

적합성은 공용 wasi-testsuite로 검증하고, WASI 0.3 커버리지가 Wasmtime과 jco에서 리눅스·macOS·윈도우로 돌고 있습니다.

정직하게 — 아직 안 끝난 것들

여기서부터가 이 글을 쓴 이유입니다. 발표 글만 읽으면 다 끝난 것처럼 보이는데, Bytecode Alliance 자신이 낸 다른 글을 같이 읽으면 그림이 상당히 달라집니다.

동기 호출이 지금 약 3.5배 느립니다. The Road to Component Model 1.0에 이런 대목이 있습니다. 현재 구현은 컴포넌트 경계를 넘는 async 태스크 인프라를 호스트 콜로 관리하는데, 이게 순수하게 동기적인 호출 경로에도 대략 3.5배의 오버헤드를 얹습니다(닉 피츠제럴드가 Plumbers Summit에서 측정한 수치로 인용돼 있습니다). 스펙 수준에서 일부는 이미 처리됐고(P3 개발 중 recursion check가 제거됐습니다), 나머지는 WASI P3 출하 이후로 계획돼 있습니다 — 동기 어댑터가 스택에 가벼운 태스크만 할당하도록 태스크 상태를 리팩터링해서 Cranelift가 대부분 최적화로 날려 버릴 수 있게 하는 방향이고, 목표는 async 지원이 들어오기 전의 성능 프로파일을 되찾는 것입니다.

이 문장을 정확히 읽는 게 중요합니다. 목표가 "더 빨라지기"가 아니라 "예전만큼 돌아오기"입니다. 즉 async를 네이티브로 넣은 대가를 동기 경로가 지금 치르고 있고, 그 청구서는 아직 안 갚았습니다.

게스트 툴체인이 아직 따라오는 중입니다. 스펙이 비준됐다는 것과 당신 언어로 그걸 쓸 수 있다는 건 다른 얘기입니다. 발표 글은 파이썬, 자바스크립트, C#, C를 포함한 여러 언어에서 게스트 바인딩 생성기의 async 지원이 진행 중이라고 적습니다. jco는 WASI 0.3 전체를 지원하지만 그게 기본 활성화된 릴리스는 "곧" 나갈 예정이고, 러스트 쪽은 wit-bindgen으로 async fn이 자연스럽게 매핑됩니다. Go는 결이 다른데, componentize-go가 고루틴을 ABI 경계에서 파킹했다가 스트림이 준비되면 재개하는 식으로 스택풀 코루틴을 태웁니다 — 컴포넌트 모델의 async ABI가 스택리스와 스택풀 코루틴을 둘 다 수용하도록 설계됐기 때문에 가능한 일입니다. 발표 글의 표현대로 "앞으로 몇 주에서 몇 달에 걸쳐" 개별 프로젝트가 0.3 지원을 알릴 것입니다. 그 말은 오늘 기준으로는 대부분 아직이라는 뜻입니다.

스레드와 스트림 스플라이싱은 0.3.0에 못 들어갔습니다. 둘 다 0.3.x 후속 릴리스로 밀렸습니다. 협조적 스레드는 컴포넌트 모델 수준에 구현되고(WASI 아래층입니다), 러스트 컴포넌트가 std::thread::spawn을 쓰면 WIT에 아무것도 안 해도 지원을 받습니다. 진행 상황은 wasi-libc의 pthreads 지원이 거의 끝났고, LLVM 패치가 막 랜딩됐고, Wasmtime은 피처 플래그 뒤에 동작하는 구현이 있습니다. 선협조적, 후선점형 순서입니다. 스트림 스플라이싱(중간 복사 없이 스트림을 다른 스트림에 이어 붙이기)은 스트리밍 성능에 중요하지만, 문서의 표현으로는 그냥 마감에 너무 가까워서 P3 자체에는 못 실었습니다.

LLVM이 가장 긴 리드 타임일 수 있습니다. 1.0으로 가는 ABI 작업에는 C ABI 수준의 multivalue 반환이 묶여 있는데, LLVM이 아직 C ABI 레벨에서 multivalue를 지원하지 않습니다. 정확한 ABI 제안은 나와 있지만, 그걸 업스트림에 올리는 게 이 작업 중 가장 오래 걸릴 수 있다고 글이 직접 인정합니다. 참고로 LLVM은 6개월마다 릴리스하고, 러스트에 뭔가를 넣어 stable까지 가는 데 대략 9주가 걸립니다. 이 파이프라인 전체를 Bytecode Alliance가 통제하지 못합니다.

0.2에서 마이그레이션할 문서가 아직 없습니다. 실제 예제가 붙은 상세한 0.2 → 0.3 마이그레이션 가이드는 컴포넌트 모델 문서에 "준비 중"입니다.

"6자릿수 빨라진다"는 주장에 대하여

발표 글에 이런 문장이 있습니다. middleware 월드가 가능하게 하는 서비스 체이닝 덕분에, 서로 자주 통신하는 마이크로서비스 컴포넌트들이 네트워크를 타지 않고 같은 프로세스 안에서 직접 합성될 수 있고, 그래서 "대부분의 마이크로서비스에서 다른 마이크로서비스 호출 시간이 밀리초에서 나노초로, 6자릿수(six orders of magnitude) 줄어들 것"이라는 주장입니다.

이 문장은 이렇게 읽어야 합니다.

  • 이건 측정치가 아니라 전망입니다. 원문이 미래형("will reduce")이고, 벤치마크도 측정 조건도 제시되지 않습니다.
  • 비교 대상이 불공정합니다. "네트워크 홉"과 "같은 프로세스 안의 함수 호출"을 비교하면 당연히 자릿수가 갈립니다. 이건 WASI 0.3이 빨라서가 아니라 네트워크를 안 타서 나오는 숫자입니다. 같은 논리라면 두 마이크로서비스를 하나의 바이너리로 합치는 것도 6자릿수를 줍니다.
  • 그리고 바로 앞 절에서 봤듯, 같은 조직의 다른 글이 지금 동기 컴포넌트 간 호출에 3.5배 오버헤드가 있다고 적고 있습니다. 두 문장은 모순은 아닙니다(하나는 네트워크 제거 이득, 하나는 호출 규약 오버헤드) — 하지만 나란히 놓고 봐야 균형이 맞습니다.

브라우저 쪽 숫자도 같은 기준으로 읽는 게 좋습니다. 모질라의 라이언 헌트가 한 실험에서, DOM 변경이 많은 WASM VDOM 재조정 루프가 자바스크립트 글루 레이어를 우회해 브라우저 API를 직접 호출하면 2배에 가까운 속도 향상을 얻을 수 있었다고 합니다. 글 자체가 "실제 이득은 워크로드마다 다를 것"이라고 단서를 답니다. 특정 워크로드의 상한선이지, 일반적인 기대치가 아닙니다.

WASM 진영이 오래 시달려 온 게 정확히 이 지점입니다. 기술은 실제로 좋은데 주장이 늘 반 박자 앞서갑니다. 이번 릴리스의 진짜 성과는 나노초가 아니라 async를 쓰면서도 컴포넌트를 조합할 수 있게 된 것이고, 그것만으로 충분히 큰 뉴스입니다.

Component Model 1.0과 WASI 1.0 — 날짜는 없다

자주 헷갈리는 부분이라 정리하면, Component Model 1.0과 WASI 1.0은 별개의 마일스톤입니다. 그리고 순서가 있습니다 — WASI 1.0은 컴포넌트 모델이 먼저 1.0에 도달해야 그로부터 따라옵니다.

루크 와그너가 Plumbers Summit에서 쓴 비유가 이 관계를 잘 설명합니다. 컴포넌트 모델은 항상 존재하는 마이크로커널입니다 — 어떤 호스트에서든 돌아가는 기초 원시 타입을 제공합니다. WASI는 그 위에 얹히는 OS 서비스(네트워킹, 스토리지, 그래픽)이고, 마이크로커널 위의 프로세스처럼 돌며 기기에 따라 있을 수도 없을 수도 있습니다. 브라우저를 생각하면 이해가 쉽습니다 — 브라우저는 어떤 I/O API가 존재해야 하는지에 대해 아주 강한 의견이 있고, 그래서 WASI 인터페이스는 브라우저에서 폴리필로 돕니다. 반면 컴포넌트 모델 자체는 계산 원시 타입만 제공하므로 코어 WASM 옆에 네이티브로 구현될 수 있습니다.

그런데 여기에 만만치 않은 관문이 있습니다. 컴포넌트 모델은 최소 두 개의 브라우저 엔진에 네이티브로 구현되지 않으면 공식적으로 1.0에 도달할 수 없습니다. 현재 상태는 이렇습니다 — 모질라 팀이 최근 WebAssembly CG 대면 회의에서 성능 결과를 발표했고, 크롬 V8 팀은 컴포넌트 모델 구현 평가를 위한 이슈를 열었습니다. 글 자체의 표현이 정확합니다: 이건 약속이 아니라 의미 있는 신호입니다.

그래서 1.0을 얻어내려는 전략이 흥미롭습니다. asm.js 시절의 플레이북을 그대로 씁니다 — asm.js는 브라우저 엔진이 기능 사용 텔레메트리로 감지할 수 있는 no-op 문자열 "use asm"을 넣어 두고, 그 데이터로 최적화의 근거를 만들었고 결국 WebAssembly 자체로 이어졌습니다. jco가 지금 같은 걸 합니다. jco가 생성하는 자바스크립트 글루가 "use components" 식별자를 뱉습니다(jco 1.16.8에서 "use jco"에서 이름이 바뀌었습니다). jco로 트랜스파일된 컴포넌트의 실사용이 쌓이면, 그게 네이티브 구현의 근거가 된다는 계산입니다.

정리하면, WASI 1.0의 날짜를 말하는 1차 자료는 없습니다. 로드맵 페이지에도 없고, 1.0으로 가는 길을 다룬 글에도 없습니다. 로드맵 문서 자신이 첫 줄에 못을 박습니다 — 이건 살아 있는 문서이고, 목표와 전망은 잠정적이며 수정될 수 있다고. (인터넷에 도는 "2026년 말/2027년 초 목표" 같은 숫자를 몇 군데서 봤는데, 1차 자료에서 확인되지 않아 여기 옮기지 않습니다.)

확실한 건 케이던스뿐입니다. WASI 포인트 릴리스는 2개월마다, 해당 월의 첫 번째 목요일에, 릴리스 트레인 모델로 나갑니다 — "기차에 탈 준비가 된" 것이 무엇이든 상관없이 정해진 주기로 나간다는 뜻입니다. 0.3.0 이후로는 하위 호환되는 0.3.x가 이 기차를 탑니다. 예정된 화물은 언어 관용구에 통합된 취소(cancellation), 스트림 특수화와 최적화, 제로 카피를 넓히는 호출자 제공 버퍼, 그리고 스레드입니다.

그래서, 지금 옮겨야 하나

가장 중요한 사실부터. 마이그레이션은 필수가 아닙니다. 0.3 지원 런타임을 쓰기 위해 0.3으로 옮길 필요가 없습니다. wasmtime serve는 같은 바이너리로 0.3 컴포넌트와 0.2 컴포넌트를 컴포넌트별로 디스패치해서 둘 다 돌립니다. 로드맵도 구현체가 0.2와 0.3을 나란히 지원하거나, 0.2를 0.3 위에서 폴리필(가상화)할 수 있다고 명시합니다. P1 모듈도 아직 돌고, P2 컴포넌트도 아직 돕니다 — 이 팀은 P1 이래로 시맨틱 버저닝, 나란히 두는 구현, WASM-to-WASM 어댑터로 안정성을 유지해 왔고 그 이야기는 1.0 이후로도 계속됩니다.

지금 옮겨서 값을 하는 경우

  • 컴포넌트를 실제로 합성하고 있고, 그 사슬에 async나 스트리밍이 끼어 있다. 샌드위치 문제로 이미 아파 봤다면 이게 바로 그 약입니다.
  • HTTP 미들웨어를 컴포넌트로 표현하고 싶다 — 0.2의 proxy 월드로는 표현이 안 되던 게 middleware 월드로 일급이 됐습니다.
  • 스트림을 중간에 끊는 소비자가 있고, 정상 종료와 에러를 구분해야 한다.
  • 런타임을 Wasmtime 46+로 고정할 수 있고, 게스트 언어가 러스트다.

아직 기다리는 게 나은 경우

  • 동기 호출 경로가 핫패스다. 3.5배 오버헤드 수정이 아직 계획 단계입니다.
  • 게스트 언어가 파이썬·C#·C처럼 바인딩 async 지원이 진행 중인 쪽이다.
  • 스레드나 스트림 스플라이싱이 필요하다 — 0.3.x를 기다려야 합니다.
  • 단일 컴포넌트를 호스트 위에서만 돌린다. 합성을 안 하면 0.2로 충분하고, 0.3이 푸는 문제가 당신 문제가 아닙니다.

옮기기로 했다면 실무 함정이 하나 있습니다. 툴체인 버전을 일관되게 고정하세요. 컴포넌트 모델이 캐노니컬 인터페이스 이름을 정의해서 호환 버전 간 링크가 가능하긴 한데, 모든 도구가 아직 이 버전 인식 링킹을 지원하지 않습니다. 그때까지는 Wasmtime과 바인딩 생성기(러스트면 wit-bindgen, 자바스크립트면 jco)가 같은 WIT 버전 0.3.0을 타깃해야 합니다. 어긋나면 인스턴스화 시점에 알아보기 힘든 wrong type 에러로 튀어나옵니다. 이런 종류의 에러는 원인을 찾는 데 반나절이 사라지는 유형이라, 미리 알고 있는 값이 큽니다.

나머지 마이그레이션 절차는 대체로 기계적입니다 — wasi:io 타입을 위 매핑표대로 바꾸고, 월드를 적절히 고르고(CLI면 wasi:cli/command, HTTP 서버면 wasi:http/service, 미들웨어면 wasi:http/middleware), start-foo / finish-foo 호출 지점을 async func로 바꾸면 됩니다. 앞서 말한 wasi:randommax-len 같은 의미 변화만 기계적 변환에서 빠뜨리지 않으면 됩니다.

마치며

정리하면 이렇습니다. WASI 0.3.0은 2026년 6월 11일에 비준됐고, async를 wasi:io 패키지에서 컴포넌트 모델의 캐노니컬 ABI로 내렸습니다. wasi:io는 삭제됐고, pollable / input-stream / output-stream / pollasync func와 스트림·퓨처 원시 타입으로 대체됐습니다. Wasmtime 46이 6월 22일에 이걸 기본값으로 켰습니다.

이 릴리스의 값은 속도가 아니라 합성입니다. 0.2에서는 컴포넌트가 async를 쓰는 순간 조합 불가능해졌고 — 컴포넌트 모델의 존재 이유를 정면으로 갉아먹는 결함이었습니다 — 0.3은 이벤트 루프 소유권을 호스트로 옮겨 그걸 고쳤습니다. 그거면 충분히 큰 뉴스입니다. 나노초 마케팅은 없어도 됐습니다.

동시에 정직하게: 동기 호출 경로는 지금 약 3.5배 오버헤드를 지고 있고 그 수정은 아직 계획이며, 게스트 툴체인은 언어마다 진행 중이고, 스레드와 스플라이싱은 다음 기차이고, 컴포넌트 모델 1.0은 브라우저 엔진 두 곳의 네이티브 구현을 기다리고 있고, 아무도 WASI 1.0 날짜를 약속하지 않았습니다.

그래서 실무 판단은 단순합니다. 컴포넌트를 조합하고 있고 그 사슬에 async가 있다면 지금 보세요. 그게 아니라면 0.2는 계속 돌아가고, 폴리필도 있고, 다음 기차는 2개월 뒤에 옵니다. 이 프로젝트에서 가장 신뢰할 만한 건 로드맵의 날짜가 아니라 릴리스 트레인의 케이던스입니다 — 그리고 이번엔 예고한 것이 11일 만에 실제로 도착했다는 사실입니다.

참고 자료