> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Protobuf 형식에 대한 문서

# Protobuf

| 입력 | 출력 | 별칭 |
| -- | -- | -- |
| ✔  | ✔  |    |

<div id="description">
  ## 설명
</div>

`Protobuf` 형식은 [Protocol Buffers](https://protobuf.dev/) 형식입니다.

이 포맷을 사용하려면 외부 포맷 스키마가 필요하며, 이 스키마는 쿼리 간에 캐시됩니다.

ClickHouse는 다음을 지원합니다:

* `proto2` 및 `proto3` 구문
* `Repeated`/`optional`/`required` 필드

테이블 컬럼과 Protocol Buffers 메시지 타입의 필드가 어떻게 대응되는지 확인하기 위해 ClickHouse는 이름을 비교합니다.
이 비교는 대소문자를 구분하지 않으며, 문자 `_`(밑줄)와 `.`(점)은 동일한 것으로 간주됩니다.
컬럼과 Protocol Buffers 메시지의 필드 타입이 다르면 필요한 변환이 적용됩니다.

중첩된 메시지도 지원합니다. 예를 들어, 다음 메시지 타입의 필드 `z`는 다음과 같습니다:

```capnp theme={null}
message MessageType {
  message XType {
    message YType {
      int32 z;
    };
    repeated YType y;
  };
  XType x;
};
```

ClickHouse는 `x.y.z`라는 이름의 컬럼(또는 `x_y_z`, `X.y_Z` 등)을 찾습니다.

중첩 메시지는 [중첩 데이터 구조](/docs/ko/reference/data-types/nested-data-structures/index)의 입력이나 출력에 적합합니다.

아래와 같은 protobuf 스키마에 정의된 기본값은 적용되지 않으며, 대신 [테이블 기본값](/docs/ko/reference/statements/create/table#default_values)이 사용됩니다:

```capnp theme={null}
syntax = "proto2";

message MessageType {
  optional int32 result_per_page = 3 [default = 10];
}
```

메시지에 [oneof](https://protobuf.dev/programming-guides/proto3/#oneof)가 포함되어 있고 `input_format_protobuf_oneof_presence`가 설정된 경우, ClickHouse는 oneof에서 발견된 필드를 나타내는 컬럼의 값을 채웁니다.

```capnp theme={null}
syntax = "proto3";

message StringOrString {
  oneof string_oneof {
    string string1 = 1;
    string string2 = 42;
  }
}
```

```sql theme={null}
CREATE TABLE string_or_string ( string1 String, string2 String, string_oneof Enum('no'=0, 'hello' = 1, 'world' = 42))  Engine=MergeTree ORDER BY tuple();
INSERT INTO string_or_string from INFILE '$CURDIR/data_protobuf/String1' SETTINGS format_schema='$SCHEMADIR/string_or_string.proto:StringOrString' FORMAT ProtobufSingle;
SELECT * FROM string_or_string
```

```text theme={null}
   ┌─────────┬─────────┬──────────────┐
   │ string1 │ string2 │ string_oneof │
   ├─────────┼─────────┼──────────────┤
1. │         │ string2 │ world        │
   ├─────────┼─────────┼──────────────┤
2. │ string1 │         │ hello        │
   └─────────┴─────────┴──────────────┘
```

존재 여부를 나타내는 컬럼 이름은 oneof의 이름과 같아야 합니다.
중첩된 메시지가 지원됩니다([basic-examples](#basic-examples) 참조). 빈 메시지도 지원됩니다.
허용되는 타입은 Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Enum, Enum8 또는 Enum16입니다.
Enum(및 Enum8 또는 Enum16)에는 oneof에 사용할 수 있는 모든 태그와 없음을 나타내는 0이 포함되어야 하며, 문자열 표현은 중요하지 않습니다.

설정 [`input_format_protobuf_oneof_presence`](/docs/ko/reference/settings/formats#input_format_protobuf_oneof_presence)은 기본적으로 비활성화되어 있습니다

ClickHouse는 protobuf 메시지를 `length-delimited` 포맷으로 입력하고 출력합니다.
즉, 각 메시지 앞에 해당 메시지의 길이를 [가변 길이 정수(varint)](https://developers.google.com/protocol-buffers/docs/encoding#varints)로 기록해야 합니다.

<div id="example-usage">
  ## 사용 예시
</div>

<div id="basic-examples">
  ### 데이터 읽기 및 쓰기
</div>

<Info>
  **예시 파일**

  이 예시에서 사용하는 파일은 [examples 리포지토리](https://github.com/ClickHouse/formats/ProtoBuf)에서 확인할 수 있습니다.
</Info>

이 예시에서는 `protobuf_message.bin` 파일의 데이터를 ClickHouse 테이블로 읽어옵니다. 그런 다음 `Protobuf` 형식을 사용해 이 데이터를
`protobuf_message_from_clickhouse.bin`이라는 파일로 다시 씁니다.

`schemafile.proto` 파일이 다음과 같다고 가정합니다:

```capnp theme={null}
syntax = "proto3";

message MessageType {
  string name = 1;
  string surname = 2;
  uint32 birthDate = 3;
  repeated string phoneNumbers = 4;
};
```

<Accordion title="바이너리 파일 생성">
  이미 `Protobuf` 포맷으로 데이터를 직렬화하고 역직렬화하는 방법을 알고 있다면 이 단계는 건너뛰어도 됩니다.

  Python을 사용해 일부 데이터를 `protobuf_message.bin`에 직렬화한 뒤 ClickHouse로 읽어오겠습니다.
  다른 언어를 사용하려면 ["인기 있는 언어에서 길이 구분 Protobuf 메시지를 읽고 쓰는 방법"](https://cwiki.apache.org/confluence/display/GEODE/Delimiting+Protobuf+Messages)도 참고하십시오.

  다음 명령을 실행하여 `schemafile.proto`와
  같은 디렉터리에 `schemafile_pb2.py`라는 Python 파일을 생성하십시오. 이 파일에는
  `UserData` Protobuf 메시지를 나타내는 Python 클래스가 포함됩니다:

  ```bash theme={null}
  protoc --python_out=. schemafile.proto
  ```

  이제 `schemafile_pb2.py`와 같은
  디렉터리에 `generate_protobuf_data.py`라는 새 Python 파일을 생성하십시오. 여기에 다음 코드를 붙여 넣으십시오:

  ```python theme={null}
  import schemafile_pb2  # 'protoc'가 생성한 모듈
  from google.protobuf import text_format
  from google.protobuf.internal.encoder import _VarintBytes # 내부 varint 인코더 가져오기

  def create_user_data_message(name, surname, birthDate, phoneNumbers):
      """
      UserData Protobuf 메시지를 생성하고 채웁니다.
      """
      message = schemafile_pb2.MessageType()
      message.name = name
      message.surname = surname
      message.birthDate = birthDate
      message.phoneNumbers.extend(phoneNumbers)
      return message

  # 예시 사용자 데이터
  data_to_serialize = [
      {"name": "Aisha", "surname": "Khan", "birthDate": 19920815, "phoneNumbers": ["(555) 247-8903", "(555) 612-3457"]},
      {"name": "Javier", "surname": "Rodriguez", "birthDate": 20001015, "phoneNumbers": ["(555) 891-2046", "(555) 738-5129"]},
      {"name": "Mei", "surname": "Ling", "birthDate": 19980616, "phoneNumbers": ["(555) 956-1834", "(555) 403-7682"]},
  ]

  output_filename = "protobuf_messages.bin"

  # 바이너리 파일을 바이너리 쓰기 모드('wb')로 엽니다
  with open(output_filename, "wb") as f:
      for item in data_to_serialize:
          # 현재 사용자에 대한 Protobuf 메시지 인스턴스를 생성합니다
          message = create_user_data_message(
              item["name"],
              item["surname"],
              item["birthDate"],
              item["phoneNumbers"]
          )

          # 메시지를 직렬화합니다
          serialized_data = message.SerializeToString()

          # 직렬화된 데이터의 길이를 구합니다
          message_length = len(serialized_data)

          # Protobuf 라이브러리의 내부 _VarintBytes를 사용해 길이를 인코딩합니다
          length_prefix = _VarintBytes(message_length)

          # 길이 접두사를 씁니다
          f.write(length_prefix)
          # 직렬화된 메시지 데이터를 씁니다
          f.write(serialized_data)

  print(f"Protobuf messages (length-delimited) written to {output_filename}")

  # --- 선택 사항: 검증(다시 읽어서 출력) ---
  # 다시 읽을 때도 varint용 내부 Protobuf 디코더를 사용합니다.
  from google.protobuf.internal.decoder import _DecodeVarint32

  print("\n--- Verifying by reading back ---")
  with open(output_filename, "rb") as f:
      buf = f.read() # varint 디코딩을 쉽게 하기 위해 파일 전체를 버퍼로 읽습니다
      n = 0
      while n < len(buf):
          # varint 길이 접두사를 디코딩합니다
          msg_len, new_pos = _DecodeVarint32(buf, n)
          n = new_pos

          # 메시지 데이터를 추출합니다
          message_data = buf[n:n+msg_len]
          n += msg_len

          # 메시지를 파싱합니다
          decoded_message = schemafile_pb2.MessageType()
          decoded_message.ParseFromString(message_data)
          print(text_format.MessageToString(decoded_message, as_utf8=True))
  ```

  이제 명령줄에서 스크립트를 실행하십시오. 예를 들어 `uv`를 사용해
  Python 가상 환경에서 실행하는 것을 권장합니다:

  ```bash theme={null}
  uv venv proto-venv
  source proto-venv/bin/activate
  ```

  다음 Python 라이브러리를 설치해야 합니다:

  ```bash theme={null}
  uv pip install --upgrade protobuf
  ```

  바이너리 파일을 생성하려면 스크립트를 실행하십시오:

  ```bash theme={null}
  python generate_protobuf_data.py
  ```
</Accordion>

스키마와 일치하는 ClickHouse 테이블을 생성하십시오:

```sql theme={null}
CREATE DATABASE IF NOT EXISTS test;
CREATE TABLE IF NOT EXISTS test.protobuf_messages (
  name String,
  surname String,
  birthDate UInt32,
  phoneNumbers Array(String)
)
ENGINE = MergeTree()
ORDER BY tuple()
```

명령줄에서 테이블에 데이터를 삽입하세요:

```bash theme={null}
cat protobuf_messages.bin | clickhouse-client --query "INSERT INTO test.protobuf_messages SETTINGS format_schema='schemafile:MessageType' FORMAT Protobuf"
```

`Protobuf` 형식을 사용하여 데이터를 바이너리 파일에 다시 쓸 수도 있습니다:

```sql theme={null}
SELECT * FROM test.protobuf_messages INTO OUTFILE 'protobuf_message_from_clickhouse.bin' FORMAT Protobuf SETTINGS format_schema = 'schemafile:MessageType'
```

Protobuf 스키마를 사용하면 이제 ClickHouse에서 파일 `protobuf_message_from_clickhouse.bin`에 기록된 데이터를 역직렬화할 수 있습니다.

<div id="basic-examples-cloud">
  ### ClickHouse Cloud에서 데이터 읽기 및 쓰기
</div>

ClickHouse Cloud에서는 Protobuf 스키마 파일을 업로드할 수 없습니다. 하지만 `format_protobuf_schema`
설정을 사용해 쿼리에서 스키마를 지정할 수 있습니다. 이 예시에서는 로컬
머신에서 직렬화된 데이터를 읽어 ClickHouse Cloud의 테이블에 삽입하는 방법을 보여줍니다.

이전 예시와 마찬가지로, ClickHouse Cloud에서 Protobuf 스키마에 맞게 테이블을 생성합니다:

```sql theme={null}
CREATE DATABASE IF NOT EXISTS test;
CREATE TABLE IF NOT EXISTS test.protobuf_messages (
  name String,
  surname String,
  birthDate UInt32,
  phoneNumbers Array(String)
)
ENGINE = MergeTree()
ORDER BY tuple()
```

설정 `format_schema_source`는 `format_schema` 설정의 소스를 정의합니다

가능한 값:

* 'file' (기본값): Cloud에서는 지원되지 않습니다
* 'string': `format_schema`는 스키마(schema)의 리터럴 내용입니다.
* 'query': `format_schema`는 스키마(schema)를 가져오기 위한 쿼리입니다.

<div id="format-schema-source-string">
  ### `format_schema_source='string'`
</div>

스키마를 문자열로 지정해 ClickHouse Cloud에 데이터를 삽입하려면 다음을 실행하십시오:

```bash theme={null}
cat protobuf_messages.bin | clickhouse client --host <hostname> --secure --password <password> --query "INSERT INTO testing.protobuf_messages SETTINGS format_schema_source='syntax = "proto3";message MessageType {  string name = 1;  string surname = 2;  uint32 birthDate = 3;  repeated string phoneNumbers = 4;};', format_schema='schemafile:MessageType' FORMAT Protobuf"
```

테이블에 삽입된 데이터를 조회하세요:

```bash theme={null}
clickhouse client --host <hostname> --secure --password <password> --query "SELECT * FROM testing.protobuf_messages"
```

```response theme={null}
Aisha Khan 19920815 ['(555) 247-8903','(555) 612-3457']
Javier Rodriguez 20001015 ['(555) 891-2046','(555) 738-5129']
Mei Ling 19980616 ['(555) 956-1834','(555) 403-7682']
```

<div id="format-schema-source-query">
  ### `format_schema_source='query'`
</div>

Protobuf 스키마를 테이블(table)에 저장할 수도 있습니다.

데이터를 삽입할 ClickHouse Cloud 테이블을 생성합니다:

```sql theme={null}
CREATE TABLE testing.protobuf_schema (
  schema String
)
ENGINE = MergeTree()
ORDER BY tuple();
```

```sql theme={null}
INSERT INTO testing.protobuf_schema VALUES ('syntax = "proto3";message MessageType {  string name = 1;  string surname = 2;  uint32 birthDate = 3;  repeated string phoneNumbers = 4;};');
```

데이터를 ClickHouse Cloud에 삽입할 때 실행할 쿼리에서 스키마를 지정하세요:

```bash theme={null}
cat protobuf_messages.bin | clickhouse client --host <hostname> --secure --password <password> --query "INSERT INTO testing.protobuf_messages SETTINGS format_schema_source='SELECT schema FROM testing.protobuf_schema', format_schema='schemafile:MessageType' FORMAT Protobuf"
```

테이블에 삽입된 데이터를 조회하세요:

```bash theme={null}
clickhouse client --host <hostname> --secure --password <password> --query "SELECT * FROM testing.protobuf_messages"
```

```response theme={null}
Aisha Khan 19920815 ['(555) 247-8903','(555) 612-3457']
Javier Rodriguez 20001015 ['(555) 891-2046','(555) 738-5129']
Mei Ling 19980616 ['(555) 956-1834','(555) 403-7682']
```

<div id="using-autogenerated-protobuf-schema">
  ### 자동 생성된 스키마 사용
</div>

데이터용 외부 Protobuf 스키마가 없더라도 자동 생성된 스키마를 사용해 데이터를 Protobuf 형식으로 출력하거나 입력할 수 있습니다. 이를 위해 `format_protobuf_use_autogenerated_schema` 설정을 사용합니다.

예시:

```sql theme={null}
SELECT * FROM test.hits format Protobuf SETTINGS format_protobuf_use_autogenerated_schema=1
```

이 경우 ClickHouse는 함수 [`structureToProtobufSchema`](/docs/ko/reference/functions/regular-functions/other-functions#structureToProtobufSchema)를 사용해 테이블 구조에 따라 Protobuf 스키마를 자동으로 생성합니다. 그런 다음 이 스키마를 사용해 데이터를 Protobuf 형식으로 직렬화합니다.

자동 생성된 스키마를 사용해 Protobuf 파일도 읽을 수 있습니다. 이 경우 파일은 동일한 스키마를 사용해 생성되어 있어야 합니다:

```bash theme={null}
$ cat hits.bin | clickhouse-client --query "INSERT INTO test.hits SETTINGS format_protobuf_use_autogenerated_schema=1 FORMAT Protobuf"
```

설정 [`format_protobuf_use_autogenerated_schema`](/docs/ko/reference/settings/formats#format_protobuf_use_autogenerated_schema)는 기본적으로 활성화되어 있으며, [`format_schema`](/docs/ko/reference/settings/formats#format_schema)가 설정되지 않은 경우에 적용됩니다.

또한 설정 [`output_format_schema`](/docs/ko/reference/settings/formats#output_format_schema)를 사용하면 입력/출력 시 자동 생성된 스키마를 파일에 저장할 수 있습니다. 예시:

```sql theme={null}
SELECT * FROM test.hits format Protobuf SETTINGS format_protobuf_use_autogenerated_schema=1, output_format_schema='path/to/schema/schema.proto'
```

이 경우 자동 생성된 Protobuf 스키마가 `path/to/schema/schema.capnp` 파일에 저장됩니다.

<div id="drop-protobuf-cache">
  ### Protobuf 캐시 삭제
</div>

[`format_schema_path`](/docs/ko/reference/settings/server-settings/settings#format_schema_path)에서 로드된 Protobuf 스키마를 다시 로드하려면 [`SYSTEM DROP ... FORMAT CACHE`](/docs/ko/reference/statements/system#system-drop-schema-format) SQL 문을 사용하십시오.

```sql theme={null}
SYSTEM DROP FORMAT SCHEMA CACHE FOR Protobuf
```
