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 코어 모듈을 기반으로 언어와 라이브러리 간의 상호 운용성을 향상시킵니다. 이를 위해 모듈 간 상호 작용의 안전성을 검사하는 데 사용되는 타입 시스템을 풍부하게 하고, 별도로 컴파일된 모듈 간의 저수준 호출 규약을 명확하게 정의하고 강제합니다. 코어 모듈의 한계가 무엇인지 이해하기 위해, 먼저 코어 모듈을 정의하는 것부터 시작합니다.

WebAssembly 코어 모듈

모듈은 WebAssembly 코어 명세에 정의되어 있습니다.

WebAssembly 프로그램은 직접 손으로 작성할 수도 있지만, Rust, C, Go, JavaScript, Python과 같은 상위 수준 프로그래밍 언어를 사용하여 WebAssembly 프로그램을 빌드할 가능성이 더 높습니다. 현재 많은 기존 툴체인은 단일 바이너리 .wasm 파일인 WebAssembly 코어 모듈을 생성합니다.

코어 모듈은 일반적으로 단일 바이너리 .wasm 파일에 대응합니다. 다음은 샘플 .wasm 파일에 대해 file 명령이 출력하는 내용입니다:

$ file adder.wasm
adder.wasm: WebAssembly (wasm) binary module version 0x1 (MVP)

이 파일은 wasm-tools CLI를 사용하여 검사할 수도 있습니다:

