본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 4장: 컴포넌트 모델과 WIT
2026년 3월 28일·프로그래밍·

4장: 컴포넌트 모델과 WIT

WebAssembly 컴포넌트 모델의 필요성, WIT IDL의 문법과 타입 시스템, 인터페이스와 월드 정의, 컴포넌트 구성을 통한 언어 간 상호운용성을 다룹니다.

13분543자9개 섹션
webassemblyrust
공유
webassembly4 / 10
12345678910
이전3장: WASI — WebAssembly 시스템 인터페이스다음5장: Rust에서 Wasm 빌드

학습 목표

  • 컴포넌트 모델이 해결하는 문제를 이해합니다
  • WIT(WebAssembly Interface Type) 언어의 문법을 익힙니다
  • 레코드, 배리언트, 리소스 등 고수준 타입을 활용할 수 있습니다
  • 컴포넌트 구성(Composition)으로 다른 언어의 모듈을 조합하는 방법을 파악합니다

컴포넌트 모델이 필요한 이유

WebAssembly의 코어 스펙은 의도적으로 저수준입니다. 함수가 주고받을 수 있는 타입은 i32, i64, f32, f64 네 가지 숫자 타입뿐입니다. 문자열을 전달하려면 메모리에 바이트를 쓰고, 그 주소와 길이를 정수로 넘겨야 합니다.

이 저수준 인터페이스는 두 가지 심각한 문제를 야기합니다.

첫째, 상호운용이 극도로 어렵습니다. Rust로 컴파일한 Wasm 모듈이 문자열을 UTF-8로 인코딩하고, Python으로 컴파일한 모듈이 UTF-16을 사용한다면, 두 모듈은 직접 소통할 수 없습니다. ABI(Application Binary Interface) 호환성이 보장되지 않기 때문입니다.

둘째, 인터페이스 계약이 없습니다. 코어 Wasm 모듈의 익스포트를 보면 func(i32, i32) -> i32 같은 시그니처만 보입니다. 이 함수가 어떤 구조체를 기대하는지, 에러를 어떻게 반환하는지 알 방법이 없습니다.

**컴포넌트 모델(Component Model)**은 이 문제들을 해결하기 위해 코어 Wasm 위에 고수준 타입 시스템과 표준 ABI를 추가합니다.

WIT — WebAssembly Interface Type

**WIT(WebAssembly Interface Type)**는 컴포넌트의 인터페이스를 정의하는 IDL(Interface Description Language)입니다. Protocol Buffers나 GraphQL 스키마와 비슷한 역할을 하지만, WebAssembly에 특화된 타입 시스템을 제공합니다.

기본 타입

WIT는 다음과 같은 기본 타입을 제공합니다.

분류타입
부울bool
부호 있는 정수s8, s16, s32, s64
부호 없는 정수u8, u16, u32, u64
부동소수점f32, f64
문자char
문자열string

복합 타입

기본 타입을 조합하여 풍부한 데이터 구조를 표현할 수 있습니다.

types.wit
wit
package example:blog@1.0.0;
 
interface types {
    // 레코드 — 이름이 붙은 필드의 집합 (struct)
    record post {
        id: u64,
        title: string,
        content: string,
        author: string,
        published: bool,
        tags: list<string>,
        created-at: u64,
    }
 
    // 배리언트 — 여러 케이스 중 하나 (tagged union / enum)
    variant sort-order {
        newest,
        oldest,
        most-viewed,
        by-title(sort-direction),
    }
 
    // 열거형 — 값 없는 배리언트의 축약
    enum sort-direction {
        ascending,
        descending,
    }
 
    // 옵션 타입 — 값이 있거나 없음
    // option<T>는 내장 타입
    
    // 결과 타입 — 성공 또는 실패
    // result<T, E>는 내장 타입
 
    // 플래그 — 비트 플래그 집합
    flags permissions {
        read,
        write,
        execute,
    }
    
    // 튜플
    type point = tuple<f64, f64>;
}
Info

WIT의 네이밍 컨벤션은 kebab-case입니다. sortOrder나 sort_order가 아닌 sort-order를 사용합니다. 각 언어의 코드 생성기가 해당 언어의 컨벤션으로 자동 변환합니다. 예를 들어, sort-order는 Rust에서 SortOrder, Python에서 sort_order가 됩니다.

