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

컴포넌트 합성하기

WebAssembly 컴포넌트 모델을 사용하면 애플리케이션과 컴포넌트가 원래 어떤 언어로 작성되었는지와 관계없이 함께 동작할 수 있습니다. 컴포넌트 모델은 코드를 이식 가능한 바이너리 형식으로 패키징하고, 표준화된 애플리케이션 바이너리 인터페이스(ABI)와 함께 WIT로 작성된 기계 판독 가능한 인터페이스를 제공하여 이를 실현합니다. 예를 들어 Rust 패키지(크레이트)를 다른 Rust 코드와 함께 컴파일하여 더 상위 수준의 라이브러리나 애플리케이션을 만들 수 있는 것과 마찬가지로, WebAssembly 컴포넌트도 다른 컴포넌트와 합성할 수 있습니다.

컴포넌트 모델의 상호 운용은 언어별 외부 함수 인터페이스(FFI)보다 더 편리하고 표현력이 뛰어납니다. 일반적인 C FFI는 언어별 타입을 사용하므로, 최소한 어느 정도의 C 언어 래핑이나 변환 없이는 임의의 언어 간에 링크할 수 없습니다. 반면 컴포넌트 모델은 인터페이스를 표현하는 공통된 방식과 해당 인터페이스의 표준 바이너리 표현을 제공합니다. 따라서 임포트와 익스포트의 형태가 같다면 둘은 곧바로 맞물립니다.

합성이란 무엇입니까?

컴포넌트를 합성할 때는 하나의 주 컴포넌트의 임포트를 하나 이상의 다른 의존성 컴포넌트의 익스포트에 연결하여 새 컴포넌트를 만듭니다. 새 컴포넌트는 원래 컴포넌트와 마찬가지로 .wasm 파일이며, 그 인터페이스는 다음과 같이 정의됩니다.

  • 새 컴포넌트는 주 컴포넌트와 동일한 익스포트를 익스포트합니다.
  • 새 컴포넌트는 의존성 컴포넌트의 익스포트를 익스포트하지 않습니다.
  • 새 컴포넌트는 의존성 컴포넌트의 모든 임포트를 임포트합니다.
  • 새 컴포넌트는 주 컴포넌트의 임포트 중 의존성이 충족하지 못한 모든 임포트를 임포트합니다.
  • 여러 컴포넌트가 같은 인터페이스를 임포트하는 경우, 새 컴포넌트는 해당 인터페이스를 임포트하며, 그 임포트가 여러 다른 곳에서 선언되었다는 사실을 “기억“하지 않습니다.

예를 들어, 다음과 같은 월드를 가진 두 컴포넌트를 생각해 보십시오.

// component `validator`
package docs:validator@0.1.0;

interface validator {
    validate-text: func(text: string) -> string;
}

world validator {
    export validator;
    import docs:regex/match@0.1.0;
}
// component 'regex'
package docs:regex@0.1.0;

interface match {
    first-match: func(regex: string, text: string) -> string;
}

world regex {
    export match;
}

이 예제에서 validator는 주 컴포넌트이고 regex는 의존성 컴포넌트입니다. validator를 regex와 합성하면, validator의 docs:regex/match@0.1.0 임포트가 regex의 match 익스포트에 연결됩니다. 최종적으로 합성된 컴포넌트는 docs:validator/validator@0.1.0를 익스포트하며 임포트는 없습니다. 합성된 컴포넌트는 docs:regex/match@0.1.0를 익스포트하지 않습니다. 이는 합성된 컴포넌트의 내부 구현 세부 사항이 되었습니다.

컴포넌트 합성 도구는 현재 초기 단계에 있습니다. 다음은 오류를 방지하거나 진단하기 위한 몇 가지 팁입니다.

  • 합성은 인터페이스 수준에서 이루어집니다. 초기 컴포넌트가 함수를 직접 임포트하면 합성이 실패합니다. 합성 과정에서 “컴포넌트 path/to/component에 <name>이라는 이름의 비인스턴스 임포트가 있습니다“와 같은 오류가 보고되면, 모든 임포트와 익스포트가 인터페이스로 정의되어 있는지 확인하십시오.
  • 합성은 비대칭적입니다. 합성은 단순히 “컴포넌트를 서로 붙이는 것“이 아니라, 임포트가 있는 주 컴포넌트를 가져와 의존성 컴포넌트를 사용하여 그 임포트를 충족하는 것입니다. 예를 들어 validator에는 regex가 충족할 수 있는 의존성이 있으므로 validator 구현을 regex 구현과 합성하는 것은 타당하지만, 반대로 하는 것은 동작하지 않습니다. regex에는 validator가 충족할 수 있는 의존성은커녕 의존성 자체가 전혀 없기 때문입니다.
  • 합성은 인터페이스 버전을 따지며, 현재 도구들은 버전을 추론하거나 주입하는 시점이 일관되지 않습니다. 다른 Rust 컴포넌트가 test:mypackage에서 인터페이스를 임포트하는 경우, 이는 test:mypackage@0.1.0와 일치하지 않습니다. wasm-tools component wit를 사용하면 .wasm 파일에 포함된 임포트와 익스포트를 확인하고 서로 일치하는지 점검할 수 있습니다.