$ wasm-tools print adder.wasm | head -1
(module

코어 모듈은 정의들의 집합입니다. 정의의 종류는 다음과 같습니다:

  • 함수는 실행 가능한 코드 단위(인수의 이름, 인수와 반환 값의 타입에 대한 선언과 함께 구성된 명령어 시퀀스)를 정의합니다.
  • 선형 메모리는 명령어로 읽고 쓸 수 있는, 해석되지 않은 바이트의 버퍼를 정의합니다.
  • 임포트는 모듈 내 함수를 실행하는 데 사용 가능해야 하는 다른 모듈의 이름을, 임포트된 모듈에서 필요한 함수의 타입 시그니처와 함께 정의합니다.
  • 익스포트는 모듈 내 함수 중 외부에서 접근 가능해야 하는 함수의 이름을 정의합니다.
  • 그 밖에도 여러 가지가 있습니다. 전체 목록은 코어 명세를 참조하십시오.

컴파일된 코어 모듈은 때때로 “WebAssembly 바이너리“라고 불리며, 일반적으로 단일 .wasm 파일에 대응합니다. 이러한 모듈은 브라우저에서 실행하거나, Wasmtime 또는 WAMR과 같은 별도의 런타임을 통해 실행할 수 있습니다.

코어 모듈의 한계

코어 모듈은 수행할 수 있는 계산과 그 기능을 외부 세계에 노출하는 방식이 제한되어 있습니다. WebAssembly 코어 모듈에서 함수는 본질적으로 정수(i32 또는 i64) 또는 부동소수점(f32 또는 f64) 타입만 사용하도록 제한됩니다. 이러한 타입만 함수의 인수로 전달할 수 있으며, 이러한 타입만 함수의 결과로 반환될 수 있습니다. 고수준 프로그래밍 언어에서 흔히 사용되는 문자열, 리스트, 배열, 열거형(enumeration), 구조체(레코드)와 같은 복합 타입은 정수와 부동소수점 수로 표현해야 합니다.

예를 들어, 함수가 문자열을 받으려면 문자열 인수를 두 개의 별도 인수, 즉 메모리에 대한 정수 오프셋과 문자열의 길이를 나타내는 정수로 표현할 수 있습니다. (선형) 메모리는 모듈 내에서 선언된, 초기화되지 않은 바이트 영역이라는 점을 상기하십시오.

의사 코드로 표현하면, 문자열을 조작하는 함수의 타입 시그니처는 다음과 같을 수 있습니다:

remove-duplicates: func(offset: i32, length: i32) -> [i32, i32]

remove-duplicates가 인수에 포함된 고유한 문자로 구성된 새 문자열을 생성하는 함수라고 가정합니다. 반환 타입은 두 개의 32비트 정수로 이루어진 리스트입니다. 첫 번째 정수는 모듈이 선언한 선형 메모리 중 하나에 대한 오프셋(새로 할당된 문자열이 시작되는 위치)이며, 두 번째 정수는 문자열의 길이입니다. 함수를 호출한 후, 호출자는 반환된 오프셋과 길이를 사용하여 해당 선형 메모리에 접근해 출력 문자열을 읽어야 합니다.

이것이 동작하려면 remove-duplicates 함수를 정의하는 모듈에 인수 문자열과 결과 문자열에 사용할 메모리를 익스포트하는 익스포트 선언도 포함되어야 합니다. 의사 코드:

export "string_mem" (mem 1)

그리고 remove-duplicates 함수를 사용하는 모듈은 이 메모리를 임포트해야 합니다. 의사 코드:

import "strings" "string_mem"

(가져오는 쪽에서도 가져오는 메모리의 크기를 선언해야 하므로, 이 의사 코드는 여전히 단순화된 것입니다.)

반환된 길이와 반환된 오프셋은 둘 다 정수이므로, 타입 시스템에는 둘이 혼동되는 것을 막는 장치가 전혀 없다는 점에 유의하십시오. 또한 입력 문자열과 출력 문자열에 사용되는 메모리의 이름은 관례로 정해야 하며, (오프셋과 길이의 합이 범위 안에 있는 한) 클라이언트 코드가 다른 메모리를 인덱싱하는 것을 막는 장치도 타입 시스템에는 전혀 없습니다.

우리는 다음과 같은 의사 코드 타입 시그니처를 작성하는 편을 선호합니다:

remove-duplicates: func(s: string) -> string

메모리 익스포트와 임포트를 완전히 없앨 수 있습니다.

복잡함은 여기서 끝나지 않습니다! 데이터 표현은 프로그래밍 언어마다 고유한 경우가 많습니다. 예를 들어 C의 문자열은 Rust나 JavaScript의 문자열과 완전히 다르게 표현됩니다. 게다가 이 방식이 동작하려면 모듈이 메모리를 임포트하고 익스포트해야 하는데, 언어마다 메모리 레이아웃에 대해 서로 다른 가정을 하기 때문에 오류가 발생하기 쉽습니다.

서로 다른 언어로 작성된 WebAssembly 모듈이 원활하게 상호 운용되려면, 이러한 더 풍부한 타입을 모듈 경계를 넘어 노출하는 합의된 방법이 필요합니다.

컴포넌트

컴포넌트는 지금까지 살펴본 두 가지 문제, 즉 코어 모듈 함수의 제한적인 타입 시스템과 언어 간 상호 운용성 문제를 해결합니다. 개념적으로 컴포넌트는 모듈의 가져온 함수와 내보낸 함수를 통해서만 상호 작용하도록 제한된 WebAssembly 바이너리(모듈을 포함할 수도 있고 포함하지 않을 수도 있음)입니다. 컴포넌트는 다른 바이너리 형식을 사용합니다:

$ file add.component.wasm
add.component.wasm: WebAssembly (wasm) binary module version 0x1000d

wasm-tools CLI로 파일을 검사하면 파일에 컴포넌트가 포함되어 있음을 더 분명하게 확인할 수 있습니다:

$ wasm-tools print add.component.wasm | head -1
(component

코어 모듈과 비교하여, 컴포넌트는 함수의 타입을 표현하기 위해 기본적으로 더 풍부한 메커니즘인 인터페이스도 사용합니다.

인터페이스

인터페이스는 WebAssembly 인터페이스 타입(WIT)이라는 별도의 언어로 표현됩니다. 인터페이스에는 타입의 정의와 함수의 타입 시그니처가 포함됩니다. 타입의 비트 수준 표현은 정규 ABI(Application Binary Interface, 애플리케이션 바이너리 인터페이스)에 의해 명시됩니다. 인터페이스와 정규 ABI는 함께 모듈 간의 저수준 호출 계약을 명확하게 정의하고 강제한다는 목표를 달성합니다.

상호 운용성

WebAssembly 코어 모듈은 이미 다양한 아키텍처와 운영 체제 간에 이식 가능합니다. 컴포넌트는 이러한 이점을 유지하면서, 컴포넌트 모델 ABI를 사용하여 다양한 프로그래밍 언어 간의 이식성을 더합니다. Go로 구현된 컴포넌트는 컴포넌트 모델 ABI의 공유 관례에 의존하여 C 또는 Rust 컴포넌트와 직접적이고 안전하게 통신할 수 있습니다. 컴포넌트를 작성할 때 의존하는 컴포넌트가 어떤 언어로 구현되었는지 알 필요조차 없으며, WIT로 표현된 컴포넌트 인터페이스만 알면 됩니다. 또한 컴포넌트는 한 컴포넌트의 익스포트가 다른 컴포넌트의 임포트를 충족하는 방식으로 더 큰 그래프로 합성될 수 있습니다.

컴포넌트 모델의 이점

모든 요소를 종합하면, 컴포넌트 모델은 WebAssembly 모듈을 캡슐화하는 바이너리 WebAssembly 형식을 도입합니다. 이 형식을 사용하면 WIT로 타입이 표현된 함수의 익스포트와 임포트를 통해서만 서로 상호 작용하는 WebAssembly 모듈을 구성할 수 있습니다.

컴포넌트 모델은 Wasm의 강력한 샌드박싱을 기반으로 더 많은 이점을 제공합니다. 풍부한 타입 덕분에 컴포넌트나 인터페이스가 무엇을 하는지 한눈에 파악하기 쉬우며, 어떤 나쁜 일이 일어날 수 없는지도 보장받을 수 있습니다. 더 풍부한 타입 시그니처는 정수와 부동소수점만으로 이루어진 타입 시그니처보다 더 풍부한 의미적 속성을 표현합니다. 컴포넌트 그래프 내의 관계는 정적으로 분석할 수 있습니다. 예를 들어, 비즈니스 로직을 포함한 컴포넌트가 개인 식별 정보를 포함한 컴포넌트에 접근할 수 없음을 검증할 수 있습니다.

또한 컴포넌트는 오직 자신의 임포트를 호출하고 자신의 익스포트가 호출되는 방식으로만 런타임이나 다른 컴포넌트와 상호작용합니다. 구체적으로, 코어 모듈과 달리 컴포넌트는 메모리를 익스포트할 수 없으므로, 자신의 메모리에 쓰고 다른 쪽이 그 메모리를 읽게 하는 방식으로 다른 쪽과 간접적으로 통신할 수 없습니다. 이는 샌드박싱을 강화할 뿐만 아니라 메모리에 대해 서로 다른 가정을 하는 언어 간의 상호 운용을 가능하게 합니다. 예를 들어, 가비지 컬렉션 메모리에 의존하는 컴포넌트가 기존의 선형 메모리를 사용하는 컴포넌트와 상호 운용할 수 있습니다.

컴포넌트 사용하기

이제 컴포넌트 모델이 어떻게 도움이 되는지 더 잘 이해하셨으니, 선호하는 언어로 컴포넌트를 빌드하는 방법을 살펴보십시오!

더 읽을거리

컴포넌트 모델이 만들어진 이유에 대한 더 많은 배경은 명세의 목표, 사용 사례, 설계 선택을 살펴보십시오.


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