Warning
TinyGo용 컴포넌트 모델 도구는 현재 유지 관리되지 않습니다. 대신 Go 도구를 사용하는 것을 권장합니다.
TinyGo 도구
TinyGo 컴파일러 v0.34.0 이상은 WebAssembly 컴포넌트 모델과 WASI 0.2.0을 네이티브로 지원합니다.
이 가이드에서는 adder/world.wit 패키지에 정의된 adder 월드를 구현하는 컴포넌트를 빌드하는 과정을 단계별로 안내합니다. 이 컴포넌트는 add 함수를 가진 add 인터페이스를 포함하는 adder 월드를 구현합니다.
1. 도구 설치
TinyGo 설치 안내에 따라 TinyGo 컴파일러를 설치하십시오.
또한 wasm-tools 저장소에서 wasm-tools CLI 도구를 설치하십시오.
Warning
wit-bindgen-go에는 자체적으로 벤더링된wasm-tools버전이 포함되어 있지만, tinygo를 사용하려면 여전히 이를 설치해야 합니다. 가능성은 낮지만, 이전 버전의wasm-tools를 사용하는 경우 버전 불일치가 발생할 수 있습니다. 문제가 발생하는 경우 로컬wasm-tools를 최신 상태로 유지하고 있는지 반드시 확인하십시오.Rust 툴체인을 사용하여
wasm-tools를 설치하는 경우 다음과 같이 설치할 수 있습니다:cargo install --locked wasm-tools@1.235.0 --force또는 cargo binstall을 통해 설치할 수 있습니다:cargo binstall wasm-tools@1.235.0
설치를 확인하려면 다음 명령을 실행하십시오:
$ tinygo version
tinygo version 0.34.0 ...
$ wasm-tools -V
wasm-tools 1.255.0 ...
선택 사항: WIT 파일의 임포트를 해석하려면 wkg CLI 도구를 설치하십시오. wkg CLI는 Wasm 컴포넌트 패키지 관리자의 일부입니다. 수동으로 또는 cargo를 사용하여 설치하려면 wasm-pkg-tools 설치 안내를 참조하십시오.
2. Go 프로젝트 생성
이제 Go 프로젝트를 생성하십시오:
mkdir add && cd add
go mod init example.com
다음 tool을 설치하십시오:
go get -tool go.bytecodealliance.org/cmd/wit-bindgen-go
Note
go tool은 Golang 1.24에서 도입되었으며 Go 프로젝트의 도구를 관리하는 데 사용할 수 있습니다.
위 도구를 추가한 후 go mod tidy도 실행하는 것을 고려하십시오.
2. 컴포넌트가 구현할 월드 결정
adder 월드를 구현할 것이므로 WIT를 프로젝트에 복사할 수 있습니다. wit라는 하위 디렉터리를 생성하고 다음 코드를 wit/component.wit라는 파일에 붙여 넣으십시오:
package docs:adder@0.1.0;
interface add {
add: func(x: u32, y: u32) -> u32;
}
world adder {
include wasi:cli/imports@0.2.0;
export add;
}
TinyGo의 wasip2 대상을 사용하고 있으므로 include wasi:cli/imports@0.2.0 줄이 필요합니다. TinyGo는 컴포넌트가 wasi:cli/command@0.2.0 월드(wasi:cli의 일부)를 대상으로 한다고 가정하므로 wasi:cli/imports@0.2.0의 임포트가 필요합니다.
wkg를 사용하여 임포트를 자동으로 해석하고 다운로드하기
wkg 같은 도구는 임포트를 해석하여 완전한 WIT 패키지를 빌드하는 데 편리합니다.
wkg wit build 명령을 실행하면 WIT가 컴포넌트 모델 바이너리 형식으로 인코딩됩니다. 부수 효과로, 이 명령은 임포트를 해석하고 관련된 모든 임포트된 네임스페이스와 패키지로 wit 폴더를 채웁니다.
$ wkg wit build
WIT package written to docs:adder@0.1.0.wasm
3. Wasm 컴포넌트용 바인딩 생성하기
이제 WIT 정의를 하나의 .wasm 파일로 묶었으므로, 빌드 지시문을 추가하여 WebAssembly 컴포넌트용 바인딩을 생성할 수 있습니다:
go tool wit-bindgen-go generate --world adder --out internal ./docs:adder@0.1.0.wasm
Note
go tool지시문(Golang 1.24에서 추가됨)은 Bytecode Alliance의 Golang 도구 모음에 포함된wit-bindgen-go를 설치하고 사용할 수 있게 합니다.
internal 디렉터리에는 해당 WIT 패키지에 대해 생성된 Go 코드가 들어 있습니다.
$ tree internal
internal
├── docs
│ └── adder
│ ├── add
│ │ ├── add.exports.go
│ │ ├── add.wasm.go
│ │ ├── add.wit.go
│ │ └── empty.s
│ └── adder
│ └── adder.wit.go
└── wasi
├── cli
│ ├── environment
│ │ ├── empty.s
│ │ ├── environment.wasm.go
│ │ └── environment.wit.go
│ ├── exit
│ │ ├── empty.s
│ │ ├── exit.wasm.go
│ │ └── exit.wit.go
│ ├── stderr
│ │ ├── empty.s
│ │ ├── stderr.wasm.go
│ │ └── stderr.wit.go
│ ├── stdin
│ │ ├── empty.s
│ │ ├── stdin.wasm.go
│ │ └── stdin.wit.go
│ ├── stdout
│ │ ├── empty.s
│ │ ├── stdout.wasm.go
│ │ └── stdout.wit.go
│ ├── terminal-input
│ │ ├── empty.s
│ │ ├── terminal-input.wasm.go
│ │ └── terminal-input.wit.go
│ ├── terminal-output
│ │ ├── empty.s
│ │ ├── terminal-output.wasm.go
│ │ └── terminal-output.wit.go
│ ├── terminal-stderr
│ │ ├── empty.s
│ │ ├── terminal-stderr.wasm.go
│ │ └── terminal-stderr.wit.go
│ ├── terminal-stdin
│ │ ├── empty.s
│ │ ├── terminal-stdin.wasm.go
│ │ └── terminal-stdin.wit.go
│ └── terminal-stdout
│ ├── empty.s
│ ├── terminal-stdout.wasm.go
│ └── terminal-stdout.wit.go
├── clocks
│ ├── monotonic-clock
│ │ ├── empty.s
│ │ ├── monotonic-clock.wasm.go
│ │ └── monotonic-clock.wit.go
│ └── wall-clock
│ ├── empty.s
│ ├── wall-clock.wasm.go
│ └── wall-clock.wit.go
├── filesystem
│ ├── preopens
│ │ ├── empty.s
│ │ ├── preopens.wasm.go
│ │ └── preopens.wit.go
│ └── types
│ ├── abi.go
│ ├── empty.s
│ ├── types.wasm.go
│ └── types.wit.go
├── io
│ ├── error
│ │ ├── empty.s
│ │ ├── error.wasm.go
│ │ └── error.wit.go
│ ├── poll
│ │ ├── empty.s
│ │ ├── poll.wasm.go
│ │ └── poll.wit.go
│ └── streams
│ ├── empty.s
│ ├── streams.wasm.go
│ └── streams.wit.go
├── random
│ ├── insecure
│ │ ├── empty.s
│ │ ├── insecure.wasm.go
│ │ └── insecure.wit.go
│ ├── insecure-seed
│ │ ├── empty.s
│ │ ├── insecure-seed.wasm.go
│ │ └── insecure-seed.wit.go
│ └── random
│ ├── empty.s
│ ├── random.wasm.go
│ └── random.wit.go
└── sockets
├── instance-network
│ ├── empty.s
│ ├── instance-network.wasm.go
│ └── instance-network.wit.go
├── ip-name-lookup
│ ├── abi.go
│ ├── empty.s
│ ├── ip-name-lookup.wasm.go
│ └── ip-name-lookup.wit.go
├── network
│ ├── abi.go
│ ├── empty.s
│ ├── network.wasm.go
│ └── network.wit.go
├── tcp
│ ├── abi.go
│ ├── empty.s
│ ├── tcp.wasm.go
│ └── tcp.wit.go
├── tcp-create-socket
│ ├── empty.s
│ ├── tcp-create-socket.wasm.go
│ └── tcp-create-socket.wit.go
├── udp
│ ├── abi.go
│ ├── empty.s
│ ├── udp.wasm.go
│ └── udp.wit.go
└── udp-create-socket
├── empty.s
├── udp-create-socket.wasm.go
└── udp-create-socket.wit.go
39 directories, 91 files
add.exports.go 파일에는 Exports 구조체가 들어 있으며, 이 구조체에는 Go 코드에서 구현해야 하는 익스포트된 함수의 선언이 담겨 있습니다.
4. add 함수 구현하기
add 디렉터리에 main.go라는 파일을 만들고 다음 코드를 붙여 넣으십시오:
//go:generate go tool wit-bindgen-go generate --world adder --out internal ./docs:adder@0.1.0.wasm
package main
import (
"example.com/internal/docs/adder/add"
)
func init() {
add.Exports.Add = func(x uint32, y uint32) uint32 {
return x + y
}
}
// main is required for the `wasi` target, even if it isn't used.
func main() {}
Go의 init 함수는 다른 어떤 작업보다 먼저 수행해야 하는 초기화 작업에 사용됩니다. 여기서는 이 함수를 사용하여 Add 함수를 익스포트합니다.
5. 컴포넌트 빌드하기
TinyGo를 사용하여 컴포넌트를 빌드할 수 있습니다. 내부적으로 TinyGo는 wasm-tools를 호출하여 WIT 파일을 모듈에 임베드하고 이를 컴포넌트화합니다.
tinygo build -target=wasip2 \
-o adder.wasm \
--wit-package docs:adder@0.1.0.wasm \
--wit-world adder main.go
-target=wasip2플래그는 Preview 2 메서드를 사용하여 코드를 WebAssembly로 컴파일하도록 지정합니다.-o adder.wasm플래그는 출력을 현재 디렉터리의add.wasm에 저장하도록 지정합니다.--wit-package플래그는 사용 중인 WIT 코드의 패키지 이름을 지정합니다.--wit-world플래그는 컴포넌트의 임포트와 익스포트를 정의하는 WIT 월드가adder임을 지정합니다.
이제 adder 월드를 충족하고 add 함수를 익스포트하는 adder 컴포넌트가 준비되었습니다.
Warning
기본적으로 tinygo는 디버그 관련 정보를 모두 .wasm 파일에 포함합니다. 이는 프로토타이핑이나 로컬 테스트 시 오류가 발생했을 때 유용한 백트레이스를 얻는 데 바람직합니다(예:
wasmtime::WasmBacktraceDetails::Enable사용 시). 디버그 데이터를 제거하고 바이너리 파일을 최적화하려면-no-debug로 빌드하십시오. 그 결과 생성되는 .wasm 파일은 상당히 작아집니다(최대 75%까지 크기 감소).
wasm-tools component wit 명령으로 확인할 수 있습니다:
$ wasm-tools component wit adder.wasm
package root:component;
world root {
import wasi:io/error@0.2.0;
import wasi:io/streams@0.2.0;
import wasi:cli/stdout@0.2.0;
import wasi:random/random@0.2.0;
export add: func(x: s32, y: s32) -> s32;
}
...
5. add 컴포넌트 테스트하기
다음 섹션을 진행하려면 Rust 툴체인이 설치되어 있어야 합니다.
add 컴포넌트를 실행하려면 example 월드를 이해하는 WASI 런타임을 갖춘 호스트 프로그램을 사용해야 합니다.
이 저장소에는 adder 월드를 구현하는 컴포넌트를 실행할 수 있는, Rust로 작성된 WebAssembly 호스트 예제가 포함되어 있습니다.
git clone https://github.com/bytecodealliance/component-docs.gitcd component-docs/component-model/examples/example-hostcargo run --release -- 1 2 <PATH>/adder.wasm
- 이중 대시는
cargo에 전달되는 플래그와 사용자 코드에 전달되는 플래그를 구분합니다. - 인수 1과 2는 adder에 전달되는 인수입니다.
<PATH>자리에는 생성된adder.wasm파일이 들어 있는 디렉터리를 대신 입력하십시오.
참고: 호스트가 WASI 인터페이스를 사용하는 컴포넌트를 실행할 때는, 빌드된 컴포넌트를 실행하기 위해 명시적으로 링커에 WASI를 추가해야 합니다.
실행에 성공하면 다음과 같은 출력이 표시됩니다(물론 예제 호스트와 adder 컴포넌트의 경로는 환경에 따라 다릅니다).
cargo run --release -- 1 2 adder.wasm
Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host)
Finished `release` profile [optimized] target(s) in 7.85s
Running `target/debug/example-host 1 2 /path/to/adder.wasm`
1 + 2 = 3
올바르게 구성되지 않은 경우 다음과 같은 오류가 표시될 수 있습니다:
cargo run --release -- 1 2 adder.wasm
Compiling example-host v0.1.0 (/path/to/component-docs/component-model/examples/example-host)
Finished `release` profile [optimized] target(s) in 7.85s
Running `target/release/example-host 1 2 /path/to/adder.component.wasm`
Error: Failed to instantiate the example world
Caused by:
0: component imports instance `wasi:io/error@0.2.2`, but a matching implementation was not found in the linker
1: instance export `error` has the wrong type
2: resource implementation is missing
이러한 종류의 오류는 일반적으로 해당 호스트가 WASI 임포트를 충족하지 못한다는 것을 나타냅니다.