Data Engineering

데이터 엔지니어링 시리즈 #10: 데이터 레이크 vs 웨어하우스 - 레이크하우스 아키텍처

데이터 엔지니어링 시리즈 #10: 데이터 레이크 vs 웨어하우스 - 레이크하우스 아키텍처

대상 독자: 충분한 경험을 가진 백엔드/풀스택 엔지니어로, PostgreSQL ACID에 익숙하지만 데이터 레이크/웨어하우스는 처음인 분

이 편에서 다루는 것

"S3에 Parquet 올려두면 되는 거 아닌가요?" 라는 질문에서 시작합니다. 왜 Delta Lake 같은 테이블 포맷이 필요한지, 그리고 레이크하우스가 무엇인지 배웁니다.


데이터 저장소의 진화

세대별 변화


데이터 웨어하우스 (Data Warehouse)

특징

PostgreSQL과의 비교

특성PostgreSQL (OLTP)BigQuery (DW)
목적트랜잭션 처리분석 쿼리
스토리지Row-basedColumn-based
스케일수직 확장무한 수평 확장
비용서버 비용쿼리당 비용
쿼리 속도단건 빠름집계 빠름

데이터 레이크 (Data Lake)

특징

데이터 레이크의 문제점


레이크하우스 (Lakehouse)

두 세계의 통합

핵심 가치

특성레이크웨어하우스레이크하우스
저장 비용저렴 ✅비쌈저렴 ✅
ACID
오픈 포맷❌ (벤더)
ML 지원제한적
SQL 분석제한적

Medallion Architecture (Bronze/Silver/Gold)

레이크하우스에서 데이터를 계층화하여 관리하는 표준 패턴입니다. Databricks가 제안하고 현재 업계 표준으로 자리잡았습니다.

출처: Databricks - Medallion Architecture, Armbrust et al., "Delta Lake: High-Performance ACID Table Storage over Cloud Object Stores" (VLDB 2020)

세 레이어 구조

각 레이어의 역할

Layer목적데이터 특성소비자
Bronze원본 보존Raw, 스키마 유연데이터 엔지니어
Silver정제/통합Cleaned, 조인됨데이터 분석가, DS
Gold비즈니스 집계Aggregated, 최적화BI, 경영진

코드 예시

# Bronze: 원본 그대로 저장
raw_events = spark.readStream \
    .format("kafka") \
    .option("kafka.bootstrap.servers", "kafka:9092") \
    .option("subscribe", "user_events") \
    .load()

raw_events.writeStream \
    .format("delta") \
    .option("checkpointLocation", "/checkpoints/bronze") \
    .start("/delta/bronze/events")

# Silver: 정제 및 스키마 적용
bronze_df = spark.read.format("delta").load("/delta/bronze/events")

silver_df = bronze_df \
    .select(from_json(col("value"), schema).alias("data")) \
    .select("data.*") \
    .filter(col("user_id").isNotNull()) \
    .dropDuplicates(["event_id"])

silver_df.write.format("delta").mode("overwrite") \
    .save("/delta/silver/events")

# Gold: 비즈니스 집계
silver_df = spark.read.format("delta").load("/delta/silver/events")

gold_df = silver_df \
    .groupBy("date", "event_type") \
    .agg(
        count("*").alias("event_count"),
        countDistinct("user_id").alias("unique_users")
    )

gold_df.write.format("delta").mode("overwrite") \
    .save("/delta/gold/daily_metrics")

왜 이 패턴인가?

문제Medallion 해결책
원본 데이터 유실Bronze에 원본 보존
스키마 변경 대응Bronze는 스키마 유연, Silver에서 검증
재처리 필요Bronze → Silver → Gold 순서대로 재실행
다양한 소비자 니즈레이어별 최적화된 데이터 제공

Delta Lake 심층 분석

ACID 트랜잭션

Delta Lake의 방법: 트랜잭션 로그 (_delta_log/)

table/
├── _delta_log/
│   ├── 00000000000000000000.json  # 첫 트랜잭션
│   ├── 00000000000000000001.json  # 두 번째
│   └── 00000000000000000002.json  # 세 번째
├── part-00000.parquet
├── part-00001.parquet
└── part-00002.parquet

Time Travel

# 특정 버전으로 읽기
df = spark.read.format("delta") \
    .option("versionAsOf", 2) \
    .load("/delta/users")

# 특정 시점으로 읽기
df = spark.read.format("delta") \
    .option("timestampAsOf", "2024-01-01") \
    .load("/delta/users")

# 히스토리 조회
from delta.tables import DeltaTable

dt = DeltaTable.forPath(spark, "/delta/users")
dt.history().show()

Schema Evolution

# 자동 스키마 병합
df_new.write.format("delta") \
    .mode("append") \
    .option("mergeSchema", "true") \
    .save("/delta/users")

# 스키마 덮어쓰기 (주의!)
df_new.write.format("delta") \
    .mode("overwrite") \
    .option("overwriteSchema", "true") \
    .save("/delta/users")

MERGE (Upsert)

from delta.tables import DeltaTable

# 타겟 테이블
target = DeltaTable.forPath(spark, "/delta/users")

# 소스 데이터 (업데이트할 데이터)
source = spark.read.parquet("/staging/users")

# MERGE 실행
target.alias("t").merge(
    source.alias("s"),
    "t.user_id = s.user_id"
).whenMatchedUpdate(
    set={
        "name": "s.name",
        "email": "s.email",
        "updated_at": "current_timestamp()"
    }
).whenNotMatchedInsert(
    values={
        "user_id": "s.user_id",
        "name": "s.name",
        "email": "s.email",
        "created_at": "current_timestamp()"
    }
).execute()

Delta Lake vs Apache Iceberg

비교

특성Delta LakeApache Iceberg
개발사DatabricksNetflix→Apache
Spark 지원최고좋음
Flink 지원제한적좋음
Trino 지원좋음좋음
파티셔닝명시적Hidden (투명)
채택율높음증가 중

선택 가이드


아키텍처 결정 가이드

언제 무엇을 선택하나?


정리


다음 편 예고

11편: 데이터 모델링에서는 분석용 모델링을 다룹니다:

  • Star Schema vs Snowflake Schema
  • Fact Table vs Dimension Table
  • Slowly Changing Dimensions (SCD)

참고 자료

Share

Related Articles

Comments

이 블로그는 제가 알고 있는 것들을 잊지 않기 위해 기록하는 공간입니다.
직접 작성한 글도 있고, AI의 도움을 받아 정리한 글도 있습니다.
정확하지 않은 내용이 있을 수 있으니 참고용으로 봐주세요.

© 2026 Seogyu Kim