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

WIT 개요

WIT(Wasm Interface Type) 언어는 컴포넌트 모델의 인터페이스와 월드를 정의하는 데 사용됩니다. WIT는 범용 프로그래밍 언어가 아니며 동작을 정의하지 않습니다. WIT는 컴포넌트 간의 계약만을 정의합니다.

새 컴포넌트를 정의하려면 Wasm Interface Type(WIT) 언어로 코드를 작성하여 월드와 인터페이스를 정의해야 합니다. WIT는 사용하려는 기존 컴포넌트에 대한 문서 역할도 합니다.

이 주제에서는 WIT 언어의 핵심 요소에 대한 개요를 제공합니다. 공식 WIT 명세와 그 변경 이력은 WebAssembly/component-model 저장소에서 확인할 수 있습니다.

WIT 파일의 구조

WIT 파일에는 하나 이상의 인터페이스 또는 월드가 포함됩니다. 인터페이스나 월드는 타입, 함수 또는 둘 모두를 정의할 수 있습니다.

타입과 함수는 인터페이스나 월드 외부에서 정의할 수 없습니다.

파일은 선택적으로 패키지 선언으로 시작할 수 있습니다.

주석

WIT 주석 구문은 C++ 계열 언어에서 사용하는 구문과 유사합니다.

  • //부터 줄 끝까지의 모든 내용은 주석입니다.
  • /* … */로 둘러싸인 모든 텍스트는 주석입니다.
    • C++ 계열과 달리 블록 주석은 중첩될 수 있습니다. 예: /* blah /* rabbit */ rhubarb */.

문서화

WIT는 문서화를 위한 특수 주석 형식을 정의합니다.

  • ///부터 줄 끝까지의 모든 내용은 뒤따르는 항목에 대한 문서입니다.
  • /** … */로 둘러싸인 모든 텍스트는 뒤따르는 항목에 대한 문서입니다.

예를 들면 다음과 같습니다.

/// Prints "hello".
print-hello: func();

/**
Prints "hello".
*/
print-hello: func();

식별자

식별자는 변수, 함수, 타입, 인터페이스, 월드의 이름입니다. WIT 식별자는 C, Rust, Java 같은 언어에서 익숙하게 접했을 규칙과는 약간 다른 규칙을 따릅니다. 이 규칙은 패키지를 제외한 모든 이름에 적용됩니다. 패키지 식별자는 조금 더 복잡하며 패키지 섹션에서 다룹니다.

  • 식별자는 ASCII kebab-case로 제한됩니다. 즉, 단일 하이픈으로 구분된 단어의 나열이어야 합니다.
    • 이중 하이픈(--)은 허용되지 않습니다.
    • 하이픈은 나열의 시작이나 끝에는 올 수 없으며, 단어 사이에만 올 수 있습니다.
  • 식별자 앞에는 % 기호 하나를 붙일 수 있습니다.
    • 식별자가 그대로 두면 WIT 키워드가 되는 경우에는 이것이 필수입니다. 예를 들어 interface는 올바른 식별자가 아니지만, %interface는 올바른 식별자입니다.
  • 각 단어에는 ASCII 문자와 숫자만 포함할 수 있습니다.
    • 첫 번째 단어는 숫자로 시작할 수 없습니다. 나머지 단어는 숫자 또는 ASCII 문자로 시작할 수 있습니다.
    • 단어에는 ASCII가 아닌 유니코드 문자를 포함할 수 없습니다.
    • 단어에는 문장 부호, 밑줄 등을 포함할 수 없습니다.
  • 각 단어는 모두 lowercase이거나 모두 UPPERCASE여야 합니다.
    • 식별자 안의 각 단어는 대소문자가 서로 달라도 됩니다. 예를 들어 WIT-demo는 허용됩니다.
  • 식별자는 interface와 같은 WIT 키워드일 수 없습니다(단, 앞에 % 기호가 붙은 경우는 예외입니다).

내장 타입

이 절의 타입은 WIT 언어 자체에서 정의합니다.

원시 타입

WIT는 다음과 같은 원시 타입을 정의합니다:

