> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-fix-nav-issues.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> WebAssembly 사용자 정의 함수 문서

# WebAssembly 사용자 정의 함수

export const ExperimentalBadge = () => {
  return <div className="experimentalBadge">
            <div className="experimentalIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.25" d="M5.5 2H10.5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M9.50015 2V6.19625L13.4283 12.7425C13.4738 12.8183 13.4985 12.9049 13.4996 12.9934C13.5008 13.0818 13.4785 13.169 13.435 13.246C13.3914 13.323 13.3283 13.3871 13.2519 13.4317C13.1755 13.4764 13.0886 13.4999 13.0002 13.5H3.00015C2.91164 13.5 2.8247 13.4766 2.74822 13.432C2.67174 13.3874 2.60847 13.3233 2.56487 13.2463C2.52126 13.1693 2.49889 13.082 2.50004 12.9935C2.50119 12.905 2.52582 12.8184 2.5714 12.7425L6.50015 6.19625V2" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.25" d="M4.47656 9.56754C5.30344 9.41254 6.47656 9.47942 7.99969 10.25C10.0153 11.2707 11.4216 11.0569 12.2184 10.7282" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
        </div>
            Experimental feature. <u><a href="/docs/beta-and-experimental-features#experimental-features">Learn more.</a></u>
        </div>;
};

export const CloudNotSupportedBadge = () => {
  return <div className="cloudNotSupportedBadge">
            <div className="cloudNotSupportedIcon">
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                <path strokeWidth="1.5" d="M6.33366 12.6666L12.3739 12.6667C13.6593 12.6667 14.7073 11.6187 14.7073 10.3334C14.7073 9.04804 13.6593 8.00003 12.3739 8.00003C12.3739 8.00003 12.3337 7.66659 12.0003 7.33325M10.667 5.33322C8.00033 2.33325 4.45395 4.78537 4.14195 6.68203C2.55728 6.7627 1.29395 8.06203 1.29395 9.6667C1.29395 11.3234 2.66699 12.6666 4.00033 12.6666" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
                <path strokeWidth="1.5" d="M2.66699 14L12.0003 4.66663" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" />
            </svg>

        </div>
            Not supported in ClickHouse Cloud
        </div>;
};

ClickHouse는 WebAssembly로 작성된 사용자 정의 함수(UDF) 생성 기능을 지원합니다. 이를 통해 Rust, C, C++ 등으로 작성한 사용자 정의 로직을 WebAssembly 모듈로 컴파일해 실행할 수 있습니다.

<div id="overview">
  ## 개요
</div>

WebAssembly 모듈은 ClickHouse에서 호출할 수 있는 하나 이상의 함수를 포함하는 컴파일된 바이너리 파일입니다.
모듈은 한 번 로드해 여러 번 재사용하는 라이브러리나 공유 객체로 생각하면 됩니다.

UDF를 포함하는 WebAssembly 모듈은 Rust, C, C++처럼 WebAssembly로 컴파일할 수 있는 모든 언어로 작성할 수 있습니다.

WebAssembly로 컴파일된 코드("guest" 코드)와 ClickHouse에서 실행되는 코드("host")는 전용 메모리 공간에만 접근할 수 있는 샌드박스 환경에서 실행됩니다.

게스트 코드는 ClickHouse가 호출할 수 있는 함수를 export합니다. 여기에는 사용자 지정 로직을 구현하는 함수(UDF 정의에 사용됨)뿐 아니라 메모리 관리와 ClickHouse와 WebAssembly 코드 간 데이터 교환에 필요한 지원 함수도 포함됩니다.

코드는 운영 체제나 표준 라이브러리에 의존하지 않는 "freestanding" WebAssembly(즉, `wasm32-unknown-unknown`)로 컴파일해야 합니다. 또한 기본 32비트 WebAssembly 대상만 지원됩니다(`wasm64` 확장은 지원되지 않음).
모듈은 ClickHouse와 상호작용하기 위해 지원되는 통신 프로토콜(ABI) 중 하나를 따라야 합니다.

컴파일이 완료되면 모듈의 바이너리 코드는 `system.webassembly_modules` 테이블에 삽입되어 ClickHouse에 로드됩니다.
그 후 `CREATE FUNCTION ... LANGUAGE WASM` 구문을 사용해 모듈이 내보낸 함수를 참조하는 UDF를 생성할 수 있습니다.

