729 words
4 minutes
[DE Design Pattern]05-3. Dynamic Joiner

3. Data Decoration: Wrapper#

01. Pattern Overview#

  • Wrapper는 원본 레코드에 computed attributes
  • 원본 값과 계산된 값을 분리하는 패턴.
  • 디버깅을 위해 원본을 보존하면서도 다운스트림 소비자에게 가공된 값을 제공

소프트웨어 엔지니어링의 Decorator/Wrapper 패턴과 같은 아이디어입니다. 원본 객체를 감싸는 envelope을 추가하는 것입니다.

02. 4가지 Wrapper 구현 방식#

  • 구현 1: 원본은 flat, 계산값은 nested struct → {col1, col2, ..., computed: {a, b}}
  • 구현 2: 원본은 nested struct, 계산값은 flat → {a, b, raw_data: {col1, col2}}
  • 구현 3: 모두 같은 레벨에 flat → {col1, col2, a, b}
  • 구현 4: 원본 테이블과 계산값 테이블을 분리 → 키로 JOIN

언제 어떤 구현을 고려하는가

  • 구현 1, 2는 비정규화(읽기 빠름),
  • 구현 4는 정규화(논리적 격리 가능, 원본 구조 변경 불필요).

03. 원본 flat + 계산값 nested#

from pyspark.sql import functions as F
# 메타데이터 컨텍스트를 struct로 추가
visits_wrapped = (
visits
.withColumn("processing_context", F.struct(
F.lit(job_version).alias("job_version"),
F.lit(str(processing_time)).alias("processing_time")
))
.withColumn("output", F.struct(
F.col("value").cast("string").alias("raw_data"),
F.col("processing_context")
))
)

04. 계산값 flat + 원본 nested (SQL)#

-- 계산값을 1급 컬럼으로, 원본은 struct로 보관
SELECT
CASE WHEN context.user.connected_since IS NULL
THEN false ELSE true END AS is_connected,
CONCAT_WS('-', page, context.referral) AS page_referral_key,
STRUCT(visit_id, event_time, user_id, page, context) AS raw_data
FROM input_visits

05. 구현 1의 SQL 변형: NAMED_STRUCT#

-- 원본 컬럼 유지 + decorated struct 추가
SELECT *, NAMED_STRUCT(
'is_connected',
CASE WHEN context.user.connected_since IS NULL
THEN false ELSE true END,
'page_referral_key', CONCAT_WS('-', page, context.referral)
) AS decorated
FROM input_visits

NAMED_STRUCTkey, value 쌍으로 struct를 생성하는 편의 함수입니다.

주요 트레이드오프#

  • Domain Split: 하나의 도메인(예: user) 속성이 raw와 computed 두 곳에 분산됨. 소비자가 두 위치를 알아야 함 → Silver 레이어까지만 적용하고, 최종 사용자 노출 레이어에서는 통합하는 것을 권장
  • Size 영향: 계산값이 레코드의 일부가 되므로 전체 크기와 네트워크 트래픽 증가 → columnar 포맷의 projection pushdown으로 완화 (필요한 컬럼만 물리적으로 읽기)

Concept

  • Wrapper 패턴 : 원본 레코드에 계산된 속성을 추가하면서 원본과 계산값을 명확히 분리하는 데코레이션 패턴
  • Envelope : 원본 값과 계산된 값을 감싸는 상위 추상화 구조
  • Denormalized Wrapper (구현 1, 2) : 원본과 계산값을 같은 레코드에 nested struct로 포함. 읽기 성능 유리
  • Normalized Wrapper (구현 4) : 원본과 계산값을 별도 테이블로 분리. 논리적 격리와 원본 구조 보존에 유리
  • NAMED_STRUCT : SQL에서 key-value 쌍으로 struct를 생성하는 함수
  • Projection Pushdown : columnar 포맷에서 필요한 컬럼만 물리적으로 읽어 I/O를 최적화하는 기법
  • Domain Split : Wrapper 적용 시 하나의 도메인 속성이 raw/computed 두 곳에 분산되는 문제

[DE Design Pattern]05-3. Dynamic Joiner
https://yjinheon.netlify.app/posts/02de/de-design-pattern/05_data_value/05-03-data-decoration-wrapper/
Author
Datamind
Published at
2025-03-14
License
CC BY-NC-SA 4.0