현대적 모델링 패턴 — 역할연기 차원, 액티비티 스키마, 반정형 데이터
50 Data Modeling Interview Questions for DEs (2026 Guide) — DataVidhya blog Advanced Patterns, Q34-38 (pp.12-14)
이 모듈을 다 읽으면
- 역할연기 차원의 개념과 셀프 도큐멘팅을 위한 뷰 활용법을 설명할 수 있다
- 커버리지형 팩트리스 팩트로 '부재(absence)'를 분석하는 방법을 설명할 수 있다
- 액티비티 스키마 패턴의 장단점을 설명할 수 있다
- JSON/배열 같은 반정형 데이터를 웨어하우스에서 다루는 모범 사례를 설명할 수 있다
하나의 차원 테이블을 여러 역할로 재사용하는 역할연기 차원, 부재를 분석하는 커버리지 팩트리스 팩트, 이벤트 스트림을 한 테이블에 몰아넣는 액티비티 스키마, 넓은 테이블과 정규화 테이블의 트레이드오프, 그리고 JSON/배열 모델링을 다룬다.
역할연기 차원과 커버리지 팩트리스 팩트
역할연기 차원(role-playing dimension)은 하나의 물리적 차원 테이블이 같은 팩트 테이블 안에서 여러 의미로 재사용되는 것이다. 대표적인 예는 배송 팩트에서 dim_date가 order_date_key, ship_date_key, delivery_date_key 세 번 등장하는 경우다 — 각 외래 키는 '다른 역할'을 하지만 모두 같은 날짜 차원을 가리킨다. 실무에서는 BI 사용자를 위해 dim_order_date, dim_ship_date 같은 뷰를 만들어 모델을 셀프 도큐멘팅하게 만든다.
09번 모듈에서 다룬 팩트리스 팩트 테이블 중 커버리지형은 09번에서 이미 설명했지만, 다시 강조할 가치가 있다 — 실제 발생한 일이 아니라 발생할 수 있었던 조건을 기록한다는 점에서, 리테일 체인이 모든 제품-매장-날짜 조합 중 프로모션이 활성화된 조합을 담은 테이블을 만들고 실제 판매 팩트와 LEFT JOIN하면, 어떤 프로모션이 어떤 매장에서 판매 0건을 기록했는지 찾을 수 있다 — 거래 데이터만으로는 절대 도출할 수 없는 통찰이다.
핵심 포인트
- 역할연기 차원은 하나의 물리 차원 테이블을 같은 팩트 테이블에서 여러 역할(주문일/배송일/도착일)로 재사용한다.
- 역할별 뷰(dim_order_date 등)를 만들어 모델을 셀프 도큐멘팅하게 만드는 것이 실무 관행이다.
액티비티 스키마 패턴
액티비티 스키마는 모든 사용자 행동을 entity_id, activity_type, timestamp, 그리고 JSON feature 컬럼을 가진 단 하나의 좁은 테이블에 저장하는 현대적 패턴이다. 클릭, 가입, 구매 각각을 위한 별도의 팩트 테이블을 만드는 대신 모든 것을 하나의 스트림에 담는다. 그런 다음 이 테이블을 셀프 조인해 고객 여정이나 퍼널 분석을 만든다. 트레이드오프는 쿼리 복잡도다 — 큰 액티비티 테이블에 대한 셀프 조인은 비용이 클 수 있다. 이는 웨어하우스 전통적인 차원 모델링을 대체하는 것이 아니라, 이벤트 기반 분석 제품에 유용한 패턴으로 이해해야 한다.
핵심 포인트
- 액티비티 스키마는 모든 사용자 행동을 entity_id/activity_type/timestamp/JSON feature를 가진 단일 좁은 테이블에 담는다.
- 고객 여정·퍼널 분석에는 강하지만 대규모 셀프 조인 비용이 트레이드오프이며, 전통적 차원 모델링의 대체재가 아니다.
넓은 테이블 vs 정규화 테이블, 반정형 데이터 모델링
넓은 테이블(wide table)은 읽기 성능을 위해 데이터를 미리 조인하고, 정규화 테이블은 쓰기 무결성을 위해 중복을 최소화한다. 현대 컬럼형 엔진에서는 요청한 컬럼만 스캔하므로 200개 컬럼을 가진 넓은 테이블이라도 5개 컬럼만 읽는 쿼리에는 불이익이 없다. 다만 넓은 테이블은 유지보수와 추론이 더 어렵다. 실무적 기본값은 변환 계층(dbt staging/intermediate 모델)에서 정규화하고, 최종 마트로 넓은 테이블을 구체화(materialize)하는 것이다.
JSON 페이로드 같은 반정형 데이터는 변환 계층에서 타입이 지정된 컬럼으로 평탄화해야 한다. 대부분의 웨어하우스(BigQuery, Snowflake)가 JSON을 네이티브로 쿼리할 수 있지만, 프로덕션 대시보드에서 JSON 접근에 의존하는 것은 취약하다 — 소스의 스키마 변경이 다운스트림 쿼리를 조용히 깨뜨리기 때문이다. 모범 사례는 알려진 필드를 컬럼으로 추출하고 원본 JSON을 대비책(fallback)으로 남겨두는 것이다. 배열은 CROSS JOIN으로 언네스트(unnest)해 별도 행으로 풀어내야 하며, 바로 이 지점에서 그레인(08번 모듈) 결정이 중요해진다.
핵심 포인트
- 컬럼형 엔진에서는 넓은 테이블도 필요한 컬럼만 스캔하면 성능 불이익이 없지만 유지보수는 더 어렵다.
- 실무 기본값: 변환 계층(dbt staging/intermediate)에서 정규화하고 최종 마트에서 넓은 테이블을 구체화한다.
- JSON은 알려진 필드를 컬럼으로 추출하고 원본을 fallback으로 남기며, 배열은 CROSS JOIN으로 언네스트한다.