WASI 0.2에서 WASI 0.3으로 마이그레이션하기
WASI 0.3은 네이티브 비동기 프리미티브인 async func, stream<T>, future<T>를 중심으로 WASI의 인터페이스를 재구성합니다. wasi:cli, wasi:http, wasi:filesystem, wasi:sockets의 변경 사항 대부분은 이러한 프리미티브로 전환한 데 따른 결과입니다.
이 페이지에서는 WASI 0.2와 WASI 0.3의 개념 간 대응 관계를 다룹니다. 모든 WASI 0.3 인터페이스를 WIT 수준에서 비교한 내용은 WASI.dev의 WASI 0.3을 참조하십시오.
마이그레이션해야 합니까?
당장은 아닙니다 – WASI 0.2는 이전과 마찬가지로 호스트에서 사용할 수 있으며, WASI 0.3은 순전히 기능을 추가하는 변경입니다.
이와 별개로, WASI 0.3 런타임은 호스트 경계에서 0.2 임포트를 네이티브 0.3 프리미티브에 매핑하여 0.2를 폴리필할 수 있으며, Wasmtime의 wasmtime serve는 이미 동일한 바이너리에서 0.3 컴포넌트와 0.2 컴포넌트를 모두 실행하면서 컴포넌트별로 디스패치합니다. 다음이 필요한 경우에는 마이그레이션하는 것이 적절합니다.
- 컴포넌트 경계를 넘어 합성할 수 있는 비동기(샌드위치 문제가 사라집니다).
- 더 새로운 인터페이스 형태 — 특히
wasi:http에서 아홉 개의 리소스가 두 개로 축소된 점입니다. - 계속 출시되고 있는 0.3 대상 툴체인의 일급 지원.
개념 대응
WASI 0.3은 모든 wasi:io 리소스를 정규 ABI 프리미티브로 대체합니다. 대응 관계는 대부분 일대일입니다.
WASI 0.2 (wasi:io) | WASI 0.3 (컴포넌트 모델) |
|---|---|
resource pollable | future<T> |
resource input-stream | stream<u8> |
resource output-stream | stream<u8> (호출 안으로 전달됨) |
poll(list<pollable>) | 퓨처에 대한 await |
리소스에 대한 subscribe() | 호출에서 future 반환 |
start-foo / finish-foo | 단일 func 또는 async func |
WIT에서 변경된 사항
읽기를 위한 스트림과 퓨처의 조합
WASI 0.2의 읽기 호출은 단일 input-stream 리소스를 반환했으며, 종료 오류는 이를 소비하는 과정에서만 드러났습니다. WASI 0.3은 이러한 관심사를 분리합니다. 호출은 데이터를 위한 stream<u8>와 결과를 위한 future<result<_, error-code>>를 튜플로 묶어 반환합니다.
// WASI 0.2 (filesystem read)
read-via-stream: func(offset: filesize) -> result<input-stream, error-code>;
// WASI 0.3 (filesystem read)
read-via-stream: func(offset: filesize) -> tuple<stream<u8>, future<result<_, error-code>>>;
WASI 0.3에서는 호출자가 읽기가 정상적으로 완료되었는지 알기 위해 스트림을 끝까지 소비할 필요가 없습니다. 퓨처는 어느 경우든 확정됩니다.
쓰기 방향의 전환
WASI 0.2의 쓰기 경로는 게스트에게 호스트가 소유한 리소스(output-stream)를 넘겨주고 게스트가 그 안으로 바이트를 밀어 넣도록 했습니다. WASI 0.3은 이를 뒤집습니다. 게스트가 데이터를 stream<u8> 값으로 제공하고, 호스트는 스트림 소비를 마치면 확정되는 future를 반환합니다.
// WASI 0.2: receive an output-stream resource, write into it
get-stdout: func() -> output-stream;
// WASI 0.3: pass a stream value in, receive a completion future
write-via-stream: func(data: stream<u8>) -> future<result<_, error-code>>;
2단계 호출의 통합
WASI 0.2는 일시 중단될 수 있는 연산을 start-foo / finish-foo 쌍으로 모델링했으며, 그 사이에 준비 상태를 확인하기 위한 pollable을 두었습니다. WASI 0.3은 각 쌍을 단일 호출로 통합합니다:
// WASI 0.2
start-connect: func(network: borrow<network>, remote-address: ip-socket-address) -> result<_, error-code>;
finish-connect: func() -> result<tuple<input-stream, output-stream>, error-code>;
// WASI 0.3
connect: async func(remote-address: ip-socket-address) -> result<_, error-code>;
연산이 호스트에서 일시 중단되어야 하는 경우(예: connect) 통합된 호출은 async func입니다. 기존에 논블로킹 디스패치만을 위해 2단계 형태를 사용했던 연산은 대신 일반 func로 통합될 수 있습니다(bind, listen).
인터페이스 주요 변경 사항
인터페이스별 전체 변경 사항은 WASI.dev의 WASI 0.3에서 확인할 수 있습니다. 마이그레이션 작업을 유발할 가능성이 가장 큰 세 가지 변경 사항은 다음과 같습니다:
wasi:io가 제거되었습니다. 이 패키지에는 0.3.0 릴리스가 없습니다. 이 패키지가 노출하던 모든 리소스(pollable,input-stream,output-stream)는 위의 개념 매핑에 따라 컴포넌트 모델 기본 요소로 대체됩니다.wasi:http의 리소스가 9개에서 2개로 줄어듭니다. incoming/outgoing × request/response/body 조합과future-trailers,future-incoming-response,response-outparam은 모두request와response가 되며, 본문은stream<u8>로, 트레일러는future로 표현됩니다. 이제 핸들러는async func입니다:
// WASI 0.2
handle: func(request: incoming-request, response-out: response-outparam);
// WASI 0.3
handle: async func(request: request) -> result<response, error-code>;
proxy 월드는 service로 대체되며, 새로운 middleware 월드는 핸들러를 임포트하는 동시에 익스포트합니다.
wasi:sockets에서network리소스가 제거됩니다. 네트워크 접근 권한은 모든bind,connect, DNS 조회에 일일이 전달되는 대신 월드 수준에서 부여됩니다. WASI 0.2의 일곱 가지 소켓 인터페이스는 하나의types인터페이스와ip-name-lookup으로 통합되며, TCPlisten은 별도의accept루프를 요구하는 대신stream<tcp-socket>을 직접 반환합니다.
인터페이스별 소규모 변경 사항, 즉 async func로 바뀌는 파일 시스템 메서드, wasi:clocks의 이름 변경(wall-clock → system-clock, datetime → instant), wasi:random의 max-len 이름 변경, 새로운 공유 wasi:cli/types 인터페이스는 위에 링크된 WASI.dev 페이지에 문서화되어 있습니다.
도구 요구 사항
| 도구 | 최소 버전 | 비고 |
|---|---|---|
| Wasmtime | 46+ | WASI 0.3과 component-model-async가 기본적으로 활성화되어 있습니다. |
wit-bindgen | 0.46+ | 0.3 바인딩 생성에는 async 기능을 사용하십시오. |
| jco | 최신 | 0.3 호스트 바인딩은 preview3-shim 패키지로 제공됩니다. |
wkg | 0.15+ | wasi:cli@0.3.0 및 관련 패키지를 가져오는 데 필요합니다. |
| Rust | nightly | 현재 stable 버전에 번들된 wasm-component-ld는 wit-bindgen 0.58의 0.3 출력을 처리하기에는 너무 오래되었습니다. |
더 읽을거리
- 비동기, 스트림, 퓨처 — 개념적 기반
- Rust로 실행 가능한 컴포넌트 만들기 — 0.3
async fn run()패턴을 사용한 Rust 실전 예제 - WASI.dev의 WASI 0.3 — 인터페이스별 WIT 수준 전체 변경 사항