Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

C# 도구

C#로 작성하는 WebAssembly 컴포넌트는 componentize-dotnet으로 빌드할 수 있습니다. componentize-dotnet은 완전히 AOT(ahead-of-time) 컴파일된 컴포넌트를 만드는 데 사용할 수 있는 NuGet 패키지로, .NET 개발자에게 Rust 및 TinyGo에 견줄 만한 컴포넌트 개발 경험을 제공합니다.

componentize-dotnet으로 컴포넌트 빌드하기

componentize-dotnet은 여러 도구를 하나로 감싼 원스톱 도구 역할을 합니다:

먼저 .NET SDK를 설치하십시오. 이 안내에서는 .NET 10 SDK 프리뷰를 사용합니다. 생성한 바이너리를 실행할 수 있도록 wasmtime도 설치되어 있어야 합니다. 컴포넌트를 합성하려면 wac도 설치해야 합니다.

1. 새 프로젝트 만들기

.NET SDK를 설치했으면 새 프로젝트를 만드십시오:

dotnet new install BytecodeAlliance.Componentize.DotNet.Templates
dotnet new componentize.wasi.cli -o adder
cd adder

2. WIT 월드 만들기 또는 다운로드하기

다음으로, 대상으로 삼을 WIT 월드를 만들거나 다운로드하십시오.

이 예제에서는 두 개의 월드를 포함하는 WIT 파일을 사용합니다(처음에는 example 월드만 사용합니다). 다음 내용을 복사하여 “wit/component.wit“라는 새 파일에 붙여 넣으십시오.

package docs:adder@0.1.0;

interface add {
    add: func(x: u32, y: u32) -> u32;
}

world example {
    export add;
}

world hostapp {
    import add;
}

adder.csproj 프로젝트 파일에서 기존 <ItemGroup>과 같은 수준에 새 <ItemGroup>을 추가하십시오:

<ItemGroup>
    <Wit Update="wit/component.wit" World="example" />
</ItemGroup>

이 컴포넌트는 기능을 익스포트하기만 하므로 dotnet은 이를 라이브러리 프로젝트로 간주합니다. adder.csproj에서 <OutputType>을 라이브러리로 변경하십시오:

- <OutputType>Exe</OutputType>
+ <OutputType>Library</OutputType>

그리고 자동으로 생성된 Program.cs 파일을 삭제하십시오:

rm Program.cs

3. adder 월드의 구현 작성하기

dotnet build로 프로젝트를 빌드하려고 하면 다음과 같은 오류가 발생합니다:

➜ dotnet build
Restore complete (8.6s)
You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
  adder failed with 1 error(s) (25.6s)
    /path/to/adder/obj/Debug/net10.0/wasi-wasm/wit_bindgen/AdderWorld.wit.exports.docs.adder.v0_1_0.AddInterop.cs(15,19): error CS0103: The name 'AddImpl' does not exist in the current context

Build failed with 1 error(s) in 34.6s

이는 구현을 제공하겠다고 약속했지만 example 월드에 대한 구현을 아직 작성하지 않았기 때문입니다.

이 문제를 해결하려면 Component.cs라는 파일에 다음 코드를 추가하십시오:

namespace ExampleWorld.wit.exports.docs.adder.v0_1_0;

public class AddImpl : IAdd
{
    public static uint Add(uint x, uint y)
    {
        return x + y;
    }
}

그런 다음 컴포넌트를 빌드할 수 있습니다:

dotnet build

컴포넌트는 bin/Debug/net10.0/wasi-wasm/native/adder.wasm에 생성됩니다.

4. (선택 사항) 예제 호스트에서 컴포넌트 실행하기

다음 섹션을 진행하려면 Rust 툴체인이 설치되어 있어야 합니다.

이 저장소에는 adder 월드를 구현하는 컴포넌트를 실행할 수 있는, Rust로 작성된 WebAssembly 호스트 예제가 포함되어 있습니다.

  1. git clone https://github.com/bytecodealliance/component-docs.git
  2. cd component-docs/component-model/examples/example-host
  3. cargo 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 임포트를 충족하지 못한다는 것을 나타냅니다.

인터페이스를 임포트하는 컴포넌트 빌드하기

지금까지는 라이브러리 컴포넌트를 다루었습니다. 이제 hostapp 월드를 구현하는 command 컴포넌트를 만들겠습니다. 이 컴포넌트는 adder 컴포넌트에서 익스포트한 add 인터페이스를 임포트하고 add 함수를 호출합니다. 이후 이 command 컴포넌트를 방금 빌드한 adder 라이브러리 컴포넌트와 합성할 것입니다.