식별자설명
booltrue 또는 false인 불리언 값입니다.
s8, s16, s32, s64해당 너비의 부호 있는 정수입니다. 예를 들어 s32는 부호 있는 32비트 정수입니다.
u8, u16, u32, u64해당 너비의 부호 없는 정수입니다. 예를 들어 u32는 부호 없는 32비트 정수입니다.
f32, f64해당 너비의 부동소수점 수입니다. 예를 들어 f64는 64비트(배정밀도) 부동소수점 수입니다. 아래의 NaN에 관한 참고 사항을 확인하십시오.
char유니코드 문자입니다. (정확히는 유니코드 스칼라 값입니다.)
string유니코드 문자열, 즉 문자의 유한한 시퀀스입니다.

f32 및 f64 타입은 일반적인 IEEE 754 단정밀도 및 배정밀도 값 집합을 지원하지만, 논리적으로는 단 하나의 nan 값만 가진다는 점이 다릅니다. 값이 WIT 인터페이스를 통과하며 단일한 WIT nan 값으로 전달될 때 IEEE 754 NaN의 정확한 비트 수준 표현이 보존된다는 보장은 없습니다.

리스트

임의의 타입 T에 대해 list<T>는 T 타입 값의 순서 있는 시퀀스를 나타냅니다. T는 내장 타입이든 사용자 정의 타입이든 어떤 타입이라도 될 수 있습니다:

list<u8>       // byte buffer
list<customer> // a list of customers

이는 Rust의 Vec이나 Java의 List와 유사합니다.

옵션

임의의 타입 T에 대한 option<T>는 T 타입의 값을 포함하거나 아무 값도 포함하지 않을 수 있습니다. T는 내장 타입이든 사용자 정의 타입이든 어떤 타입이든 될 수 있습니다. 예를 들어, 조회 함수는 조회 키를 찾지 못했을 가능성을 허용하기 위해 option을 반환할 수 있습니다:

option<customer>

이는 Rust의 Option, C++의 std::optional, Haskell의 Maybe와 유사합니다.

이는 variant 타입의 특수한 경우입니다. WIT는 이를 표현하는 공통된 방법을 제공하여 모든 값 타입마다 variant 타입을 만들 필요가 없도록 하고, option 타입이 있는 언어에 관용적으로 매핑될 수 있도록 이 타입을 정의합니다.

Result

임의의 타입 T와 E에 대한 result<T, E>는 T 타입의 값 또는 E 타입의 값을 포함할 수 있습니다(둘 다 포함할 수는 없습니다). 예를 들어, HTTP 요청 함수는 result를 반환할 수 있으며, 이때 성공 케이스(T 타입)는 HTTP 응답을 나타내고 오류 케이스(E 타입)는 발생할 수 있는 다양한 종류의 오류를 나타냅니다:

result<http-response, http-error>

이는 Rust의 Result 또는 Haskell의 Either와 유사합니다.

이는 variant 타입의 특수한 경우입니다. WIT는 이러한 동작을 표현하는 공통된 방법을 제공하여 개발자가 값 타입과 오류 타입의 모든 조합마다 variant 타입을 만들 필요가 없도록 하고, result 또는 “either” 타입이 있는 언어에 관용적으로 매핑될 수 있도록 result 타입을 정의합니다.

때로는 한쪽 또는 양쪽 케이스에 연관된 데이터가 없는 경우가 있습니다. 예를 들어, print 함수는 실패하면 오류 코드를 반환할 수 있지만, 성공하면 반환할 것이 없습니다. 이 경우 다음과 같이 해당 타입을 생략할 수 있습니다:

result<u32>     // no data associated with the error case
result<_, u32>  // no data associated with the success case
result          // no data associated with either case

밑줄 _는 “데이터 없음“을 나타내며, 일반적으로 대상 언어에서 unit 타입으로 표현됩니다(예: Rust의 (), JavaScript의 null).

튜플

tuple 타입은 지정된 타입의 값들로 이루어진 순서가 있는 고정 길이 시퀀스입니다. 이는 record와 유사하지만, 필드가 이름 대신 인덱스로 식별된다는 점이 다릅니다.

tuple<u64, string>      // An integer and a string
tuple<u64, string, u64> // An integer, then a string, then an integer

이는 Rust나 OCaml의 튜플과 유사합니다.

스트림

