> ## 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.

# Lakekeeper catalog

> 이 가이드에서는 ClickHouse와 Lakekeeper 카탈로그를 사용해 데이터를 쿼리하는 방법을 단계별로 안내합니다.

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>
            실험 기능입니다. <u><a href="/docs/docs/beta-and-experimental-features#experimental-features">자세히 알아보세요.</a></u>
        </div>;
};

<ExperimentalBadge />

<Note>
  Lakekeeper 카탈로그와의 통합은 Iceberg 테이블에서만 작동합니다.
  이 통합은 AWS S3와 기타 클라우드 스토리지 제공업체를 모두 지원합니다.
</Note>

ClickHouse는 여러 카탈로그(Unity, Glue, REST, Polaris 등)와의 통합을 지원합니다. 이 가이드에서는 ClickHouse와 [Lakekeeper](https://docs.lakekeeper.io/) 카탈로그를 사용해 데이터를 쿼리하는 방법을 단계별로 안내합니다.

Lakekeeper는 Apache Iceberg용 오픈 소스 REST 카탈로그 구현체로, 다음을 제공합니다:

* 높은 성능과 안정성을 위한 **Rust 네이티브** 구현
* Iceberg REST 카탈로그 사양을 준수하는 **REST API**
* S3 호환 스토리지와의 **클라우드 스토리지** 통합

<Note>
  이 기능은 아직 실험적 기능이므로, 다음을 사용해 활성화해야 합니다:
  `SET allow_experimental_database_iceberg = 1;`
</Note>

<div id="local-development-setup">
  ## 로컬 개발 환경 설정
</div>

로컬 개발 및 테스트를 위해 컨테이너화된 Lakekeeper 환경을 사용할 수 있습니다. 이 방식은 학습, 프로토타입 작성, 개발 환경에 적합합니다.

<div id="local-prerequisites">
  ### 사전 준비 사항
</div>

1. **Docker and Docker Compose**: Docker가 설치되어 있고 실행 중인지 확인합니다
2. **예제 설정**: Lakekeeper docker-compose 구성을 사용할 수 있습니다

<div id="setting-up-local-lakekeeper-catalog">
  ### 로컬 Lakekeeper 카탈로그 설정
</div>

Lakekeeper, PostgreSQL 메타데이터 백엔드, 그리고 객체 스토리지용 MinIO를 포함한 전체 환경을 제공하는 공식 [Lakekeeper docker-compose 설정](https://github.com/lakekeeper/lakekeeper/tree/main/examples/minimal)을 사용할 수 있습니다.

**1단계:** 예시를 실행할 새 폴더를 만든 다음, 아래 구성으로 `docker-compose.yml` 파일을 생성하세요:

```yaml theme={null}
version: '3.8'

services:
  lakekeeper:
    image: quay.io/lakekeeper/catalog:latest
    environment:
      - LAKEKEEPER__PG_ENCRYPTION_KEY=This-is-NOT-Secure!
      - LAKEKEEPER__PG_DATABASE_URL_READ=postgresql://postgres:postgres@db:5432/postgres
      - LAKEKEEPER__PG_DATABASE_URL_WRITE=postgresql://postgres:postgres@db:5432/postgres
      - RUST_LOG=info
    command: ["serve"]
    healthcheck:
      test: ["CMD", "/home/nonroot/lakekeeper", "healthcheck"]
      interval: 1s
      timeout: 10s
      retries: 10
      start_period: 30s
    depends_on:
      migrate:
        condition: service_completed_successfully
      db:
        condition: service_healthy
      minio:
        condition: service_healthy
    ports:
      - 8181:8181
    networks:
      - iceberg_net

  migrate:
    image: quay.io/lakekeeper/catalog:latest-main
    environment:
      - LAKEKEEPER__PG_ENCRYPTION_KEY=This-is-NOT-Secure!
      - LAKEKEEPER__PG_DATABASE_URL_READ=postgresql://postgres:postgres@db:5432/postgres
      - LAKEKEEPER__PG_DATABASE_URL_WRITE=postgresql://postgres:postgres@db:5432/postgres
      - RUST_LOG=info
    restart: "no"
    command: ["migrate"]
    depends_on:
      db:
        condition: service_healthy
    networks:
      - iceberg_net

  bootstrap:
    image: curlimages/curl
    depends_on:
      lakekeeper:
        condition: service_healthy
    restart: "no"
    command:
      - -w
      - "%{http_code}"
      - "-X"
      - "POST"
      - "-v"
      - "http://lakekeeper:8181/management/v1/bootstrap"
      - "-H"
      - "Content-Type: application/json"
      - "--data"
      - '{"accept-terms-of-use": true}'
      - "-o"
      - "/dev/null"
    networks:
      - iceberg_net

  initialwarehouse:
    image: curlimages/curl
    depends_on:
      lakekeeper:
        condition: service_healthy
      bootstrap:
        condition: service_completed_successfully
    restart: "no"
    command:
      - -w
      - "%{http_code}"
      - "-X"
      - "POST"
      - "-v"
      - "http://lakekeeper:8181/management/v1/warehouse"
      - "-H"
      - "Content-Type: application/json"
      - "--data"
      - '{"warehouse-name": "demo", "project-id": "00000000-0000-0000-0000-000000000000", "storage-profile": {"type": "s3", "bucket": "warehouse-rest", "key-prefix": "", "assume-role-arn": null, "endpoint": "http://minio:9000", "region": "local-01", "path-style-access": true, "flavor": "minio", "sts-enabled": true}, "storage-credential": {"type": "s3", "credential-type": "access-key", "aws-access-key-id": "minio", "aws-secret-access-key": "ClickHouse_Minio_P@ssw0rd"}}'
      - "-o"
      - "/dev/null"
    networks:
      - iceberg_net

  db:
    image: bitnami/postgresql:16.3.0
    environment:
      - POSTGRESQL_USERNAME=postgres
      - POSTGRESQL_PASSWORD=postgres
      - POSTGRESQL_DATABASE=postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -p 5432 -d postgres"]
      interval: 2s
      timeout: 10s
      retries: 5
      start_period: 10s
    volumes:
      - postgres_data:/bitnami/postgresql
    networks:
      - iceberg_net

  minio:
    image: bitnami/minio:2025.4.22
    environment:
      - MINIO_ROOT_USER=minio
      - MINIO_ROOT_PASSWORD=ClickHouse_Minio_P@ssw0rd
      - MINIO_API_PORT_NUMBER=9000
      - MINIO_CONSOLE_PORT_NUMBER=9001
      - MINIO_SCHEME=http
      - MINIO_DEFAULT_BUCKETS=warehouse-rest
    networks: 
      iceberg_net:
        aliases:
          - warehouse-rest.minio
    ports:
      - "9002:9000"
      - "9003:9001"
    healthcheck:
      test: ["CMD", "mc", "ls", "local", "|", "grep", "warehouse-rest"]
      interval: 2s
      timeout: 10s
      retries: 3
      start_period: 15s
    volumes:
      - minio_data:/bitnami/minio/data

  clickhouse:
    image: clickhouse/clickhouse-server:head
    container_name: lakekeeper-clickhouse
    user: '0:0'  # Ensures root permissions
    ports:
      - "8123:8123"
      - "9000:9000"
    volumes:
      - clickhouse_data:/var/lib/clickhouse
      - ./clickhouse/data_import:/var/lib/clickhouse/data_import  # 데이터셋 폴더 마운트
    networks:
      - iceberg_net
    environment:
      - CLICKHOUSE_DB=default
      - CLICKHOUSE_USER=default
      - CLICKHOUSE_DO_NOT_CHOWN=1
      - CLICKHOUSE_PASSWORD=
    depends_on:
      lakekeeper:
        condition: service_healthy
      minio:
        condition: service_healthy

volumes:
  postgres_data:
  minio_data:
  clickhouse_data:

networks:
  iceberg_net:
    driver: bridge
```

**2단계:** 다음 명령을 실행하여 서비스를 시작하세요:

```bash theme={null}
docker compose up -d
```

**3단계:** 모든 서비스가 준비될 때까지 기다리십시오. 다음 명령으로 로그를 확인할 수 있습니다:

```bash theme={null}
docker-compose logs -f
```

<Note>
  Lakekeeper 설정을 사용하려면 먼저 샘플 데이터가 Iceberg 테이블에 로드되어 있어야 합니다. ClickHouse를 통해 해당 테이블을 쿼리하기 전에 환경에서 테이블이 생성되고 데이터가 채워졌는지 확인하십시오. 테이블 사용 가능 여부는 사용하는 docker-compose 설정과 샘플 데이터 로딩 스크립트에 따라 달라집니다.
</Note>

<div id="connecting-to-local-lakekeeper-catalog">
  ### 로컬 Lakekeeper 카탈로그에 연결하기
</div>

ClickHouse 컨테이너에 연결하십시오:

```bash theme={null}
docker exec -it lakekeeper-clickhouse clickhouse-client
```

그런 다음 Lakekeeper 카탈로그에 대한 데이터베이스 연결을 생성합니다:

```sql theme={null}
SET allow_experimental_database_iceberg = 1;

CREATE DATABASE demo
ENGINE = DataLakeCatalog('http://lakekeeper:8181/catalog', 'minio', 'ClickHouse_Minio_P@ssw0rd')
SETTINGS catalog_type = 'rest', storage_endpoint = 'http://minio:9002/warehouse-rest', warehouse = 'demo'
```

<div id="querying-lakekeeper-catalog-tables-using-clickhouse">
  ## ClickHouse로 Lakekeeper 카탈로그 테이블(table) 쿼리하기
</div>

이제 연결(connection)이 완료되었으므로 Lakekeeper 카탈로그를 사용해 쿼리를 실행할 수 있습니다. 예시:

```sql theme={null}
USE demo;

SHOW TABLES;
```

구성에 샘플 데이터(예: 택시 데이터셋)가 포함되어 있다면, 다음과 같은 테이블이 표시됩니다:

```response theme={null}
┌─name──────────┐
│ default.taxis │
└───────────────┘
```

<Note>
  테이블이 보이지 않는다면, 일반적으로 다음 중 하나를 의미합니다:

  1. 환경에서 아직 샘플 테이블이 생성되지 않았습니다
  2. Lakekeeper 카탈로그 서비스가 아직 완전히 초기화되지 않았습니다
  3. 샘플 데이터 로딩 프로세스가 아직 완료되지 않았습니다

  테이블 생성 진행 상황은 Spark 로그에서 확인할 수 있습니다:

  ```bash theme={null}
  docker-compose logs spark
  ```
</Note>

테이블을 쿼리하려면(사용 가능한 경우):

```sql theme={null}
SELECT count(*) FROM `default.taxis`;
```

```response theme={null}
┌─count()─┐
│ 2171187 │
└─────────┘
```

<Info>
  **백틱이 필요합니다**

  ClickHouse는 둘 이상의 네임스페이스를 지원하지 않으므로 백틱을 사용해야 합니다.
</Info>

테이블 DDL을 확인하려면:

```sql theme={null}
SHOW CREATE TABLE `default.taxis`;
```

```response theme={null}
┌─statement─────────────────────────────────────────────────────────────────────────────────────┐
│ CREATE TABLE demo.`default.taxis`                                                             │
│ (                                                                                             │
│     `VendorID` Nullable(Int64),                                                               │
│     `tpep_pickup_datetime` Nullable(DateTime64(6)),                                           │
│     `tpep_dropoff_datetime` Nullable(DateTime64(6)),                                          │
│     `passenger_count` Nullable(Float64),                                                      │
│     `trip_distance` Nullable(Float64),                                                        │
│     `RatecodeID` Nullable(Float64),                                                           │
│     `store_and_fwd_flag` Nullable(String),                                                    │
│     `PULocationID` Nullable(Int64),                                                           │
│     `DOLocationID` Nullable(Int64),                                                           │
│     `payment_type` Nullable(Int64),                                                           │
│     `fare_amount` Nullable(Float64),                                                          │
│     `extra` Nullable(Float64),                                                                │
│     `mta_tax` Nullable(Float64),                                                              │
│     `tip_amount` Nullable(Float64),                                                           │
│     `tolls_amount` Nullable(Float64),                                                         │
│     `improvement_surcharge` Nullable(Float64),                                                │
│     `total_amount` Nullable(Float64),                                                         │
│     `congestion_surcharge` Nullable(Float64),                                                 │
│     `airport_fee` Nullable(Float64)                                                           │
│ )                                                                                             │
│ ENGINE = Iceberg('http://minio:9002/warehouse-rest/warehouse/default/taxis/', 'minio', '[HIDDEN]') │
└───────────────────────────────────────────────────────────────────────────────────────────────┘
```

<div id="loading-data-from-your-data-lake-into-clickhouse">
  ## 데이터 레이크의 데이터를 ClickHouse로 로드하기
</div>

Lakekeeper 카탈로그의 데이터를 ClickHouse로 로드해야 하는 경우, 먼저 로컬 ClickHouse 테이블을 생성하십시오:

```sql theme={null}
CREATE TABLE taxis
(
    `VendorID` Int64,
    `tpep_pickup_datetime` DateTime64(6),
    `tpep_dropoff_datetime` DateTime64(6),
    `passenger_count` Float64,
    `trip_distance` Float64,
    `RatecodeID` Float64,
    `store_and_fwd_flag` String,
    `PULocationID` Int64,
    `DOLocationID` Int64,
    `payment_type` Int64,
    `fare_amount` Float64,
    `extra` Float64,
    `mta_tax` Float64,
    `tip_amount` Float64,
    `tolls_amount` Float64,
    `improvement_surcharge` Float64,
    `total_amount` Float64,
    `congestion_surcharge` Float64,
    `airport_fee` Float64
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(tpep_pickup_datetime)
ORDER BY (VendorID, tpep_pickup_datetime, PULocationID, DOLocationID);
```

그런 다음 `INSERT INTO SELECT`를 사용하여 Lakekeeper 카탈로그 테이블의 데이터를 로드합니다:

```sql theme={null}
INSERT INTO taxis 
SELECT * FROM demo.`default.taxis`;
```
