Apache Iceberg 완벽 가이드 — 차세대 Lakehouse 테이블 포맷의 모든 것
Apache Iceberg의 등장 배경, 메타데이터 아키텍처, 핵심 기능, 카탈로그 종류, 엔진별 사용법, 운영 자동화, 성능 최적화, 실무 도입 전략까지 한 편으로 정리한 종합 가이드.
이 글은 Apache Iceberg를 처음 접하는 데이터 엔지니어부터, 이미 운영 중인 플랫폼에서 Iceberg 도입을 고민 중인 아키텍트까지 모두를 대상으로 한 종합 가이드입니다. Iceberg가 왜 등장했는지, 내부적으로 어떻게 동작하는지, 어떻게 사용하고 운영해야 하는지를 실무 관점에서 정리합니다.
1. Apache Iceberg란?
1.1 등장 배경 — Hive 테이블 포맷의 한계
2010년대 빅데이터 생태계의 사실상 표준은 Hive Metastore + 디렉토리 파티셔닝 이었습니다. 단순하고 직관적인 이 구조는 HDFS 시대에는 잘 작동했지만, 클라우드 객체 스토리지(S3, ADLS, GCS)가 표준이 되면서 한계가 드러났습니다.
Hive 방식의 구조적 문제:
- 디렉토리 = 파티션 = 쿼리 조건:
WHERE event_date='2026-05-23'가 디렉토리 경로와 정확히 일치해야 Partition Pruning이 동작합니다. 사용자가WHERE ts > ...처럼 표현식을 쓰면 Pruning이 무력화됩니다. - 파티션 진화 불가: "일별 파티션을 시간별로 바꾸자"는 결정이 곧 전체 테이블 재작성을 의미합니다.
- 원자성 부재: S3에서 rename은 copy + delete로 구현되어, 잡 중간 실패 시 부분 가시성 문제가 발생합니다.
- List 비용: 수십만 파일이 있는 prefix에 대한 S3 list 호출은 비싸고 일관성도 약합니다.
- 읽기-쓰기 동시성 부재: 동일 파티션을 쓰는 동안 읽으면 부분 파일이 보일 수 있습니다.
이 한계를 해결하기 위해 Netflix에서 2017년에 시작한 프로젝트가 Apache Iceberg 입니다.
1.2 Iceberg의 핵심 가치
Iceberg는 객체 스토리지 위에 데이터베이스 수준의 트랜잭션과 진화(evolution) 의미론 을 부여하는 개방형 테이블 포맷 입니다.
| 가치 | 설명 |
|---|---|
| ACID 트랜잭션 | Snapshot 기반의 Serializable Isolation |
| Hidden Partitioning | 사용자가 파티션 컬럼을 알 필요 없이 자동 Pruning |
| Schema/Partition Evolution | 재작성 없이 스키마와 파티셔닝 변경 |
| Time Travel | 과거 Snapshot 조회·롤백 |
| Branch / Tag | Git처럼 테이블에 분기와 태그 부여 |
| 엔진 중립적 | Spark, Trino, Flink, Snowflake, BigQuery 등 다중 엔진 지원 |
1.3 Lakehouse 아키텍처에서의 위치
┌─────────────────────────────────────────────────┐
│ Spark │ Trino │ Flink │ Snowflake │ BigQuery │ ← 멀티 엔진
├─────────────────────────────────────────────────┤
│ Iceberg Catalog (REST) │ ← 통제점
├─────────────────────────────────────────────────┤
│ Iceberg Metadata (Snapshot / Manifest / ...) │ ← 테이블 포맷
├─────────────────────────────────────────────────┤
│ Parquet / ORC / Avro (Data Files) │ ← 파일 포맷
├─────────────────────────────────────────────────┤
│ Object Storage (S3 / ADLS / GCS) │ ← 스토리지
└─────────────────────────────────────────────────┘
Iceberg는 "스토리지 포맷"이 아니라 "메타데이터 구조" 입니다. Parquet 같은 파일 포맷 위에 트랜잭션과 진화 의미론을 얹는 사양(spec)입니다.
1.4 주요 채택 사례
- Netflix: 원개발사. 수십 페타바이트 규모의 데이터 플랫폼에서 사용.
- Apple: 내부 데이터 플랫폼의 핵심 포맷.
- Airbnb, LinkedIn, Stripe, Pinterest, Adobe: 멀티 엔진 환경에서 채택.
- Snowflake, AWS, Google Cloud, Databricks: 외부 카탈로그·외부 테이블로 Iceberg를 1급 시민으로 지원.
2. Iceberg 아키텍처
2.1 3-Layer 메타데이터 구조
Iceberg는 메타데이터를 3개 계층으로 분리합니다.
Catalog (Hive / REST / Glue / Nessie ...)
│
└─▶ metadata.json (현재 스냅샷, 스키마, 파티션 spec)
│
└─▶ manifest list (snap-*.avro)
│
└─▶ manifest file (*.avro)
│
└─▶ data file (*.parquet)
2.2 디렉토리 구성 예시
warehouse/db/orders/
├── data/
│ ├── 00000-0-...parquet
│ └── 00001-0-...parquet
└── metadata/
├── v1.metadata.json
├── v2.metadata.json
├── snap-3051729675574597004-1-...avro # manifest list
└── 0c7a1f8c-...avro # manifest file
| 파일 | 역할 |
|---|---|
metadata.json | 테이블의 현재 상태: 스키마, 파티션 spec, sort order, 현재 snapshot 포인터 |
Manifest List (snap-*.avro) | 한 Snapshot에 속한 모든 manifest의 목록 + 파티션 통계 |
Manifest File (*.avro) | 한 묶음의 데이터 파일 목록 + 컬럼별 통계 (min/max, null count 등) |
Data File (*.parquet) | 실제 데이터 |
2.3 Snapshot 기반 트랜잭션 모델
모든 쓰기 작업은 새로운 Snapshot 을 생성합니다. Snapshot은 불변(immutable)이며, "그 시점의 테이블 상태"를 완전히 표현합니다.
Snapshot 0 (테이블 생성)
│
├─▶ INSERT → Snapshot 1
│
├─▶ UPDATE → Snapshot 2
│
└─▶ DELETE → Snapshot 3 (현재)
쓰기는 Optimistic Concurrency Control(OCC) 로 직렬화됩니다. Commit 시점에 카탈로그가 atomic compare-and-swap으로 current snapshot pointer를 교체합니다. 충돌이 발생하면 retry합니다.
2.4 Predicate Pushdown의 새로운 차원
쿼리 엔진은 manifest의 컬럼 통계를 보고 파일을 열기 전에 필요 없는 파일을 잘라낼 수 있습니다.
쿼리: SELECT * FROM orders WHERE order_date = '2026-05-23'
1. Manifest List 스캔 → 파티션 통계로 manifest 1개로 좁힘
2. Manifest 스캔 → 컬럼 min/max로 data file 5개로 좁힘
3. Data file 5개만 읽음
Hive는 list → 모든 파일 오픈 → footer 읽기 였지만, Iceberg는 manifest 한 번 읽기 → 필요한 파일만 오픈 입니다.
3. Iceberg 핵심 기능
3.1 Schema Evolution
Iceberg는 모든 컬럼에 고유 ID 를 부여합니다. 이름이 아니라 ID로 매핑하므로, 컬럼 이름 변경/재배치도 안전합니다.
| 연산 | Hive | Iceberg |
|---|---|---|
| Add column | OK | OK |
| Drop column | 위험 | 안전 |
| Rename column | 위험 | 안전 |
| Reorder column | 위험 | 안전 |
| Type promote (int → long) | 부분 | 안전 |
ALTER TABLE orders ADD COLUMN customer_tier STRING;
ALTER TABLE orders RENAME COLUMN amt TO amount;
ALTER TABLE orders ALTER COLUMN amount TYPE BIGINT;3.2 Hidden Partitioning과 Partition Evolution
Hive에서는 사용자가 WHERE event_date='2026-05-23'처럼 파티션 컬럼을 직접 써야 했습니다. Iceberg는 파티션 변환(transform)을 메타데이터에 저장합니다.
CREATE TABLE orders (
id BIGINT,
ts TIMESTAMP,
amount DECIMAL(10,2)
) USING iceberg
PARTITIONED BY (days(ts)); -- ts에서 일자를 자동 추출
-- 사용자는 ts만 알면 됨
SELECT * FROM orders WHERE ts >= '2026-05-23';지원하는 transform: identity, bucket(N, col), truncate(N, col), year, month, day, hour.
Partition Evolution: 운영 중에 파티션 전략을 바꿀 수 있습니다.
ALTER TABLE orders DROP PARTITION FIELD days(ts);
ALTER TABLE orders ADD PARTITION FIELD hours(ts);기존 파일은 그대로 두고, 신규 쓰기부터 새 전략을 적용합니다.
3.3 Time Travel, Tag, Branch
-- Snapshot ID로 조회
SELECT * FROM orders VERSION AS OF 3051729675574597004;
-- 타임스탬프로 조회
SELECT * FROM orders TIMESTAMP AS OF '2026-05-20 00:00:00';
-- 태그 생성 (영구 보존)
ALTER TABLE orders CREATE TAG `release-2026-Q2`;
-- 브랜치 생성 (실험용)
ALTER TABLE orders CREATE BRANCH experimental;3.4 Row-Level 연산 — CoW vs MoR
| 모드 | 동작 | 쓰기 비용 | 읽기 비용 |
|---|---|---|---|
| Copy-on-Write (CoW) | 영향받은 파일 전체를 다시 씀 | 높음 | 낮음 |
| Merge-on-Read (MoR) | Delete file을 추가, 읽을 때 병합 | 낮음 | 높음 |
CDC처럼 잦은 UPDATE/DELETE에는 MoR, 분석 위주에는 CoW를 권장합니다.
V2 spec에서 도입된 Delete 종류:
- Position Delete: "파일 X의 N번째 row를 삭제"
- Equality Delete: "특정 컬럼이 특정 값인 row를 삭제"
V3에서는 Deletion Vector 가 도입되어 MoR의 효율이 크게 개선됩니다.
4. Iceberg Catalog
4.1 Catalog의 역할
Catalog는 "테이블 이름 → 현재 metadata.json 위치" 매핑을 보관합니다. 쓰기 시 atomic swap을 제공해야 하므로, Iceberg에서 Catalog는 데이터베이스의 트랜잭션 매니저에 해당합니다.
4.2 Catalog 종류
| Catalog | 특징 | 권장 환경 |
|---|---|---|
| Hive Metastore | 기존 HMS 재활용 | Hive 자산이 많은 온프레미스 |
| Hadoop (file-based) | 카탈로그 서버 없이 파일 락 | 개발/테스트만 |
| REST Catalog | 엔진 중립 표준 API | 신규 도입 시 1순위 |
| AWS Glue | AWS 통합 | AWS-native 환경 |
| Nessie | Git 같은 multi-table branching | Data versioning |
| Snowflake / Polaris | 관리형 | Snowflake와 Iceberg 공유 |
| Unity Catalog | Databricks 통합 | Databricks와 Iceberg 공유 |
| JDBC | 관계형 DB 사용 | 간단한 메타스토어 |
4.3 REST Catalog 표준의 의미
REST Catalog는 카탈로그를 언어/엔진 중립 HTTP API 로 정의한 표준입니다. Spark, Trino, Flink, PyIceberg가 모두 동일 API로 접근하므로, 카탈로그 구현체를 자유롭게 교체할 수 있습니다. 2026년 현재 Iceberg 생태계의 사실상 표준은 REST Catalog입니다.
5. Iceberg 사용하기
5.1 Spark
-- Spark 설정 (REST Catalog)
spark.sql.catalog.demo = org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.demo.type = rest
spark.sql.catalog.demo.uri = http://iceberg-rest:8181
spark.sql.catalog.demo.warehouse = s3://my-warehouseCREATE TABLE demo.db.orders (
id BIGINT,
customer_id BIGINT,
ts TIMESTAMP,
amount DECIMAL(10,2)
) USING iceberg
PARTITIONED BY (days(ts));
INSERT INTO demo.db.orders VALUES (1, 100, current_timestamp(), 99.99);
MERGE INTO demo.db.orders t
USING updates u
ON t.id = u.id
WHEN MATCHED THEN UPDATE SET amount = u.amount
WHEN NOT MATCHED THEN INSERT *;5.2 Trino
-- Trino catalog 설정 파일 (iceberg.properties)
connector.name = iceberg
iceberg.catalog.type = rest
iceberg.rest-catalog.uri = http://iceberg-rest:8181SELECT * FROM iceberg.db.orders WHERE ts >= DATE '2026-05-01';
-- 메타데이터 테이블
SELECT * FROM iceberg.db."orders$snapshots";
SELECT * FROM iceberg.db."orders$files";
SELECT * FROM iceberg.db."orders$partitions";5.3 Flink로 스트리밍 적재
CREATE TABLE orders_iceberg (
id BIGINT,
ts TIMESTAMP(3),
amount DECIMAL(10,2)
) WITH (
'connector' = 'iceberg',
'catalog-type' = 'rest',
'uri' = 'http://iceberg-rest:8181',
'warehouse' = 's3://my-warehouse'
);
INSERT INTO orders_iceberg
SELECT * FROM kafka_orders;5.4 PyIceberg
from pyiceberg.catalog import load_catalog
catalog = load_catalog(
"demo",
**{
"type": "rest",
"uri": "http://iceberg-rest:8181",
"warehouse": "s3://my-warehouse",
},
)
table = catalog.load_table("db.orders")
# Pandas로 직접 읽기
df = table.scan(
row_filter="ts >= '2026-05-01'",
selected_fields=("id", "amount"),
).to_pandas()6. 운영과 유지보수
6.1 Compaction — Small File 문제
스트리밍 적재 등으로 작은 파일이 누적되면 쿼리 성능이 급격히 떨어집니다. 정기적으로 컴팩션이 필요합니다.
-- Spark Action
CALL demo.system.rewrite_data_files(
table => 'db.orders',
options => map('target-file-size-bytes', '536870912') -- 512MB
);6.2 Snapshot Expiration
Snapshot이 계속 쌓이면 메타데이터가 비대해지고 데이터 파일도 GC되지 않습니다.
CALL demo.system.expire_snapshots(
table => 'db.orders',
older_than => TIMESTAMP '2026-05-01 00:00:00',
retain_last => 10
);6.3 Orphan File 정리
실패한 쓰기 등으로 메타데이터에 참조되지 않는 고아 파일이 생길 수 있습니다.
CALL demo.system.remove_orphan_files(
table => 'db.orders',
older_than => TIMESTAMP '2026-05-01 00:00:00'
);6.4 Metadata Rewrite
Manifest가 너무 많아지면 plan 시간이 늘어납니다.
CALL demo.system.rewrite_manifests(table => 'db.orders');6.5 운영 자동화 표준 패턴
| 작업 | 주기 | 도구 |
|---|---|---|
rewrite_data_files | 시간/일 단위 | Spark Action, Airflow |
expire_snapshots | 일 단위 | Spark Action |
remove_orphan_files | 주 단위 | Spark Action |
rewrite_manifests | 필요 시 | Spark Action |
| 메트릭 수집 | 분 단위 | Catalog API + Prometheus |
운영 자동화 없이는 Iceberg는 빠르게 "메타데이터 늪"이 됩니다.
7. Iceberg Spec 진화 (V1 → V2 → V3)
| 버전 | 출시 | 핵심 기능 |
|---|---|---|
| V1 | 2019 | Snapshot, Schema/Partition Evolution, Hidden Partitioning |
| V2 | 2021 | Row-level Delete (Position/Equality Delete), MoR |
| V3 | 2025+ | Deletion Vector, Variant 타입, Geospatial, Default values, Row Lineage |
V3의 Deletion Vector는 Position Delete 파일 대신 압축 비트맵을 사용하여, MoR 워크로드의 읽기 성능을 대폭 개선합니다.
호환성 주의:
- 상위 버전 테이블을 하위 버전만 지원하는 엔진은 읽지 못합니다.
- V2 → V3 업그레이드는 일방향입니다.
- 모든 클라이언트(Spark, Trino, Flink) 버전을 미리 확인해야 합니다.
8. Delta Lake / Hudi와의 비교
| 항목 | Iceberg | Delta Lake | Hudi |
|---|---|---|---|
| 원개발사 | Netflix | Databricks | Uber |
| 트랜잭션 로그 | Manifest tree | JSON log | Timeline |
| Hidden Partitioning | OK | 부분 | 부분 |
| Partition Evolution | OK | 미지원 | 미지원 |
| Schema Evolution | 강함 | 강함 | 강함 |
| CoW / MoR | 둘 다 | CoW (DV 도입) | 둘 다 |
| Branch / Tag | OK | 미지원 | 미지원 |
| REST Catalog 표준 | OK | UC 종속 | 미지원 |
| 단일 엔진 친화 | 보통 | 매우 강함(Databricks) | 보통 |
| 멀티 엔진 친화 | 매우 강함 | 보통 (UniForm) | 보통 |
선택 기준:
- Databricks-only 환경 → Delta Lake
- 잦은 Upsert + 스트리밍 → Hudi
- 멀티 엔진, 장기 보존, 진화 의미론 → Iceberg
Delta Lake가 UniForm, Iceberg가 XTable 같은 상호운용 레이어를 도입하면서, 미래에는 포맷 선택이 점점 덜 중요해질 가능성이 큽니다.
9. 성능 최적화 베스트 프랙티스
9.1 파일 크기
너무 작은 파일(<100MB)은 plan 오버헤드, 너무 큰 파일(>2GB)은 병렬성 저하. 보통 256MB~1GB 권장.
ALTER TABLE orders SET TBLPROPERTIES (
'write.target-file-size-bytes' = '536870912'
);9.2 파티셔닝 전략
- 카디널리티가 너무 높은 컬럼으로 파티셔닝하지 마세요 (e.g.
user_id). - 시간 컬럼은 거의 항상
day또는hourtransform 사용. - 고-카디널리티 컬럼은
bucket(N, col)로 처리.
9.3 Sort Order / Z-Order
쿼리 패턴에 맞춰 정렬하면 manifest pruning 효과가 극대화됩니다.
ALTER TABLE orders WRITE ORDERED BY ts, customer_id;9.4 Manifest 분할
대형 테이블에서는 manifest를 파티션별로 분할해 plan 비용을 줄입니다. rewrite_manifests Action 활용.
9.5 안티 패턴
- Snapshot Expiration 미실행 → 메타데이터 폭증
- 컴팩션 미실행 → 수십만 작은 파일
- 너무 잦은 commit → manifest 폭증
- 파티션 컬럼 과다 → list 비용 증가
- Hadoop Catalog 운영 사용 → commit 충돌, 데이터 손실 위험
10. 실무 도입 가이드
10.1 기존 Hive 테이블 마이그레이션
두 가지 방식이 있습니다.
| 방식 | 동작 | 비용 | 롤백 |
|---|---|---|---|
Snapshot (snapshot 프로시저) | 원본 Hive 테이블 유지하면서 Iceberg shadow 생성 | 낮음 | 쉬움 |
Migrate (migrate 프로시저) | 원본을 in-place로 Iceberg로 변환 | 낮음 | 어려움 |
-- 방식 1: 검증용 shadow 생성
CALL demo.system.snapshot('hive_db.orders', 'demo.db.orders');
-- 방식 2: 검증 끝나면 완전 전환
CALL demo.system.migrate('hive_db.orders');10.2 CDC 파이프라인 구축
RDB → Debezium → Kafka → Flink → Iceberg (MoR)
│
└─▶ Compaction (배치)
핵심 포인트:
- Flink Iceberg sink는
upsert모드로 Equality Delete 사용 - 분 단위 commit (너무 자주는 X)
- 야간 배치로 컴팩션 + snapshot expiration
10.3 거버넌스와 보안
- AWS Lake Formation: Glue Catalog + Iceberg에 행/열 수준 권한
- Apache Ranger: Hive/Trino + Iceberg에 정책 기반 인가
- Unity Catalog: Databricks 환경에서 Iceberg 외부 테이블 통제
10.4 비용 최적화
- 컴팩션 파일 크기 조정으로 list/get 호출 수 절감
- Snapshot 보존 기간을 워크로드별로 차등 (시계열 vs 마스터)
- S3 Intelligent-Tiering으로 오래된 Snapshot 데이터 자동 이동
- Manifest 캐시(Spark, Trino)로 plan 시간 단축
11. 마무리
11.1 언제 Iceberg를 선택해야 하는가
다음 중 2개 이상 해당된다면 Iceberg가 명확한 우위입니다.
- 2개 이상의 쿼리 엔진을 동시에 사용한다 (Spark + Trino, Flink + Snowflake 등)
- 장기 보존 데이터에 대한 시간 여행, 법적 정정이 필요하다
- 파티션 전략을 향후 변경할 가능성이 있다
- 실험·재현성을 위한 브랜치/태그가 필요하다
- 특정 벤더(Databricks 등)에 lock-in되고 싶지 않다
11.2 Iceberg 생태계의 미래
- REST Catalog 표준화: 카탈로그가 Lakehouse의 진정한 통제점(control plane) 으로 자리잡고 있습니다.
- V3 확산: Deletion Vector, Variant, Geospatial 지원으로 활용 범위 확대.
- 상호운용성: XTable, UniForm으로 Iceberg ↔ Delta ↔ Hudi 경계가 흐려집니다.
- 관리형 카탈로그 경쟁: Snowflake Polaris, Databricks Unity, AWS Glue, Tabular의 후계자들이 시장을 형성합니다.
11.3 학습 리소스
- Apache Iceberg 공식 문서
- Iceberg Spec v2 / v3
- 본 블로그의 관련 글:
Iceberg는 더 이상 "새로운 옵션"이 아닙니다. 멀티 엔진 Lakehouse의 사실상 표준 이며, 카탈로그 계층이 데이터 플랫폼의 통제점으로 부상하는 흐름의 중심에 있습니다. 도입 자체보다, 운영 자동화와 카탈로그 전략에 더 많은 고민을 투자하시기 바랍니다.