임의의 타입 T에 대한 stream<T>는 T 타입 값들의 비동기적이고 순서가 있는 시퀀스를 나타냅니다. 모든 값을 한 번에 포함하는 list<T>와 달리, stream<T>는 값을 점진적으로 전달합니다. 생산자는 요소가 사용 가능해지는 대로 밀어 넣고, 소비자는 요소가 도착하는 대로 받습니다.

stream<u8>        // a stream of bytes
stream<log-entry> // a stream of records

stream<T>는 리소스가 아니라 정규 ABI 값입니다. 따라서 원시 타입과 같은 방식으로 함수에서 반환되거나, 매개변수로 전달되거나, 컴포넌트 경계를 넘어 전달될 수 있습니다. 중간 컴포넌트는 스트림을 소비하지 않고 그대로 넘겨줄 수도 있습니다.

스트림은 WASI 0.3에서 추가되었으며, 스트림이 종료되었음을 알리는 future와 함께 쓰이는 경우가 가장 많습니다. 스트림과 퓨처로 이루어진 튜플 형태는 wasi:filesystem, wasi:cli, wasi:sockets 전반에 걸쳐 나타납니다.

근본적인 동기는 비동기, 스트림, 퓨처를 참조하십시오.

퓨처

임의의 타입 T에 대한 future<T>는 나중에 사용 가능해질 T 타입의 단일 값에 대한 타입이 지정된 핸들입니다. future<T>를 반환하는 함수는 값이 생성될 때까지 블록되지 않으며, 호출자는 값이 필요할 때 퓨처를 await합니다.

future<u32>                          // a future that will resolve to a u32
future<result<response, error-code>> // a future that will resolve to either a response or an error

stream<T>와 마찬가지로 future<T>는 리소스가 아니라 정규 ABI 값이므로, 기본 타입과 같은 방식으로 컴포넌트 경계를 넘어 이동합니다.

퓨처는 WASI 0.3에서 추가되었습니다. 이는 JavaScript의 Promise 또는 Rust의 std::future::Future와 유사합니다.

근본적인 동기는 비동기, 스트림, 퓨처를 참조하십시오.

사용자 정의 타입

새로운 도메인별 타입은 interface 또는 world 안에서 정의할 수 있습니다.

레코드

record 타입은 쉼표로 구분된 이름 있는 필드들의 집합을 선언하며, 각 필드는 name: type 형식입니다. 레코드 인스턴스는 모든 필드에 대한 값을 포함합니다. 필드 타입은 기본 제공 타입이거나 사용자 정의 타입일 수 있습니다. 구문은 다음과 같습니다.

record customer {
    id: u64,
    name: string,
    picture: option<list<u8>>,
    account-manager: employee,
}

레코드는 C 또는 Rust의 struct와 유사합니다.

사용자 정의 레코드는 제네릭일 수 없습니다(즉, 타입으로 매개변수화될 수 없습니다). 내장 타입만 제네릭일 수 있습니다.

variant

variant 타입은 구조가 달라지는 데이터를 나타냅니다. 선언은 케이스 목록을 정의하며, 각 케이스는 이름을 가지고, 선택적으로 해당 케이스와 연관된 데이터 타입을 가집니다. variant 타입의 인스턴스는 정확히 하나의 케이스에 해당합니다. 케이스는 쉼표로 구분됩니다. 구문은 다음과 같습니다.

variant allowed-destinations {
    none,
    any,
    restricted(list<address>),
}

이는 “허용된 목적지는 없음(none), 모두(any), 또는 특정 주소 목록으로 제한됨 중 하나“라고 읽을 수 있습니다.

variant는 Rust의 enum이나 OCaml의 판별 공용체(discriminated union)와 비슷합니다. C에서 가장 가까운 개념은 태그된 공용체(tagged union)입니다. 다만 WIT의 variant는 “태그”(케이스)를 처리할 뿐 아니라 각 태그에 맞는 올바른 데이터 형태도 강제합니다.

사용자 정의 variant는 제네릭일 수 없습니다(즉, 타입으로 매개변수화할 수 없습니다). 내장 타입만 제네릭일 수 있습니다.

열거형

enum 타입은 어떤 케이스에도 연관 데이터가 없는 variant 타입입니다.

enum color {
    hot-pink,
    lime-green,
    navy-blue,
}

이는 판별 공용체가 없는 언어에서 더 단순한 표현을 제공할 수 있습니다. 예를 들어, WIT enum은 C/C++ enum으로 직접 변환될 수 있습니다.