리소스 타입

**리소스(Resource)**는 컴포넌트 모델에서 가장 강력한 타입 중 하나입니다. 불투명한 핸들로, 소유권과 수명을 추적합니다. 데이터베이스 연결, 파일 핸들 등 상태가 있는 객체를 표현하는 데 적합합니다.

resource-example.wit
wit
interface database {
    // 리소스 정의 — 불투명 핸들
    resource connection {
        // 생성자
        constructor(url: string);
        
        // 메서드 (self 참조)
        query: func(sql: string) -> result<list<row>, db-error>;
        
        // 정적 메서드
        pool-size: static func() -> u32;
    }
    
    record row {
        columns: list<column-value>,
    }
    
    variant column-value {
        integer(s64),
        float(f64),
        text(string),
        null,
    }
    
    variant db-error {
        connection-failed(string),
        query-failed(string),
        timeout,
    }
}

리소스는 **소유권 의미론(Ownership Semantics)**을 따릅니다. 리소스를 함수에 전달하면 소유권이 이전되며, 빌려주려면 borrow<connection> 타입을 사용합니다. Rust의 소유권 모델에서 영감을 받은 설계입니다.

인터페이스와 월드

인터페이스(Interface)

인터페이스는 관련된 함수와 타입의 모음입니다. 하나의 WIT 파일에 여러 인터페이스를 정의할 수 있습니다.

blog-service.wit
wit
package example:blog-service@1.0.0;
 
interface reader {
    use types.{post, sort-order};
    
    get-post: func(id: u64) -> option<post>;
    list-posts: func(
        page: u32,
        per-page: u32,
        order: sort-order,
    ) -> list<post>;
    search: func(query: string) -> list<post>;
}
 
interface writer {
    use types.{post};
    
    record create-post-input {
        title: string,
        content: string,
        tags: list<string>,
    }
    
    create-post: func(input: create-post-input) -> result<post, string>;
    update-post: func(id: u64, input: create-post-input) -> result<post, string>;
    delete-post: func(id: u64) -> result<_, string>;
}

월드(World)

**월드(World)**는 컴포넌트의 전체 인터페이스를 정의합니다. 어떤 인터페이스를 가져오고(import), 어떤 인터페이스를 내보내는지(export) 명시합니다.

blog-world.wit
wit
world blog-api {
    // 이 컴포넌트가 사용하는 인터페이스 (호스트 제공)
    import wasi:logging/logging;
    import wasi:keyvalue/store;
    
    // 이 컴포넌트가 제공하는 인터페이스
    export reader;
    export writer;
}

Canonical ABI

컴포넌트 모델의 핵심 기반은 Canonical ABI입니다. WIT에서 정의한 고수준 타입이 코어 Wasm의 저수준 타입으로 어떻게 변환되는지 규정합니다.

예를 들어, string 타입은 Canonical ABI에서 다음과 같이 처리됩니다.

  1. 호출자가 문자열 데이터를 선형 메모리에 UTF-8로 인코딩합니다
  2. 메모리 주소(i32)와 바이트 길이(i32)를 함수 인자로 전달합니다
  3. 호출받는 쪽이 해당 메모리 영역을 읽어 문자열을 복원합니다

이 변환 과정은 개발자가 직접 다룰 필요가 없습니다. WIT에서 코드를 생성하면 바인딩 코드가 자동으로 Canonical ABI를 처리합니다.

Tip

Canonical ABI의 자동 처리 덕분에, Rust로 작성한 컴포넌트가 내보내는 func(input: string) -> result<post, error> 함수를 Python으로 작성한 다른 컴포넌트가 아무런 수동 작업 없이 호출할 수 있습니다. 이것이 컴포넌트 모델의 핵심 가치입니다.

컴포넌트 구성 (Composition)

컴포넌트 모델의 가장 강력한 기능 중 하나는 **컴포넌트 구성(Component Composition)**입니다. 여러 컴포넌트를 조합하여 새로운 컴포넌트를 만들 수 있습니다. 각 컴포넌트가 다른 언어로 작성되어 있어도 상관없습니다.

wasm-tools compose 명령어를 사용하여 컴포넌트를 구성할 수 있습니다.