이제 adder 컴포넌트를 가져와 다른 WebAssembly 컴포넌트에서 실행하겠습니다.

dotnet new componentize.wasi.cli는 실행 파일을 만드는 새 프로젝트를 생성합니다.

현재 프로젝트의 상위 디렉터리로 이동한 후 새 프로젝트를 생성하십시오:

cd ..
dotnet new componentize.wasi.cli -o host-app
cd host-app

다음 WIT 파일을 프로젝트의 wit/add.wit 파일에 복사하십시오:

package docs:adder@0.1.0;

interface add {
    add: func(x: u32, y: u32) -> u32;
}

world example {
    export add;
}

world hostapp {
    import add;
}

이를 host-app.csproj 프로젝트 파일의 최상위 수준에 새 ItemGroup으로 추가하십시오:

<ItemGroup>
    <Wit Update="wit/add.wit" World="hostapp" />
</ItemGroup>

World가 example에서 hostapp으로 변경된 점에 주목하십시오. 이전 예제에서는 이 WIT 파일의 클래스 라이브러리, 즉 export 함수를 구현하는 데 중점을 두었습니다. 이제는 애플리케이션의 실행 파일 측면, 즉 hostapp 월드에 중점을 두겠습니다.

Program.cs를 다음과 같이 수정하십시오:

// Pull in all imports of the `hostapp` world, namely the `add` interface.
// example.component refers to the package name defined in the WIT file.
using HostappWorld.wit.imports.docs.adder.v0_1_0;

uint left = 1;
uint right = 2;
var result = AddInterop.Add(left, right);
Console.WriteLine($"{left} + {right} = {result}");

다시 한번 dotnet build로 컴포넌트를 컴파일하십시오:

$ dotnet build
Restore complete (0.4s)
You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy
  host-app succeeded (1.1s) → bin/Debug/net10.0/wasi-wasm/host-app.dll

Build succeeded in 2.5s

이 시점에서 두 개의 WebAssembly 컴포넌트가 생깁니다:

  1. example 월드를 구현하는 컴포넌트.
  2. hostapp 월드를 구현하는 컴포넌트.

host-app 컴포넌트는 example 월드에 정의된 add 함수에 의존하므로 첫 번째 컴포넌트와 합성해야 합니다. wac plug를 실행하여 host-app 컴포넌트를 adder 컴포넌트와 합성할 수 있습니다:

wac plug \
    bin/Debug/net10.0/wasi-wasm/native/host-app.wasm \
    --plug ../adder/bin/Debug/net10.0/wasi-wasm/native/adder.wasm \
    -o main.wasm

다음과 같은 오류 메시지가 표시되면:

error: the socket component had no matching imports for the plugs that were provided

두 .wit 파일(adder 컴포넌트용 파일과 host-app 컴포넌트용 파일)의 패키지 이름이 동일한지 확인하십시오.

host-app.csproj에 다음 내용을 추가하여 이 과정을 자동화할 수도 있습니다:

<Target Name="ComposeWasmComponent" AfterTargets="Publish">
    <PropertyGroup>
        <EntrypointComponent>bin/$(Configuration)/$(TargetFramework)/wasi-wasm/native/host-app.wasm</EntrypointComponent>
        <DependencyComponent>../adder/bin/$(Configuration)/$(TargetFramework)/wasi-wasm/native/adder.wasm</DependencyComponent>
    </PropertyGroup>
    <MakeDir Directories="dist" />
    <Exec Command="$(WacExe) plug $(EntrypointComponent) --plug $(DependencyComponent) -o dist/main.wasm" />
</Target>

이를 위해서는 원래의 adder.wasm 컴포넌트가 host-app 컴포넌트가 있는 디렉터리를 기준으로 ../adder에 있어야 합니다.

dotnet build를 다시 실행하면 ./dist/main.wasm에 합성된 컴포넌트가 생성됩니다.

그런 다음 합성된 컴포넌트를 실행할 수 있습니다:

wasmtime run ./dist/main.wasm
1 + 2 = 3

더 많은 구성 옵션은 componentize-dotnet 문서를 참조하십시오.


원문 © 2023 The Bytecode Alliance Contributors · WebAssembly Components Documentation · CC BY 4.0
Yeokja 한국어 번역·편집 · Anthropic 사의 claude-sonnet-5 모델을 활용하여 번역되었으며 학습을 모두 비허용한 상태로 작업하였습니다 · 이 번역은 원문을 수정한 문서이며 CC BY 4.0으로 제공합니다.