리소스

리소스는 컴포넌트 외부에 존재하는 어떤 엔티티에 대한 핸들입니다. 리소스는 복사할 수 없거나 복사해서는 안 되는 엔티티, 즉 값이 아닌 참조로 전달되어야 하는 엔티티를 나타냅니다. 컴포넌트는 핸들을 통해 서로 리소스를 전달할 수 있습니다. 컴포넌트는 리소스의 소유권을 전달하거나, 리소스에 대한 소유권 없는 참조를 전달할 수 있습니다.

참조의 빌림(borrowing)과 소유권(ownership) 개념에 익숙하지 않다면 Rust 문서를 참조하십시오.

단순한 일반 데이터인 다른 WIT 타입과 달리, 리소스는 메서드를 통해서만 동작을 노출합니다. 리소스는 인터페이스를 구현하는 객체로 생각할 수 있습니다. (여기서 “인터페이스“는 WIT 인터페이스가 아니라 객체 지향 프로그래밍에서의 의미로 사용됩니다.)

예를 들어, blob(바이너리 대형 객체)을 리소스로 모델링할 수 있습니다. 다음 WIT는 생성자 하나, 메서드 두 개, 정적 함수 하나를 포함하는 blob 리소스 타입을 정의합니다:

resource blob {
    constructor(init: list<u8>);
    write: func(bytes: list<u8>);
    read: func(n: u32) -> list<u8>;
    merge: static func(lhs: blob, rhs: blob) -> blob;
}

blob 예제에서 보듯이, 리소스는 다음을 포함할 수 있습니다:

  • 메서드: 핸들인 self(this라고도 함) 매개변수를 암묵적으로 받는 함수입니다. (일부 프로그래밍 언어는 self 대신 this 키워드를 사용합니다.) read와 write는 메서드입니다.
  • 정적 함수: 암묵적인 self 매개변수가 없지만 C++나 Java의 정적 함수와 유사하게 리소스 타입의 스코프 안에 중첩되도록 의도된 함수입니다. merge는 정적 함수입니다.
  • 최대 하나의 생성자: 자신을 포함하는 리소스 타입의 핸들을 반환하는 함수에 대한 문법적 설탕(syntactic sugar)인 함수입니다. 생성자는 constructor로 선언합니다.

메서드는 빌린(borrowed) self 매개변수를 받는 함수로 다시 작성할 수 있고, 생성자는 호출자가 소유하는 값을 반환하는 함수로 다시 작성할 수 있습니다. 예를 들어, 위의 blob 리소스는 다음과 같이 근사할 수 있습니다:

resource blob;
blob-constructor: func(bytes: list<u8>) -> blob;
blob-write: func(self: borrow<blob>, bytes: list<u8>);
blob-read: func(self: borrow<blob>, n: u32) -> list<u8>;
blob-merge: static func(lhs: blob, rhs: blob) -> blob;

resource 타입 이름을 borrow<...>로 감싸면 “빌린(borrowed)” 리소스를 나타냅니다. 빌린 리소스는 호출이 진행되는 동안 호출자가 피호출자에게 리소스를 일시적으로 빌려주는 것을 나타냅니다. 반면, 소유된(owned) 리소스의 소유자가 해당 리소스를 드롭(drop)하면 리소스는 파기됩니다. (리소스를 드롭한다는 것은 기반 프로그래밍 언어가 지원하는 경우 명시적으로 드롭하거나, 소유권을 다른 함수로 이전하지 않고 반환하는 것을 의미합니다.)

더 정확히 말하면, 이들은 리소스의 빌린 handles 또는 소유된 handles입니다. handles에 대한 자세한 내용은 업스트림 컴포넌트 모델 명세를 참조하십시오.

플래그

flags 타입은 이름이 붙은 불리언의 집합입니다.

flags allowed-methods {
    get,
    post,
    put,
    delete,
}

flags 타입은 논리적으로 각 필드가 bool 타입인 레코드 타입과 동등하지만, 바이너리 수준에서는 더 효율적으로(비트필드로) 표현됩니다.

타입 별칭

type ... = ...을 사용하여 새로운 타입 별칭을 정의할 수 있습니다. 타입 별칭은 타입에 더 짧거나 더 의미 있는 이름을 부여할 때 유용합니다:

