Skip to main content

Column Mapping Mode 내부 동작 상세

Column Mapping은 Delta Lake에서 스키마 유연성을 크게 높이는 핵심 기능입니다. 내부 동작 원리를 이해하면 제한사항과 문제를 예방할 수 있습니다.

물리적 이름 vs 논리적 이름

Column Mapping이 활성화되면, 테이블의 논리적 컬럼 이름(사용자가 보는 이름)과 물리적 컬럼 이름(Parquet 파일 내 실제 이름)이 분리됩니다.
| Column Mapping 모드 | 논리적 이름 | 물리적 이름 | 설명 |
|--------------------|-----------|-----------|------|
| `none` (기본, 비활성) | customer_name | customer_name | 동일 |
| `name` (활성화) | customer_name | col-a1b2c3d4-e5f6 | UUID 기반 물리적 이름 |
모드논리적 이름물리적 이름이름 변경 시
nonecustomer_namecustomer_name (동일)불가 — 모든 Parquet 파일을 다시 써야 함
namecustomer_namecol-a1b2c3d4 (UUID)메타데이터만 변경 — 파일 재작성 불필요

메타데이터 저장 위치

Column Mapping 정보는 Delta Log의 메타데이터 액션 에 저장됩니다.
// Delta Log의 metadata action 내 column mapping 정보
{
  "metaData": {
    "schemaString": "{\"fields\":[{\"name\":\"customer_name\",\"metadata\":{\"delta.columnMapping.id\":1,\"delta.columnMapping.physicalName\":\"col-a1b2c3d4-e5f6\"}}]}",
    "configuration": {
      "delta.columnMapping.mode": "name",
      "delta.columnMapping.maxColumnId": "5"
    }
  }
}

이름 변경/삭제의 실제 동작

-- Column Mapping = name 일 때
ALTER TABLE products RENAME COLUMN customer_name TO client_name;

-- 실제 일어나는 일:
-- 1. Delta Log에 새 metadata action 기록
-- 2. 논리적 이름: customer_name → client_name (변경)
-- 3. 물리적 이름: col-a1b2c3d4 (변경 없음!)
-- 4. 기존 Parquet 파일: 수정 없음 (제로 카피 연산)
-- → 수 TB 테이블이라도 이름 변경이 밀리초 단위로 완료됩니다

Column Mapping 활성화 시 제한사항

Column Mapping을 활성화하면 강력한 기능을 얻지만, 몇 가지 중요한 제한사항이 있습니다.

호환성 제한

제한사항설명해결 방법
되돌리기 불가name 모드를 none으로 되돌릴 수 없습니다새 테이블을 생성하여 데이터를 복사
Delta 프로토콜 요구Reader Version 2, Writer Version 5 이상 필요구버전 Delta 클라이언트에서 읽기 불가
Streaming 읽기Column Mapping 변경 후 기존 스트리밍 체크포인트가 무효화될 수 있음스트림을 중지 → 체크포인트 삭제 → 재시작
Delta Sharing일부 구버전 소비자가 Column Mapping 테이블을 읽지 못할 수 있음소비자 클라이언트 버전 확인
Time Travel이름 변경 전 버전을 조회할 때 현재 논리적 이름으로 접근해야 함SELECT client_name FROM t VERSION AS OF 5 (이전에는 customer_name이었더라도)
CLONEColumn Mapping 테이블의 SHALLOW CLONE은 제한적DEEP CLONE 사용 권장

Streaming과 Column Mapping의 상호작용

⚠️ 중요: Column Mapping이 활성화된 테이블에서 컬럼을 이름 변경하거나 삭제 하면, 해당 테이블을 읽는 Structured Streaming 쿼리가 실패 할 수 있습니다.
# Column Mapping 변경 후 스트리밍 재시작 절차
# 1. 스트리밍 쿼리 중지
streaming_query.stop()

# 2. 체크포인트 초기화 (또는 삭제)
dbutils.fs.rm("/checkpoints/my_stream", recurse=True)

# 3. 스트리밍 쿼리 재시작 (새 체크포인트 경로)
(spark.readStream.table("source_table")
    .writeStream
    .option("checkpointLocation", "/checkpoints/my_stream_v2")  # 새 경로
    .table("target_table"))

