튜토리얼
직접 해 보면서 배우는 것을 좋아하신다면, 이 튜토리얼에서는 계산기 예제를 통해 컴포넌트를 빌드하고, 합성하고, 실행하는 방법을 차근차근 살펴봅니다. 계산기는 덧셈, 뺄셈, 곱셈 등 많은 연산을 수행할 수 있습니다.
이 예제에서는 각 연산이 하나의 컴포넌트가 되며, 이 컴포넌트는 예상되는 연산자를 사용하여 식을 평가하는 eval-expression 컴포넌트와 합성됩니다. 컴포넌트마다 연산을 하나씩 두므로 이 계산기는 과장될 정도로 세분화되어 있으며, 이는 애플리케이션의 독립적인 로직을 하나의 컴포넌트에 담을 수 있는 방식을 보여 주기 위한 것입니다.
프로덕션 환경에서는 컴포넌트가 단순한 수학 연산보다 더 넓은 범위를 가질 가능성이 높습니다.
최종 솔루션은 다음 세 가지 컴포넌트로 구성됩니다:
- 계산기 엔진,
- 덧셈 연산
- 명령줄 인터페이스.
이들을 별도의 Wasm 컴포넌트로 빌드한 후에는 하나의 실행 가능한 컴포넌트로 합성하고, wasmtime CLI를 사용하여 테스트합니다.
계산기 인터페이스
튜토리얼 목적상 “계산기 엔진” 인터페이스와 “덧셈 연산” 인터페이스를 각각 하나의 WIT 파일을 포함하는 두 개의 별도 WIT 패키지에 넣습니다.
이 구성이 과도해 보일 수 있지만, 이는 컴포넌트가 서로 다른 작성자와 패키지에서 제공되는 실제 사용 사례를 보여 줍니다.
이 파일들은 컴포넌트 북 저장소의 examples/tutorial/wit 디렉터리에서 wit/adder/world.wit 및 wit/calculator/world.wit 경로로 찾을 수 있습니다:
// wit/adder/world.wit
package docs:adder@0.1.0;
interface add {
add: func(x: u32, y: u32) -> u32;
}
world adder {
export add;
}
// wit/calculator/world.wit
package docs:calculator@0.1.0;
interface calculate {
enum op {
add,
}
eval-expression: func(op: op, x: u32, y: u32) -> u32;
}
world calculator {
export calculate;
import docs:adder/add@0.1.0;
}
world app {
import calculate;
}
이 파일들은 다음을 정의합니다:
add인터페이스를 익스포트하는adder월드입니다. 다시 말하지만, 계산기와 같은 컴포넌트는 숫자를 더해야 할 때 이를 호출할 수 있습니다.- 계산기 컴포넌트를 기술하는 월드
calculator. 이 월드는 calculator 인터페이스를 익스포트합니다. 따라서 다른 컴포넌트가 이 인터페이스를 호출하여 계산을 수행할 수 있습니다. 이 월드는 (add와 같은) 연산 인터페이스를 임포트합니다. 따라서 해당 연산을 수행하려면 다른 컴포넌트가 필요합니다. - 평가 함수와, 계산에 포함될 수 있는 연산을 구분하는 열거형을 포함하는 인터페이스
calculate. 이 튜토리얼에서 유일한 연산은add입니다. calculate인터페이스를 임포트하는 “주” 앱 컴포넌트를 기술하는app월드입니다. 이 컴포넌트는 명령줄 인수를 받아 계산기 컴포넌트의eval-expression함수에 전달합니다.
add 컴포넌트 만들기
언어 가이드를 참조하여 adder/wit/world.wit의 adder 월드를 구현하는 컴포넌트를 만드십시오.
참고로, 완성된 예제를 확인하십시오.
calculator 컴포넌트 만들기
언어 가이드를 참조하여 wit/calculator/world.wit의 calculator 월드를 구현하는 컴포넌트를 만드십시오.
참고로, 완성된 예제를 확인하십시오.
완성된 컴포넌트는 adder 월드에서 add 함수를 임포트하고, op 열거형이 add와 일치하면 이 함수를 호출해야 합니다.
command 컴포넌트 만들기
command는 wasmtime(또는 다른 wasi:cli 호스트)에서 직접 실행할 수 있게 하는 특정 익스포트를 가진 컴포넌트입니다.
WebAssembly 호스트는 이 컴포넌트가 wasi:cli/run 인터페이스를 익스포트하기를 기대하며, 이 인터페이스는 WASI에서 main 함수에 해당합니다.
command 컴포넌트를 빌드하려면 wasi:cli 인터페이스를 준수하는 --lib 크레이트를 빌드하도록 cargo를 구성해야 합니다. 중요한 점은 컴포넌트가 반드시 wasi:cli/run을 익스포트해야 한다는 것입니다.
실행 가능한 새 Wasm 애플리케이션의 뼈대를 만들려면 다음과 같이 하십시오.
cargo new --lib example
cd example
이 컴포넌트는 calculate 인터페이스를 임포트하는 app 월드를 구현합니다.
Cargo.toml에서 이 크레이트를 cdylib로 구성하십시오.
[lib]
crate-type = "cdylib"
또한 계산 함수를 임포트하여 사용하는 WIT 인터페이스를 실행 가능하도록 수정해야 합니다.
world app {
import calculate;
+ export wasi:cli/run@0.2.7;
}
calculator 월드가 add 인터페이스를 임포트하므로, command 컴포넌트도 adder WIT를 의존성으로 가져와야 합니다.
이를 위해 wasm-pkg-tools의 wkg를 사용할 수 있습니다. 먼저 wkg가 docs:adder 저장소를 찾을 수 있도록 구성해야 합니다.
# $XDG_CONFIG_HOME/wasm-pkg/config.toml
default_registry = "ghcr.io"
[namespace_registries]
# Tell wkg that the component-book WITs can be found at ghcr.io/bytecodealliance/docs
docs = { registry = "docs", metadata = { preferredProtocol = "oci", "oci" = {registry = "ghcr.io", namespacePrefix = "bytecodealliance/" } } }
Note
docs:adderWIT 패키지는 GHCR에 미리 게시해 두었으므로, 위 구성을 통해 쉽게 찾고 접근할 수 있습니다.직접 만든 사용자 정의 WIT를 사용할 수 있게 하려면
wkg publish명령을 사용하십시오.
프로젝트의 뼈대가 준비되었으므로, 이제 다음과 같은 명령줄 애플리케이션을 실제로 구현해야 합니다.
- 세 개의 인수, 즉 두 개의 피연산자와 연산자 이름(“1 2 add”)을 받습니다.
- 연산자 이름을 파싱하고
op열거형에서 지원하는 연산자인지 확인합니다 - 인수를 전달하여
calculate인터페이스의eval_expression을 호출합니다.
참고로 완성된 코드 목록을 확인하십시오.
계산기 합성하기
이제 wac을 사용하여 컴포넌트들을 하나의 실행 가능한 계산기 컴포넌트로 결합할 준비가 되었습니다.
다음 작업을 수행합니다.
- calculator 컴포넌트의
adder임포트를 충족하기 위해 calculator 컴포넌트를 add 컴포넌트와 합성합니다. - command 컴포넌트의
calculate임포트를 충족하기 위해 이렇게 해결된 calculator 컴포넌트를 command 컴포넌트와 한 번 더 합성합니다.
그 결과 모든 임포트가 충족되고 단일 익스포트(wasi:cli/run 인터페이스)를 가진 완전한 command 컴포넌트가 만들어지며, 이 컴포넌트는 wasmtime으로 실행할 수 있습니다.
wac plug calculator.wasm --plug adder.wasm -o composed.wasm
wac plug command.wasm --plug composed.wasm -o final.wasm
계산기 실행하기
이제 모든 것이 맞아떨어집니다! wasmtime CLI의 이전 릴리스에는 컴포넌트 모델 지원이 포함되어 있지 않으므로, 최신 릴리스(v14.0.0 이상)를 사용하고 있는지 확인한 후 wasmtime CLI로 최종 컴포넌트를 실행하십시오.
wasmtime run final.wasm 1 2 add
1 + 2 = 3
무한한 공간 저 너머로!
이 실습을 확장하여 컴포넌트를 더 추가하려면 calculator.wit를 수정하여 연산자 월드를 하나 더 추가하고 op 열거형을 확장하십시오. 그런 다음 확장된 열거형을 지원하도록 command 컴포넌트와 calculator 컴포넌트를 수정하십시오.
이 튜토리얼을 확장하는 또 다른 방법으로, op 열거형을 제거하고 대신 eval-expression이 문자열을 입력받도록 수정할 수 있습니다. 이 문자열을 파싱하여 어떤 연산자 컴포넌트를 호출할지 결정하는 것입니다. 어쩌면 이 파서 자체도 하나의 컴포넌트가 될 수 있지 않을까요?!