type buffer = list<u8>;
type http-result = result<http-response, http-error>;

함수

함수는 이름과 함수 타입으로 정의됩니다. 레코드 필드와 마찬가지로, 이름과 타입은 콜론으로 구분됩니다:

do-nothing: func();

함수 타입은 func 키워드 뒤에 괄호로 묶이고 쉼표로 구분된 매개변수(이름과 타입) 목록이 오는 형태입니다. 함수가 값을 반환하는 경우, 화살표 기호(->) 뒤에 반환 타입을 붙여 표현합니다:

// This function does not return a value
print: func(message: string);

// These functions return values
add: func(a: u64, b: u64) -> u64;
lookup: func(store: kv-store, key: string) -> option<string>;

여러 값을 반환하는 함수를 표현하려면 임의의 복합 타입(튜플이나 레코드 등)을 사용할 수 있습니다.

get-customers-paged: func(cont: continuation-token) -> tuple<list<customer>, continuation-token>;

함수는 인터페이스 안에 선언하거나, 월드에서 임포트 또는 익스포트로 선언할 수 있습니다.

비동기 함수

함수를 async로 선언할 수 있으며, 이는 호출이 결과를 생성하기 전에 일시 중단될 수 있음을 나타냅니다:

// An async function returning a result
handle: async func(request: request) -> result<response, error-code>;

스케줄링은 런타임이 담당하며, WIT 관점에서 이 호출은 일반 함수처럼 보입니다. 바인딩 생성기는 각 측을 호스트 언어의 자연스러운 비동기 관용구로 생성합니다. Rust에서는 async fn, JavaScript에서는 Promise를 반환하는 함수 등입니다.

비동기 함수는 stream<T> 및 future<T>와 함께 WASI 0.3에 추가되었습니다. 근본적인 동기에 대해서는 비동기, 스트림, 퓨처를 참조하십시오.

인터페이스

인터페이스는 이름이 붙은 타입과 함수의 집합으로, 중괄호로 묶이며 interface 키워드로 시작합니다:

interface canvas {
    type canvas-id = u64;

    record point {
        x: u32,
        y: u32,
    }

    draw-line: func(canvas: canvas-id, from: point, to: point);
}

인터페이스 안의 타입과 함수는 쉼표로 구분되지 않는다는 점에 유의하십시오.

다른 곳의 정의 사용하기

인터페이스는 use 지시문을 통해 다른 인터페이스에 선언된 타입을 재사용할 수 있습니다. use 지시문에는 타입이 선언된 인터페이스, 점, 그리고 재사용할 타입들을 중괄호로 묶은 목록을 차례로 지정해야 합니다. 그러면 해당 인터페이스는 use에 명시된 타입을 참조할 수 있습니다.

interface types {
    type dimension = u32;
    record point {
        x: dimension,
        y: dimension,
    }
}

interface canvas {
    use types.{dimension, point};
    type canvas-id = u64;
    draw-line: func(canvas: canvas-id, from: point, to: point, thickness: dimension);
}

canvas 인터페이스는 types 인터페이스에 선언된 dimension 및 point 타입을 사용합니다.

타입을 하나만 사용하더라도 반드시 중괄호로 묶어야 합니다. 예를 들어, use types.{dimension}은 올바르지만 use types.dimension은 올바르지 않습니다.

파일들이 같은 패키지(사실상 같은 디렉터리)에 있는 한 이는 여러 파일에 걸쳐서도 동작합니다. 다른 패키지의 정의를 사용하는 방법에 대해서는 명세를 참조하십시오.

월드

대략적으로 말해, 월드는 컴포넌트의 계약을 기술합니다. 월드는 임포트와 익스포트의 집합을 기술하며, 중괄호로 묶이고 world 키워드로 시작합니다. 임포트와 익스포트는 인터페이스일 수도 있고 특정 함수일 수도 있습니다. 익스포트는 컴포넌트가 제공하는 인터페이스나 함수를 기술합니다. 임포트는 컴포넌트가 의존하는 인터페이스나 함수를 기술합니다.

interface printer {
    print: func(text: string);
}

interface error-reporter {
    report-error: func(error-message: string);
}

world multi-function-device {
    // The component implements the `printer` interface
    export printer;

    // The component implements the `scan` function
    export scan: func() -> list<u8>;

    // The component needs to be supplied with an `error-reporter`
    import error-reporter;
}