대규모 스키마 변경 전략 (컬럼 100개+ 추가)

실무에서는 스키마가 한 번에 수십~수백 개의 컬럼이 추가되는 경우가 있습니다(예: IoT 센서 데이터, 설문 데이터 등).

대량 컬럼 추가 방법 비교

방법장점단점적합한 경우
ALTER TABLE ADD COLUMN 반복명시적, 각 컬럼에 주석 가능100개면 100번 DDL 실행. 느림소규모 변경(10개 이내)
mergeSchema로 자동 추가DataFrame 쓰기 한 번으로 모든 컬럼 추가타입/이름 제어가 어려움소스의 스키마를 그대로 수용할 때
CREATE TABLE AS SELECT새 테이블에 원하는 스키마 구성데이터 전체 복사 필요대폭 스키마 재설계 시
REPLACE TABLE스키마를 완전히 교체하면서 동일 테이블명 유지기존 데이터 삭제됨빈 테이블 재정의

대량 컬럼 추가 실습 (mergeSchema 활용)

# 100개 이상의 새 컬럼이 있는 DataFrame을 기존 테이블에 병합
from pyspark.sql.types import StructType, StructField, StringType, DoubleType

# 새 스키마로 DataFrame 생성 (기존 컬럼 + 새 100개 컬럼)
new_columns = [StructField(f"sensor_{i:03d}", DoubleType(), True) for i in range(100)]
# ... DataFrame 준비 ...

# mergeSchema로 한 번에 추가
df_new_data.write \
    .mode("append") \
    .option("mergeSchema", "true") \
    .saveAsTable("catalog.schema.sensor_readings")

# 결과: 기존 테이블에 100개의 새 컬럼이 자동 추가됩니다
# 기존 행의 새 컬럼 값은 NULL로 채워집니다

스키마 변경 시 다운스트림 영향 관리

영향설명대응
하위 뷰/쿼리SELECT *을 사용하는 뷰는 새 컬럼이 자동 포함됨명시적 컬럼 목록 사용 권장
BI 대시보드새 컬럼이 대시보드에 예기치 않게 나타날 수 있음Gold 레이어에서 스키마를 고정하여 BI에 제공
ML 파이프라인피처 수가 변경되면 모델 학습이 실패할 수 있음피처 선택 로직에서 컬럼 목록을 명시적으로 지정
Delta SharingShare에 추가된 테이블의 스키마가 변경되면 소비자에게 영향스키마 변경 전 소비자에게 사전 공지

Auto Loader 스키마 진화와의 관계

Auto Loader의 스키마 진화는 Delta Lake의 스키마 진화(mergeSchema)와 함께 동작하며, 소스 파일 → 타겟 테이블 전체 흐름에서 스키마를 관리합니다.

Auto Loader + Delta 스키마 진화 전체 흐름

[소스 파일]                  [Auto Loader]              [Delta 테이블]
새 JSON 파일 도착        →   스키마 감지               →  스키마 진화
(새 필드 "loyalty_tier"      (addNewColumns 모드)         (mergeSchema=true)
 포함)                       새 컬럼 발견!                loyalty_tier 컬럼 추가
                             스키마 파일 업데이트          기존 행은 NULL
                             스트림 재시작 (자동)

schemaEvolutionMode와 Delta mergeSchema의 관계

Auto Loader 모드Delta mergeSchema동작
addNewColumns + mergeSchema=true활성새 컬럼이 소스에서 타겟까지 자동 전파됩니다
addNewColumns + mergeSchema=false비활성Auto Loader는 새 컬럼을 감지하지만 Delta 쓰기에서 에러 발생
rescue + 어떤 설정이든무관새 컬럼은 _rescued_data에 JSON으로 저장. 테이블 스키마 변경 없음
failOnNewColumns무관새 컬럼 발견 시 스트림이 즉시 중단됩니다

Rescue 모드 활용 패턴 (프로덕션 권장)