composition.sh
bash
# 개별 컴포넌트 빌드
cargo component build --manifest-path auth/Cargo.toml
componentize-py -w business-logic business_logic.py -o business.wasm
tinygo build -target=wasip2 -o cache.wasm cache/main.go
 
# 컴포넌트 구성
wasm-tools compose \
  auth/target/wasm32-wasip2/release/auth.wasm \
  --definitions business.wasm \
  --definitions cache.wasm \
  -o composed-app.wasm

코드 생성 도구

WIT 정의에서 각 언어의 바인딩 코드를 자동 생성하는 도구들이 있습니다.

언어도구설명
Rustcargo-componentCargo 통합, 빌드 시 자동 생성
Pythoncomponentize-pyPython 코드를 Wasm 컴포넌트로
Gowit-bindgen-goTinyGo 기반 바인딩
JavaScriptjcoJS 컴포넌트 트랜스파일러
C/C++wit-bindgen-cC 헤더 파일 생성
generated-bindings.rs
rust
// cargo-component가 자동 생성하는 바인딩 예시
// 개발자는 아래 trait만 구현하면 됩니다
 
impl Guest for Component {
    fn get_post(id: u64) -> Option<Post> {
        // 비즈니스 로직 구현
        Some(Post {
            id,
            title: "Hello".to_string(),
            content: "World".to_string(),
            author: "Kreath".to_string(),
            published: true,
            tags: vec!["wasm".to_string()],
            created_at: 1234567890,
        })
    }
    
    fn list_posts(page: u32, per_page: u32, order: SortOrder) -> Vec<Post> {
        // 구현
        vec![]
    }
}

정리

이번 장에서는 컴포넌트 모델과 WIT를 상세히 살펴보았습니다.

  • 컴포넌트 모델은 코어 Wasm의 저수준 타입 제약을 해결하여 고수준 상호운용성을 제공합니다
  • WIT는 레코드, 배리언트, 리소스 등 풍부한 타입 시스템을 갖춘 인터페이스 정의 언어입니다
  • 월드(World)는 컴포넌트의 임포트와 익스포트를 명세하는 계약입니다
  • Canonical ABI가 고수준 타입과 저수준 Wasm 타입 간의 변환을 자동으로 처리합니다
  • 컴포넌트 구성으로 서로 다른 언어의 모듈을 하나로 조합할 수 있습니다

다음 장 미리보기

5장에서는 이론을 실천으로 옮깁니다. Rust에서 Wasm을 빌드하는 전체 과정을 다루며, wasm-pack과 cargo-component 사용법, 바이너리 크기 최적화, 컴포넌트 모델 적용, 그리고 HTTP 핸들러 실전 예제를 작성합니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#webassembly#rust

관련 글

프로그래밍

5장: Rust에서 Wasm 빌드

Rust에서 WebAssembly를 빌드하는 전체 과정을 다룹니다. wasm-pack, cargo-component, 크기 최적화, WASI 타겟 빌드, 컴포넌트 모델 적용, HTTP 핸들러 실전 예제까지.

2026년 3월 30일·13분
프로그래밍

3장: WASI — WebAssembly 시스템 인터페이스

WASI의 탄생 배경과 Capability-based 보안 모델, WASI Preview 2의 Worlds 개념, 비동기 지원을 위한 WASI 0.3, 그리고 1.0 로드맵을 상세히 다룹니다.

2026년 3월 26일·14분
프로그래밍

6장: Go, Python, 기타 언어에서 Wasm

TinyGo, Python(componentize-py), C/C++(Emscripten), AssemblyScript, .NET Blazor 등 다양한 언어의 Wasm 지원 현황과 제약 사항, 언어 선택 가이드를 다룹니다.

2026년 4월 1일·14분
이전 글3장: WASI — WebAssembly 시스템 인터페이스
다음 글5장: Rust에서 Wasm 빌드

댓글

목차

약 13분 남음
  • 학습 목표
  • 컴포넌트 모델이 필요한 이유
  • WIT — WebAssembly Interface Type
    • 기본 타입
    • 복합 타입
    • 리소스 타입
  • 인터페이스와 월드
    • 인터페이스(Interface)
    • 월드(World)
  • Canonical ABI
  • 컴포넌트 구성 (Composition)
  • 코드 생성 도구
  • 정리
  • 다음 장 미리보기