이 코드는 multi-function device라는 월드를 정의하며, 이 월드에는 printer 인터페이스와 scan 함수라는 두 개의 익스포트가 있습니다. 익스포트된 printer 인터페이스는 같은 파일에 정의되어 있습니다. 임포트된 error-reporter 인터페이스 또한 같은 파일에 정의되어 있습니다. error-reporter 인터페이스를 보면, 월드가 인터페이스를 임포트할 때 인터페이스의 이름만이 아니라 타입과 함수 선언을 포함한 전체 인터페이스를 제공해야 한다는 것을 알 수 있습니다.

다른 패키지의 인터페이스

다른 패키지에 정의된 인터페이스를 임포트하고 익스포트하려면 package/name 구문을 사용할 수 있습니다:

world http-proxy {
    export wasi:http/incoming-handler;
    import wasi:http/outgoing-handler;
}

이 예제에서 보듯이, 임포트와 익스포트는 패키지 수준이 아니라 인터페이스 수준에서 적용됩니다. 한 패키지에 정의된 인터페이스 하나를 임포트하면서, 같은 패키지에 정의된 다른 인터페이스를 익스포트할 수 있습니다. 패키지는 정의들을 한데 묶을 뿐이며, 일관된 동작의 집합을 기술하지는 않습니다.

WIT는 패키지를 해석하는 방법을 정의하지 않으므로, 도구마다 서로 다른 방식으로 패키지를 해석할 수 있습니다.

인라인 인터페이스

인터페이스는 월드 안에 인라인으로 선언할 수 있습니다:

world toy {
    export example: interface {
        do-nothing: func();
    }
}

다른 월드 포함하기

다른 월드를 include할 수 있습니다. 이렇게 하면 여러분의 월드는 해당 월드의 모든 익스포트를 익스포트하고, 해당 월드의 모든 임포트를 임포트하게 됩니다.

world glow-in-the-dark-multi-function-device {
    // The component provides all the same exports, and depends on
    // all the same imports, as a `multi-function-device`...
    include multi-function-device;

    // ...but also exports a function to make it glow in the dark
    export glow: func(brightness: u8);
}

use 지시문과 마찬가지로, 다른 패키지의 월드를 include할 수 있습니다.

패키지

패키지는 인터페이스와 월드의 집합이며, 같은 디렉터리에 있는 여러 파일에 걸쳐 정의될 수 있습니다. 각 WIT 파일은 하나의 패키지와 연결되며, 이 패키지는 해당 파일에서 직접 선언되거나 같은 디렉터리에 있는 다른 파일에서 선언됩니다.

패키지를 선언하려면 package 지시문을 사용하여 패키지 ID를 지정하십시오. 패키지 ID는 콜론으로 구분된 네임스페이스와 이름을 반드시 포함해야 하며, 선택적으로 semver를 준수하는 버전 번호를 포함할 수 있습니다:

package documentation:example;
package documentation:example@1.0.1;

모든 파일은 .wit 확장자를 가져야 하며 같은 디렉터리에 있어야 합니다. 패키지가 여러 파일에 걸쳐 있는 경우 하나의 파일에만 패키지 선언이 있으면 되지만, 여러 파일에 패키지 선언이 있다면 패키지 ID가 모두 서로 일치해야 합니다. 예를 들어, 다음 documentation:http 패키지는 네 개의 파일에 걸쳐 있습니다:

// types.wit
interface types {
    record request { /* ... */ }
    record response { /* ... */ }
}

// incoming.wit
interface incoming-handler {
    use types.{request, response};
    // ...
}

// outgoing.wit
interface outgoing-handler {
    use types.{request, response};
    // ...
}

// http.wit
package documentation:http@1.0.0;

world proxy {
    export incoming-handler;
    import outgoing-handler;
}

이 패키지는 types.wit에서 요청 및 응답 타입을 정의하고, incoming.wit에서 수신 핸들러 인터페이스를 정의하고, outgoing.wit에서 발신 핸들러 인터페이스를 정의하며, http.wit에서 패키지를 선언하고 이러한 인터페이스를 사용하는 월드를 정의합니다.

WIT 언어에 대한 보다 공식적인 정의는 WIT 명세를 참고하십시오.


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