WAC로 컴포넌트 합성하기

WebAssembly Compositions (WAC) CLI를 사용하여 명령줄에서 컴포넌트를 합성할 수 있습니다.

빠르고 간단한 합성을 수행하려면 wac plug 명령을 사용하십시오. wac plug는 “플러그” 컴포넌트의 익스포트를 “소켓” 컴포넌트에 꽂아 소켓 컴포넌트의 임포트를 충족합니다. 소켓 컴포넌트는 주 컴포넌트이고, 플러그 컴포넌트는 의존성 컴포넌트입니다. 예를 들어, 위의 validator 월드를 구현하는 컴포넌트는 자신의 match 임포트를 충족해야 합니다. 이 컴포넌트가 소켓입니다. 반면 regex 월드를 구현하는 컴포넌트는 match 인터페이스를 익스포트하며, 플러그로 사용할 수 있습니다. wac plug는 regex 컴포넌트의 익스포트를 validator 컴포넌트의 임포트에 꽂아 다음과 같은 합성 결과물을 만들 수 있습니다.

wac plug validator-component.wasm --plug regex-component.wasm -o composed.wasm

컴포넌트는 자신이 의존하는 둘 이상의 컴포넌트와 합성할 수도 있습니다.

wac plug path/to/component.wasm --plug path/to/dep1.wasm --plug path/to/dep2.wasm -o composed.wasm

여기서 component.wasm은 인터페이스를 익스포트하는 dep1.wasm과 dep2.wasm에서 인터페이스를 임포트하는 컴포넌트입니다. 이러한 의존성이 충족되어 내부에 담긴 합성된 컴포넌트는 composed.wasm에 저장됩니다.

plug 구문은 전이 의존성을 다루지 않습니다. 예를 들어, dep1.wasm에 dep3.wasm으로 충족하려는 미충족 임포트가 있다면, 합성 순서를 신중하게 정해야 합니다. 먼저 dep1.wasm을 dep3.wasm과 합성한 다음, dep1.wasm 대신 그 합성된 컴포넌트를 참조할 수 있습니다. 하지만 이 방식은 전이 의존성이 많아지면 확장되지 않으며, 이것이 WAC 언어가 만들어진 이유입니다.

WAC 언어를 사용한 고급 합성

wac plug는 위와 같은 컴포넌트 합성에서 흔히 쓰이는 패턴을 편리하게 수행하기 위한 도구입니다. 하지만 합성은 얼마든지 복잡해질 수 있습니다. wac plug로 충분하지 않은 경우, WAC 언어를 사용하면 얼마든지 복잡한 합성을 만들 수 있습니다.

WAC 파일에서는 WAC 언어를 사용하여 합성을 기술합니다. 예를 들어, 다음은 앞에서 다룬 validator 컴포넌트를 만드는 데 사용할 수 있는 WAC 파일입니다.

//composition.wac
// Provide a package name for the resulting composition
package docs:composition;

// Instantiate the regex-impl component that implements the `regex` world.
// Bind this instance's exports to the local name `regex`.
let regex = new docs:regex-impl { };

// Instantiate the validator-impl component which implements the `validator` world
// and imports the match interface from the regex component.
let validator = new docs:validator-impl { match: regex.match, ... };

// Export all remaining exports of the validator instance
export validator...;

그런 다음 wac compose를 사용하여 컴포넌트를 합성할 수 있으며, 이때 --dep 플래그로 컴포넌트 이름과 .wasm 파일 간의 관계를 지정합니다.

wac compose --dep docs:regex-impl=regex-component.wasm \
            --dep docs:validator-impl=validator-component.wasm \
            -o composed.wasm \
            composition.wac

또는 컴포넌트를 예상되는 구조에 맞춰 deps 디렉터리에 배치할 수 있으며, 가까운 향후에는 레지스트리에서 컴포넌트를 가져올 수 있게 될 예정입니다. 자세한 내용은 wac 문서를 참조하십시오.

wac 도구 사용 방법에 대한 자세한 설명은 WAC 언어 색인과 예제를 확인하십시오.


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