# 프로덕션에서 안전한 스키마 진화 패턴
# 1단계: rescue 모드로 안전하게 수집
(spark.readStream
    .format("cloudFiles")
    .option("cloudFiles.format", "json")
    .option("cloudFiles.schemaLocation", "/checkpoints/schema")
    .option("cloudFiles.schemaEvolutionMode", "rescue")  # 새 컬럼은 _rescued_data에 저장
    .load("s3://bucket/raw/")
    .writeStream
    .option("checkpointLocation", "/checkpoints/bronze")
    .table("catalog.schema.bronze_events"))

# 2단계: _rescued_data 분석하여 새 컬럼 확인
rescued = spark.sql("""
    SELECT _rescued_data, COUNT(*) as cnt
    FROM catalog.schema.bronze_events
    WHERE _rescued_data IS NOT NULL
    GROUP BY _rescued_data
    ORDER BY cnt DESC
""")

# 3단계: 확인 후 DDL로 명시적으로 스키마 변경
# ALTER TABLE catalog.schema.bronze_events ADD COLUMN loyalty_tier STRING;

# 4단계: 스트림 재시작 (체크포인트 유지, 새 컬럼 포함)
💡 프로덕션 권장 패턴: Bronze 레이어에서는 rescue 모드를 사용하여 예기치 않은 스키마 변경을 안전하게 캡처하고, Silver/Gold 레이어에서는 DDL로 명시적 스키마 관리를 하는 것이 가장 안정적입니다.

SDP(선언적 파이프라인)에서의 스키마 진화 처리

SDP(Structured Data Pipelines, 구 DLT)에서도 스키마 진화를 처리할 수 있습니다.

SDP에서의 스키마 진화 설정

# SDP 파이프라인에서 Auto Loader + 스키마 진화
import dlt

@dlt.table(
    comment="Bronze: 원본 이벤트 데이터"
)
def bronze_events():
    return (
        spark.readStream
        .format("cloudFiles")
        .option("cloudFiles.format", "json")
        .option("cloudFiles.schemaEvolutionMode", "addNewColumns")
        .option("cloudFiles.schemaLocation", "/checkpoints/schema")
        .load("s3://bucket/raw/events/")
    )

@dlt.table(
    comment="Silver: 정제된 이벤트"
)
@dlt.expect_or_drop("valid_event_type", "event_type IS NOT NULL")
def silver_events():
    return dlt.read_stream("bronze_events").select(
        "event_id",
        "event_type",
        "user_id",
        "timestamp"
        # 명시적 컬럼 선택 → Bronze에 새 컬럼이 추가되어도 Silver에는 영향 없음
    )

SDP 스키마 진화 시 주의사항

주의사항설명대응
Full Refresh 트리거스키마 변경 시 SDP가 전체 재처리를 할 수 있음pipelines.reset.allowed = false 설정으로 방지
Expectations 깨짐새 컬럼에 의존하는 Expectation이 없으면 괜찮지만, 기존 컬럼이 변경되면 문제컬럼 타입 변경은 별도 마이그레이션으로 처리
Bronze → Silver 전파Bronze에서 SELECT *을 사용하면 새 컬럼이 자동 전파됨Silver에서는 명시적 컬럼 목록 사용
파이프라인 재시작 필요일부 스키마 변경 후 파이프라인을 수동 재시작해야 할 수 있음모니터링 및 자동 재시작 설정

정리

기능설명
스키마 강제정의된 스키마와 다른 데이터를 거부하여 데이터 품질을 보장합니다
스키마 진화DDL 또는 mergeSchema 옵션으로 스키마를 안전하게 변경합니다
Column Mapping물리적/논리적 컬럼을 분리하여 이름 변경/삭제를 가능하게 합니다
Auto Loader 스키마 진화소스 데이터의 스키마 변경을 자동으로 감지하고 처리합니다
Column Mapping 내부UUID 기반 물리적 이름을 사용하여 논리적 이름과 분리합니다
Column Mapping 제한되돌리기 불가, 스트리밍 체크포인트 무효화, SHALLOW CLONE 제한이 있습니다
대량 스키마 변경mergeSchema로 한 번에 수백 개 컬럼 추가가 가능합니다
rescue 모드새 컬럼을 _rescued_data에 저장하여 프로덕션에서 안전하게 처리합니다
SDP 스키마 진화Bronze에서 자동 진화, Silver/Gold에서 명시적 컬럼 선택이 권장됩니다

참고 링크