Lakehouse가 필요한 이유: file lake의 한계와 transaction log
데이터 레이크의 약속과 현실
2010년대 데이터 레이크는 "저렴한 object storage에 모든 데이터를 Parquet/ORC로 쌓아두면 나중에 원하는 방식으로 분석할 수 있다"는 약속으로 주목받았다. 스키마를 미리 정의하지 않아도 되고, 확장성이 좋으며, S3 같은 클라우드 스토리지는 기가바이트당 비용이 데이터베이스보다 훨씬 저렴하다.
그런데 실제 운영에 들어가면 문제가 생기기 시작했다. 한 팀이 파일을 쓰는 동안 다른 팀이 읽으면 불완전한 데이터가 보인다. 컬럼을 추가했더니 하위 파이프라인이 깨진다. 어제 실수로 쓴 데이터를 지우려면 파티션 전체를 다시 써야 한다. 스트리밍으로 들어온 데이터가 수백만 개의 작은 파일로 쌓여서 쿼리가 느려진다.
이런 문제를 해결하기 위해 등장한 것이 테이블 포맷(table format) 이다. Apache Iceberg, Delta Lake, Apache Hudi — 이 세 프로젝트는 모두 같은 방향을 바라보고 있다: 파일 기반 스토리지 위에 데이터베이스 수준의 신뢰성을 얹는 것. 이를 데이터 레이크하우스(Lakehouse) 라고 부른다.
1. File Lake의 구조적 한계
테이블 포맷이 왜 필요한지 이해하려면 먼저 순수한 파일 레이크(file lake)가 왜 문제인지 알아야 한다.
dt=2026-06-30/
part-00001.parquet
dt=2026-06-30/
part-00002.parquet
partial data 노출
파티션 전체 재작성 필요
수백만 개 소형 파일
파이프라인 런타임 오류
LIST API 비용 증가
❌ ACID 트랜잭션 없음: 읽기/쓰기 충돌
파일 레이크는 원자성(atomicity)을 보장하지 않는다. Spark job이 30개의 파일을 순서대로 쓰는 도중에 다른 쿼리가 해당 파티션을 읽으면, 일부 파일만 쓰인 불완전한 상태를 읽게 된다. 이를 dirty read 라고 한다. 반대로 두 개의 Spark job이 같은 파티션에 동시에 쓰면 마지막에 쓴 것이 앞의 것을 덮어써서 데이터 유실이 생긴다.
이 문제는 단순한 실수가 아니라 object storage가 파일 단위 쓰기에서 원자성을 보장하지 않기 때문에 생기는 구조적 한계다.
❌ 업데이트·삭제가 어렵다
Parquet, ORC 같은 컬럼형 파일 포맷은 append-only다. 특정 행을 수정하거나 삭제하려면 해당 파티션 전체를 읽어서 변경 후 다시 쓰는 수밖에 없다. GDPR의 Right to Erasure(삭제권) 처리, 중복 제거, 늦게 도착한 이벤트의 수정 — 이런 작업이 모두 비싼 full partition rewrite를 요구한다. 테이블이 커질수록 이 비용은 급격히 늘어난다.
❌ Small files 문제
스트리밍 ingestion(Kafka → Spark Structured Streaming)은 마이크로 배치 단위로 파일을 생성한다. 5분 간격으로 쓰면 하루에 파티션당 288개의 파일이 생긴다. 파티션이 수백 개라면 하루에만 수만 개의 파일이 누적된다. 쿼리 엔진은 파일 하나하나를 열고 footer를 읽어야 하므로, 파일 수가 많을수록 쿼리 시작 시간(planning + open overhead)이 늘어난다. S3의 LIST API는 파일 수에 비례해서 지연이 늘고 비용도 증가한다.
❌ 스키마 강제 없음
Hive metastore가 스키마를 관리하지만, 실제 파일이 그 스키마와 다르면 런타임에 파싱 에러가 난다. 소스 팀이 컬럼을 추가하거나 타입을 바꿔도 알림 없이 파일이 쌓이고, 다운스트림 파이프라인이 실행될 때야 오류로 감지된다. schema-on-read 의 유연성이 운영에서는 보이지 않는 시한폭탄이 되는 셈이다.
❌ 느린 메타데이터 조회와 파티션 pruning
파일 레이크에서 쿼리 엔진은 어떤 파일을 읽어야 하는지 알기 위해 LIST API를 반복 호출한다. 파티션이 수천 개이고 각 파티션에 파일이 수백 개라면, 쿼리 플래닝 단계에서만 수천 번의 API 호출이 발생한다. Hive 파티션 pruning은 폴더 구조에 의존하므로, 쿼리 조건이 파티션 컬럼 외의 컬럼을 포함하면 해당 파티션의 모든 파일을 열어봐야 한다.
2. Transaction Log가 바꾼 것
테이블 포맷의 핵심 아이디어는 단순하다: 파일을 직접 추적하지 말고, 파일 변경 내역을 별도의 로그에 기록한다. 이 로그가 있으면 ACID, time travel, 빠른 메타데이터 조회가 모두 가능해진다.
Delta Lake: _delta_log/
Delta Lake는 테이블 디렉토리 안에 _delta_log/ 폴더를 만들고, 모든 변경을 JSON 파일(commit log)로 기록한다. 각 JSON 파일은 어떤 파일이 추가되었고(add), 어떤 파일이 제거되었는지(remove) 담고 있다. 10번째 commit마다 Parquet checkpoint 파일로 합쳐서 오래된 JSON 로그를 읽지 않아도 현재 상태를 빠르게 파악할 수 있다.
s3://my-table/
├── _delta_log/
│ ├── 00000000000000000000.json # 최초 생성
│ ├── 00000000000000000001.json # 첫 번째 변경
│ ├── 00000000000000000010.json # 10번째 변경
│ └── 00000000000000000010.checkpoint.parquet # checkpoint
├── part-00001.parquet
└── part-00002.parquettime travel은 특정 버전의 log만 읽으면 그 시점의 테이블 상태를 재현할 수 있다. VERSION AS OF 5나 TIMESTAMP AS OF '2026-06-01' 쿼리가 동작하는 원리다.
Apache Iceberg: 계층형 메타데이터
Iceberg는 더 정교한 계층 구조를 사용한다. Delta Lake의 log가 commit 순서 기반이라면, Iceberg는 스냅샷(snapshot) 을 중심으로 설계되어 있다.
파일 경로를 가리키는
단일 포인터
REST Catalog
파티션 스펙,
스냅샷 목록 포함
새 파일 작성
(이전 파일 보존)
전체 상태를 나타내는
불변 뷰
파일 목록을 가리킴
목록 + 파티션 범위
요약 통계
목록 + 컬럼별
min/max/null count
Avro 실제 데이터
이 계층 구조의 핵심 장점은 두 가지다.
쿼리 플래닝 속도: Manifest File이 각 데이터 파일의 컬럼별 min/max 통계를 보유하므로, 쿼리 엔진은 파일을 실제로 열지 않고도 어떤 파일을 건너뛸 수 있는지 알 수 있다(file skipping). WHERE event_date = '2026-06-30' AND user_id > 10000 같은 조건에서 해당 범위 밖에 있는 파일은 아예 읽지 않는다.
Hidden Partitioning: Iceberg에서 파티션은 물리적 폴더 구조가 아니라 메타데이터에 정의된다. 사용자는 WHERE event_time >= timestamp 같이 자연스러운 조건을 쓰면 Iceberg가 자동으로 파티션 pruning을 수행한다. 파티션 컬럼을 쿼리에 직접 써야 했던 Hive 방식과 다르다.
3. Transaction Log가 가능하게 하는 네 가지
테이블 포맷이 transaction log를 도입함으로써 file lake에서는 불가능했던 기능들이 가능해진다.
① ACID 트랜잭션 (Optimistic Concurrency Control)
새 데이터를 쓸 때 파일을 먼저 스토리지에 업로드한 뒤, 마지막에 catalog의 포인터를 atomic하게 바꾼다. 두 write가 동시에 발생하면 하나만 성공하고 나머지는 retry한다(Optimistic Concurrency Control). 읽기는 항상 커밋된 스냅샷을 본다 — 쓰기 중에 읽어도 불완전한 데이터는 보이지 않는다.
| 특성 | File Lake | Lakehouse (테이블 포맷) |
|---|---|---|
| 원자성(Atomicity) | 없음 (파일 단위) | 있음 (commit 단위) |
| 일관성(Consistency) | 없음 | 있음 (스냅샷 격리) |
| 격리성(Isolation) | 없음 | 있음 (OCC) |
| 지속성(Durability) | 파일 있으면 OK | 있음 (log 있으면 복구 가능) |
② Time Travel (버전 조회)
모든 이전 스냅샷/버전이 보존되므로, 특정 시점의 테이블 상태를 재조회할 수 있다.
-- Iceberg: 특정 스냅샷 ID로 조회
SELECT * FROM orders FOR VERSION AS OF 8157992549605115800;
-- Delta Lake: 특정 시점으로 조회
SELECT * FROM orders TIMESTAMP AS OF '2026-06-25 00:00:00';
-- 특정 스냅샷들을 비교해서 데이터 이상 원인을 파악하거나
-- 파이프라인 실수를 복구할 때 유용하다③ Schema Evolution (안전한 스키마 변경)
테이블 포맷에서 컬럼 추가, 이름 변경, 타입 변경은 메타데이터 파일만 수정하는 metadata-only 변경이다. 실제 데이터 파일을 건드리지 않는다. 이전 파일에 없는 컬럼은 자동으로 null로 처리하고, 컬럼 이름이 바뀌어도 내부 ID 기반으로 추적하므로 파이프라인이 깨지지 않는다.
④ 빠른 Compaction과 Cleanup
작은 파일들을 합치는 compaction이 테이블 포맷에서 안전하게 된다. Compaction은 논리적으로는 같은 데이터를 유지하면서 더 큰 파일로 묶는 작업이다. Transaction log가 있으므로 compaction 중에도 읽기/쓰기가 충돌 없이 동작하고, 오래된 스냅샷과 그에 속한 파일들은 VACUUM / expire_snapshots 명령으로 안전하게 삭제할 수 있다.
4. 세 가지 테이블 포맷 간략 비교
Lakehouse 생태계에는 현재 세 가지 주요 오픈소스 테이블 포맷이 있다.
| 항목 | Apache Iceberg | Delta Lake | Apache Hudi |
|---|---|---|---|
| 출처 | Netflix (2017) | Databricks (2019) | Uber (2016) |
| 메타데이터 구조 | Snapshot → Manifest List → Manifest | Transaction Log (JSON + Checkpoint) | Timeline + Index + Data |
| 멀티 엔진 지원 | 최우선 설계 목표 | Spark 우선, 점차 확장 | Spark 우선, 확장 중 |
| Hidden Partitioning | 지원 | 미지원 (폴더 기반) | 미지원 |
| Upsert (CDC) | MERGE 지원 | MERGE 지원 | Upsert 강점 (MOR/COW) |
| Time Travel | 스냅샷 기반 | 버전 기반 | Commit Timeline 기반 |
2024년 이후 Iceberg가 멀티 엔진 지원 측면에서 사실상 업계 표준으로 자리잡는 추세다. Trino, Flink, DuckDB, Snowflake, BigQuery 등 주요 쿼리 엔진이 Iceberg를 기본 지원한다. Delta Lake는 Databricks 환경에서 강점이 있고, Hudi는 Uber와 같은 CDC가 많은 환경에서 여전히 활발히 쓰인다.
마무리: File Lake에서 Lakehouse로의 전환 관점
테이블 포맷을 도입하면 파일 레이크가 가진 구조적 문제를 해결할 수 있다. 하지만 단순히 기술을 바꾸는 것이 아니라, 운영 방식도 함께 바뀐다.
- 데이터 파일 뒤에 메타데이터 레이어가 생기므로, catalog 서버(Glue, Nessie, Polaris)의 안정적 운영이 새로운 운영 과제가 된다.
- Compaction을 주기적으로 실행하지 않으면 small files 문제가 다시 누적된다.
- Snapshot expiration을 관리하지 않으면 메타데이터가 무한정 증가한다.
이 시리즈의 나머지 장에서는 Iceberg, Delta Lake, Hudi 각각의 내부 동작과, 파일 레이아웃 최적화, 스키마 진화, 쿼리 엔진 연동, 운영 체크리스트를 차례로 다룬다.
References
- Apache Iceberg, Official Specification, https://iceberg.apache.org/spec/
- Onehouse, "Apache Iceberg vs Delta Lake vs Apache Hudi: Feature Comparison Deep Dive," https://www.onehouse.ai/blog/apache-hudi-vs-delta-lake-vs-apache-iceberg-lakehouse-feature-comparison
- Dremio, "Apache Iceberg vs Delta Lake: Which is right for your lakehouse?," https://www.dremio.com/blog/apache-iceberg-vs-delta-lake/
- OLake, "Apache Iceberg Metadata Explained: Snapshots & Manifests," https://olake.io/blog/2025/10/03/iceberg-metadata/
- Conduktor, "Iceberg Table Architecture: Metadata and Snapshots," https://www.conduktor.io/glossary/iceberg-table-architecture-metadata-and-snapshots
- Artie, "How Apache Iceberg Works: Catalogs, Manifests & Snapshots," https://www.artie.com/blogs/how-apache-iceberg-works
- Alex Merced, "The Ultimate Guide to Open Table Formats: Iceberg, Delta Lake, Hudi, Paimon, and DuckLake," https://dev.to/alexmercedcoder/the-ultimate-guide-to-open-table-formats-iceberg-delta-lake-hudi-paimon-and-ducklake-dnk
- Alex Merced, "The 2025 & 2026 Ultimate Guide to the Data Lakehouse," https://dev.to/alexmercedcoder/the-2025-2026-ultimate-guide-to-the-data-lakehouse-and-the-data-lakehouse-ecosystem-dig