<div id="prerequisites">
  ## 사전 요구 사항
</div>

ClickHouse 구성에서 WebAssembly 지원을 사용 설정하세요:

```xml theme={null}
<clickhouse>
    <allow_experimental_webassembly_udf>true</allow_experimental_webassembly_udf>
    <webassembly_udf_engine>wasmtime</webassembly_udf_engine>
</clickhouse>
```

사용 가능한 Engine 구현:

* `wasmtime` (기본값, 권장) — [WasmTime](https://github.com/bytecodealliance/wasmtime)을 사용합니다
* `wasmedge` — [WasmEdge](https://github.com/WasmEdge/WasmEdge)를 사용합니다

<div id="quick-start">
  ## 빠른 시작
</div>

이 예시는 [콜라츠 추측](https://en.wikipedia.org/wiki/Collatz_conjecture) 계산기를 구현하여 WebAssembly UDF를 만드는 전체 워크플로를 보여줍니다.

이 단계에서는 프로그래밍 언어가 필요하지 않으므로, WebAssembly를 사람이 읽을 수 있는 형태로 표현한 WebAssembly Text 포맷(WAT)으로 코드를 작성합니다.
ClickHouse는 모듈이 바이너리 형식이어야 하므로, 트랜스파일러를 사용해 WAT를 WASM으로 변환합니다.
이 변환을 수행하려면 [WebAssembly Binary Toolkit (WABT)](https://github.com/WebAssembly/wabt)의 `wat2wasm` 또는 [wasm-tools](https://github.com/bytecodealliance/wasm-tools)의 `parse` 명령을 사용할 수 있습니다.

```bash theme={null}
cat << 'EOF' | wasm-tools parse | clickhouse client -q "INSERT INTO system.webassembly_modules (name, code) SELECT 'collatz', code FROM input('code String') FORMAT RawBlob"
(module
  (func $next (param $n i32) (result i32)
    local.get $n i32.const 1 i32.and
    (if (result i32)
      (then local.get $n i32.const 3 i32.mul i32.const 1 i32.add)
      (else local.get $n i32.const 2 i32.div_u)))
  (func $steps (export "steps") (param $n i32) (result i32)
    (local $count i32)
    local.get $n i32.const 1 i32.lt_u
    (if (then i32.const 0 return))
    (block $done (loop $loop
      local.get $n i32.const 1 i32.eq br_if $done
      local.get $n call $next local.set $n
      local.get $count i32.const 1 i32.add local.set $count
      br $loop))
    local.get $count)
)
EOF
```

위 스니펫에서는 바이너리 WASM 코드를 `FORMAT RawBlob`을 사용해 ClickHouse client로 직접 파이프하여 `system.webassembly_modules` 테이블에 삽입합니다.

그런 다음 모듈이 내보내는 `steps` 함수를 참조하는 UDF를 정의합니다:

```sql theme={null}
CREATE FUNCTION collatz_steps LANGUAGE WASM ARGUMENTS (n UInt32) RETURNS UInt32 FROM 'collatz' :: 'steps';
```

UDF 이름과 다르므로 `::` 뒤에는 모듈의 함수 이름을 지정한다는 점에 유의하십시오.

이제 쿼리에서 `collatz_steps` 함수를 사용할 수 있습니다:

```sql theme={null}
SELECT groupArray(collatz_steps(number :: UInt32))
FROM numbers(1, 100)
FORMAT TSV
```

`number` 컬럼은 `UInt32`로 명시적으로 CAST됩니다. 이는 WebAssembly 함수가 `CREATE FUNCTION` 문에 지정된 시그니처와 정확히 일치하는 타입을 요구하기 때문입니다.

그 결과, 1부터 100까지의 수에 대한 Collatz 단계 수열을 얻을 수 있으며, 이는 [OEIS의 A006577 수열](https://oeis.org/A006577)에 해당합니다.

```text theme={null}
[0,1,7,2,5,8,16,3,19,6,14,9,9,17,17,4,12,20,20,7,7,15,15,10,23,10,111,18,18,18,106,5,26,13,13,21,21,21,34,8,109,8,29,16,16,16,104,11,24,24,24,11,11,112,112,19,32,19,32,19,19,107,107,6,27,27,27,14,14,14,102,22,115,22,14,22,22,35,35,9,22,110,110,9,9,30,30,17,30,17,92,17,17,105,105,12,118,25,25,25]
```

<div id="manage-wasm-modules-via-system-table">
  ## 시스템 테이블을 통해 WASM 모듈 관리
</div>

WebAssembly 모듈은 다음 구조를 가진 `system.webassembly_modules` 테이블에 저장됩니다:

* **컬럼**
  * `name` String — 모듈 이름입니다. 비어 있을 수 없으며, 영문자, 숫자, 밑줄 문자만 사용할 수 있습니다.
  * `code` String — 원시 바이너리 WASM 코드입니다. 쓰기 전용이며, 읽기 시 빈 문자열이 반환됩니다.
  * `hash` UInt256 — 모듈 바이너리의 SHA256입니다(디스크에는 존재하지만 아직 로드되지 않은 경우 0).

모듈 관리는 이 테이블에 대한 표준 SQL 작업을 통해 수행됩니다:

<div id="insert-a-module">
  ### 모듈 삽입
</div>

```sql theme={null}
INSERT INTO system.webassembly_modules (name, code)
SELECT 'my_module', base64Decode('AGFzbQEAAAA...');
```

필요한 경우 무결성 해시를 제공할 수 있습니다:

```sql theme={null}
INSERT INTO system.webassembly_modules (name, code, hash)
SELECT 'my_module', base64Decode('...'), reinterpretAsUInt256(unhex('369f...c57d'));
```

제공된 해시가 모듈 코드에서 계산된 SHA256과 일치하지 않으면 삽입이 실패합니다. S3 또는 HTTP 같은 외부 소스에서 모듈을 로드할 때 유용합니다.

<div id="list-modules">
  ### 모듈 목록 보기
</div>

```sql theme={null}
SELECT name, lower(hex(reinterpretAsFixedString(hash))) AS sha256 FROM system.webassembly_modules

   ┌─name────┬─sha256───────────────────────────────────────────────────────────┐
1. │ collatz │ a084a10b7b5cb07db198bc93bf1f3c1f8cb8ef279df7a4f6b66b1cdd55d79c48 │
   └─────────┴──────────────────────────────────────────────────────────────────┘
```

<div id="delete-a-module">
  ### 모듈 삭제
</div>

삭제는 `DELETE FROM system.webassembly_modules WHERE name = '...'` 문을 사용해 수행합니다.
프레디케이트는 정확한 일치에는 `name = 'literal'`, 패턴과 일치하는 모든 모듈을 삭제할 때는 `name LIKE 'pattern'`여야 하며, 그 밖의 형태는 허용되지 않습니다.

```sql theme={null}
DELETE FROM system.webassembly_modules WHERE name = 'collatz';

-- 이름이 `tmp_`로 시작하는 모든 모듈을 일괄 삭제합니다 (리터럴 밑줄은 `\_`로 이스케이프 처리됩니다):
DELETE FROM system.webassembly_modules WHERE name LIKE 'tmp\_%';
```

기존 UDF 중 일치하는 모듈을 참조하는 UDF가 있으면 삭제에 실패하므로, 먼저 해당 UDF를 삭제해야 합니다.

<div id="create-a-webassembly-udf">
  ## WebAssembly UDF 생성하기
</div>

**구문**:

```sql theme={null}
CREATE [OR REPLACE] FUNCTION function_name
LANGUAGE WASM
FROM 'module_name' [:: 'source_function_name']
ARGUMENTS ( [name type[, ...]] | [type[, ...]] )
RETURNS return_type
[ABI ROW_DIRECT | ABI BUFFERED_V1]
[DETERMINISTIC]
[SHA256_HASH 'hex']
[SETTINGS key = value[, ...]];
```

**매개변수**:

* `function_name`: ClickHouse에서의 함수 이름입니다. 모듈의 내보낸 함수 이름과 다를 수 있습니다.
* `FROM 'module_name' :: 'source_function_name'`: 사용할 로드된 WASM 모듈 이름과 WASM 모듈 내 함수 이름입니다(기본값은 function\_name).
* `ARGUMENTS`: 인수 이름과 타입 목록입니다(이름은 선택 사항이며, 이름 있는 필드를 지원하는 직렬화 포맷에서 사용됩니다).
* `ABI`: Application Binary Interface 버전입니다.
  * `ROW_DIRECT`: 직접 타입 매핑, 행 단위 처리
  * `BUFFERED_V1`: 직렬화를 사용하는 블록 기반 처리
* `DETERMINISTIC`: 함수를 결정적으로 선언합니다 — 동일한 입력에 대해 항상 동일한 출력을 반환합니다. 지정하면 모든 인수가 상수인 호출에 대해 ClickHouse가 상수 폴딩을 수행할 수 있습니다. 함수는 쿼리 분석 시점에 한 번 평가되며, 결과는 모든 행에 재사용됩니다.
* `SHA256_HASH`: 검증에 사용할 예상 모듈 해시입니다(생략하면 자동으로 채워짐). 서로 다른 레플리카에서 올바른 WASM 모듈이 로드되었는지 확인하는 데 사용할 수 있습니다.
* `SETTINGS`: 함수별 설정
  * `serialization_format` String — ABI에 필요할 때 사용하는 직렬화 포맷입니다. 기본값: `MsgPack`.

<div id="abis-versions">
  ## ABI 버전
</div>

ClickHouse와 상호작용하려면 WebAssembly 모듈이 지원되는 ABI(Application Binary Interface) 중 하나를 따라야 합니다.

* `ROW_DIRECT`: 직접 타입 매핑(기본 타입 `Int32`, `UInt32`, `Int64`, `UInt64`, `Float32`, `Float64`만 지원)
* `BUFFERED_V1`: 직렬화를 사용하는 복합 타입

<div id="abi-row_direct">
  ### ABI ROW\_DIRECT
</div>

내보낸 WASM 함수를 각 행에 대해 직접 호출합니다.

* 인수와 반환 타입은 숫자 타입 `Int32/UInt32/Int64/UInt64/Float32/Float64/Int128/UInt128`입니다.
* 이 ABI에서는 문자열을 지원하지 않습니다.
* 시그니처는 WASM 내보내기(`i32/i64/f32/f64/v128`)와 일치해야 합니다.
* 모듈에서 내보내야 하는 지원 함수는 필요하지 않습니다.

예를 들어, 다음과 같은 시그니처를 가진 함수가 있습니다:

```
(func (param i32 i64 f32) (result f64) ...)
```

다음과 같이 만들 수 있습니다:

```sql theme={null}
CREATE FUNCTION my_func ARGUMENTS (Int32, UInt64, Float32) RETURNS Float64 ...
```

WebAssembly는 부호 있는 인수와 부호 없는 인수를 구분하지 않으며, 대신 값을 해석할 때 서로 다른 명령어를 사용합니다. 따라서 인수의 크기는 정확히 일치해야 하고, 부호 여부는 함수 내부에서 수행하는 연산에 따라 결정됩니다.

<div id="abi-buffered_v1">
  ### ABI BUFFERED\_V1
</div>

<Note>
  이 ABI는 실험적이며 향후 릴리스에서 변경될 수 있습니다.
</Note>

WASM 메모리를 통한 (역)직렬화를 사용해 전체 블록을 한 번에 처리합니다. 모든 인수 및 반환 타입을 지원합니다.

직렬화된 데이터는 wasm 메모리에 복사되며, 버퍼를 가리키는 포인터(데이터 포인터와 데이터 크기로 구성됨)가 입력 행 수와 함께 UDF 함수에 전달됩니다. 따라서 WASM에서 실행되는 사용자 정의 함수는 항상 두 개의 `i32` 인수를 받아 하나의 `i32` 값을 반환합니다.
게스트 코드는 데이터를 처리한 후 직렬화된 결과 데이터가 들어 있는 결과 버퍼를 가리키는 포인터를 반환합니다.

게스트 코드는 이러한 버퍼를 생성하고 해제하는 두 개의 함수를 제공해야 합니다.

```
(module
  ;; 지정된 크기의 새 버퍼를 할당합니다
  ;; 반환값: Buffer 구조체에 대한 핸들 (직접 데이터 포인터가 아님!) - 데이터 포인터와 크기를 포함
  (func (export "clickhouse_create_buffer")
    (param $size i32)    ;; 할당할 데이터 크기
    (result i32))        ;; 충분한 공간을 가진 버퍼 핸들 반환

  ;; 핸들로 버퍼를 해제합니다
  (func (export "clickhouse_destroy_buffer")
    (param $handle i32)  ;; 해제할 버퍼 핸들
    (result))            ;; 반환값 없음

    ;; 사용자 정의 함수
    (func (export "user_defined_function1")
      (param $input_buffer_handle i32)  ;; 입력 버퍼 핸들
      (param $n i32)                    ;; 입력의 행 수
      (result i32))                     ;; 출력 버퍼 핸들 반환
)
```

C 정의 예시:

```c theme={null}
typedef struct {
    uint8_t * data;
    uint32_t size;
} ClickhouseBuffer;

ClickhouseBuffer * clickhouse_create_buffer(uint32_t size) { /* ... */ }

void clickhouse_destroy_buffer(ClickhouseBuffer * data) { /* ... */ }

/// 예시 사용자 정의 함수
ClickhouseBuffer * user_defined_function1(ClickhouseBuffer * span, uint32_t n) { /* ... */ }
ClickhouseBuffer * user_defined_function2(ClickhouseBuffer * span, uint32_t n) { /* ... */ }
```

<div id="note-for-developing-udfs-in-rust">
  ### Rust로 UDF를 개발할 때 참고할 사항
</div>

Rust 프로그램용으로는 ClickHouse의 WebAssembly UDF 개발을 간소화할 수 있도록 헬퍼 크레이트 [clickhouse-wasm-udf](https://crates.io/crates/clickhouse-wasm-udf)를 제공합니다. 이 크레이트는 메모리 관리 함수를 제공하므로 `clickhouse_create_buffer`와 `clickhouse_destroy_buffer` 함수를 직접 구현할 필요 없이 크레이트를 의존성으로 추가하면 됩니다. 또한 일반적인 Rust 함수를 필요한 ABI 형식으로 감싸는 매크로 `#[clickhouse_wasm_udf]`도 제공합니다.

이 크레이트를 사용하면 다음과 같이 UDF를 작성할 수 있습니다:

```rust theme={null}

use clickhouse_wasm_udf_bindgen::clickhouse_udf;

#[clickhouse_udf]
pub fn some_udf(data: String) -> HashMap<String, String> {
    // 여기에 구현 내용을 작성하세요
}

```

매크로는 버퍼 구조체를 인자로 받아 버퍼 구조체를 반환하는 래퍼 함수를 생성하고, `serde`를 사용해 직렬화/역직렬화를 자동으로 처리합니다.

<div id="host-api-available-to-modules">
  ## 모듈에서 사용할 수 있는 호스트 API
</div>

다음 호스트 함수는 import하여 모듈에서 사용할 수 있습니다:

* `clickhouse_server_version() -> i64` — ClickHouse 서버 버전을 정수로 반환합니다(예: v25.11.1.1은 25011001).
* `clickhouse_throw(ptr: i32, size: i32)` — 지정된 메시지로 오류를 발생시킵니다. 오류 메시지 문자열이 저장된 메모리 위치의 포인터와 문자열 크기를 인수로 받습니다.
* `clickhouse_log(ptr: i32, size: i32)` — 메시지를 ClickHouse 서버 텍스트 로그에 기록합니다.
* `clickhouse_random(ptr: i32, size: i32)` — 메모리를 무작위 바이트로 채웁니다.

<div id="settings">
  ## 설정
</div>

다음 쿼리 수준 설정은 WebAssembly UDF 실행을 제어합니다:

* `webassembly_udf_max_fuel` — WebAssembly UDF 인스턴스 실행당 연료 한도입니다. 각 WebAssembly 명령어는 일정량의 연료를 소비합니다. 제한을 두지 않으려면 0으로 설정합니다.

* `webassembly_udf_max_memory` — WebAssembly UDF 인스턴스당 바이트 단위의 메모리 한도입니다.

* `webassembly_udf_max_input_block_size` — 단일 블록에서 WebAssembly UDF에 전달되는 최대 행 수입니다. 모든 행을 한 번에 처리하려면 0으로 설정합니다.

* `webassembly_udf_max_instances` — 함수별로 병렬 실행할 수 있는 WebAssembly UDF 인스턴스의 최대 개수입니다.

예시 사용법:

```sql theme={null}
SET webassembly_udf_max_fuel = 200000;
SELECT my_wasm_udf(column) FROM table;
```

<div id="see-also">
  ## 관련 항목
</div>

* [ClickHouse UDF 개요](/ko/reference/functions/regular-functions/udf)
