← 목록으로
데이터베이스
#SCD#차원이력관리#서러게이트키#데이터웨어하우스#차원모델링
최종 업데이트 · 2026-11-07

점진적 차원 변경(SCD, Slowly Changing Dimension)

1. 개요

가. 정의

점진적 차원 변경(SCD, Slowly Changing Dimension)이란 데이터 웨어하우스의 차원(Dimension) 테이블에서 속성 값이 시간이 지남에 따라 느리게 바뀔 때, 그 변경을 어떻게 저장·추적할 것인가를 규정하는 차원 모델링 기법이다. 변경 이력의 보존 여부와 방식에 따라 Type 0부터 Type 6까지로 유형화한다.

랄프 킴벌(Ralph Kimball)이 체계화한 이 개념의 핵심 질문은 단순하지만 강력하다. "고객의 주소가 서울에서 부산으로 바뀌었을 때, 과거 주문은 서울에서 발생한 것으로 남겨야 하는가, 아니면 모두 부산으로 바꿔야 하는가?" 이 질문에 대한 답이 분석 결과의 정확성을 좌우한다. 차원은 팩트(Fact, 측정값)에 맥락을 부여하는 "누가·무엇을·어디서·언제"에 해당하며, 그 맥락이 변할 때 이를 어떻게 다루느냐가 곧 이력 관리 전략이다.

SCD는 "느리게(slowly)"라는 단어가 핵심이다. 상품 분류, 고객 등급, 조직 소속, 지역 코드처럼 드물게, 비정기적으로 바뀌는 속성이 대상이며, 초당 수천 건씩 쏟아지는 거래 데이터(팩트)와는 성격이 다르다. 빠르게 변하는 속성은 오히려 팩트로 분리하거나 별도의 미니 차원으로 뽑아내는 것이 정석이다.

나. 등장 배경과 필요성

전통적인 운영계(OLTP) 데이터베이스는 "현재 상태"만을 관리한다. 고객이 주소를 바꾸면 기존 레코드를 UPDATE로 덮어쓰고, 과거 주소는 흔적 없이 사라진다. 운영 업무에는 최신 값만 있으면 충분하므로 이는 합리적이다. 그러나 분석계(OLAP)에서는 이야기가 전혀 달라진다. "지역별 매출 추이"를 분석하는데 고객의 과거 주소가 사라졌다면, 3년 전 매출을 현재 주소 기준으로 왜곡해 집계하게 되어 역사적 사실(historical truth)이 훼손된다.

이 문제는 특히 규제·감사 영역에서 치명적이다. 금융권의 신용등급 변동 이력, 보험의 피보험자 속성 변화, 의료의 환자 상태 추적은 "그 시점에 어떤 값이었는가"를 정확히 재현할 수 있어야 한다. 단순 덮어쓰기는 이러한 시점 재현(point-in-time reconstruction) 능력을 원천적으로 제거한다. SCD는 바로 이 간극 — 운영계의 "현재만 유지"와 분석계의 "이력 보존 요구" 사이 — 을 메우기 위해 등장했다.

또한 데이터 웨어하우스가 전사적 의사결정의 단일 진실 공급원(Single Source of Truth)으로 자리 잡으면서, 차원 변경을 일관된 규칙 없이 그때그때 처리하면 분석 결과가 분석가·쿼리마다 달라지는 혼란이 발생한다. SCD는 변경 처리 방식을 유형으로 표준화하여, 어떤 속성은 이력을 남기고 어떤 속성은 덮어쓸지를 설계 시점에 명시적으로 결정하게 만든다. 이로써 재현 가능하고 감사 가능한 분석 기반이 확보된다.

2. 차원 모델에서 SCD의 위치와 전체 구조

SCD를 이해하려면 먼저 차원 모델(스타 스키마)에서 차원과 팩트의 관계를 보아야 한다. 팩트 테이블은 측정값(매출액·수량)과 함께 각 차원을 가리키는 외래키를 가지며, 이 외래키가 운영계의 자연키(business key, 예: 고객번호)가 아니라 서러게이트 키(surrogate key)라는 점이 SCD의 출발점이다. 서러게이트 키는 DW가 자체 생성하는 무의미한 일련번호로, "같은 고객의 서로 다른 시점 버전"을 별개의 행으로 구분할 수 있게 해 준다. 이력을 남기는 거의 모든 SCD 유형은 이 서러게이트 키 위에서 성립한다.

flowchart LR
    subgraph SRC["운영계(OLTP)"]
      OP[("고객 원천 테이블<br/>(현재 값만 UPDATE)")]
    end
    OP --> ETL["ETL / ELT 적재<br/>(변경 감지·유형별 처리)"]
    subgraph DW["데이터 웨어하우스(스타 스키마)"]
      DIM[("고객 차원<br/>(서러게이트 키·이력 버전)")]
      FACT[("주문 팩트<br/>(측정값·차원 FK)")]
      FACT -->|"서러게이트 키 참조"| DIM
    end
    ETL --> DIM
    ETL --> FACT
    DIM --> BI["BI / 분석<br/>(시점 재현·추세 분석)"]
    FACT --> BI

위 구조에서 핵심은 ETL 단계의 변경 감지(change detection)다. ETL은 원천의 현재 값과 DW에 이미 적재된 차원의 최신 버전을 비교하여, (1) 신규 키이면 삽입, (2) 추적 대상 속성이 바뀌었으면 유형별 규칙에 따라 처리, (3) 변경이 없으면 통과시킨다. 이때 "어떤 속성이 추적 대상이고, 각 속성을 어떤 Type으로 다룰지"가 설계의 결과물이며, 하나의 차원 안에서도 속성마다 Type을 다르게 섞는 것이 일반적이다(이것이 뒤에 설명할 Type 6의 바탕이 된다).

변경 감지의 정확성을 높이기 위해 실무에서는 각 속성의 해시값(hash diff)을 저장해 두고 원천 해시와 비교하거나, 2장에서 본 CDC([[change-data-capture]])로부터 변경 이벤트를 받아 처리한다. 전자는 배치 비교에, 후자는 준실시간 적재에 적합하다. 어느 방식이든 "무엇이 바뀌었는지"를 안정적으로 판별해야 SCD 규칙을 올바르게 적용할 수 있다.

여기서 반드시 구분해야 할 두 가지 시간 개념이 있다. 하나는 유효 시간(valid time) — 그 값이 현실에서 유효했던 기간 — 이고, 다른 하나는 적재 시간(transaction/load time) — DW가 그 사실을 알게 된 시점 — 이다. 원천 지연이나 소급 정정 때문에 두 시간은 어긋날 수 있으며, 둘을 모두 추적하는 설계를 양방향 시간성(bitemporal)이라 부른다. 규제·감사 요건이 "현실에서 언제 유효했는가"와 "우리가 언제 그렇게 기록했는가"를 모두 요구하는 영역에서는, Type 2 유효기간에 적재 시각 컬럼을 추가한 양방향 차원 설계가 필요하다.

3. SCD 유형별 처리 방식 (Type 0 ~ 6)

SCD의 유형은 "변경을 어떻게 저장하는가"라는 하나의 축을 따라 배열된다. 가장 보수적인 Type 0(불변)에서 출발하여, 덮어쓰기(Type 1), 행 추가로 완전 이력화(Type 2), 제한적 이력(Type 3), 그리고 이들의 결합(Type 6)으로 발전한다. 아래에서 각 유형이 어떤 상황을 위해 고안되었고, 어떤 대가를 치르는지를 순서대로 서술한다.

flowchart TB
    START["차원 속성 변경 발생"] --> Q0{"변경을 반영해야 하나?"}
    Q0 -- "아니오(불변 속성)" --> T0["Type 0<br/>원본 값 영구 보존"]
    Q0 -- "예" --> Q1{"과거 이력이 분석에 필요한가?"}
    Q1 -- "불필요(최신만)" --> T1["Type 1<br/>기존 값 덮어쓰기"]
    Q1 -- "필요(완전 이력)" --> T2["Type 2<br/>새 버전 행 추가"]
    Q1 -- "직전 값 정도만" --> T3["Type 3<br/>이전 값 컬럼 추가"]
    T2 --> T6["Type 6<br/>1+2+3 혼합<br/>(현재·이력 동시 지원)"]
    T3 --> T6

가. Type 0 — 원본 고정(Retain Original). 한 번 기록된 값을 절대 바꾸지 않는 방식이다. 고객의 최초 가입일, 생년월일, 주민 고유식별자처럼 정의상 불변이거나 불변으로 간주해야 하는 속성에 적용한다. 원천에서 값이 바뀌더라도 DW는 이를 무시하거나 별도 오류로 분류한다. 구현은 가장 단순하지만, "불변"의 판단이 잘못되면(예: 초기 데이터 입력 오류를 영원히 고칠 수 없게 됨) 오히려 품질 문제를 고착시키므로, 진정으로 변하지 않아야 하는 속성에만 제한적으로 적용한다.

나. Type 1 — 덮어쓰기(Overwrite). 새 값으로 기존 값을 UPDATE하여 과거를 버리는 방식이다. 운영계의 동작과 동일하여 가장 직관적이며, 차원 행 수가 늘지 않아 저장·쿼리 비용이 낮다. 전화번호 오타 수정, 분류 코드 표기 정정처럼 과거 이력이 분석적으로 무의미하거나 단순 오류 교정인 경우에 적합하다. 그러나 Type 1로 덮어쓰면 그 속성 기준의 과거 팩트 집계가 소급 변경(retroactive restatement)된다. 즉, 3년 전 매출을 지금 분류 기준으로 다시 집계하게 되어, "그 당시의 실제 모습"을 재현할 수 없다. 이 소급 효과가 허용되는지 여부가 Type 1 적용의 결정 기준이다.

다. Type 2 — 행 추가로 이력 보존(Add New Row). SCD의 사실상 표준이자 가장 널리 쓰이는 방식이다. 속성이 바뀌면 기존 행을 덮어쓰지 않고 새 서러게이트 키를 가진 새 행을 추가한다. 같은 자연키(고객번호)를 공유하되 서러게이트 키가 다른 여러 버전이 공존하며, 각 버전은 유효 시작일(effective_date)·유효 종료일(end_date)·현재 여부 플래그(is_current) 같은 메타 컬럼으로 구분된다. 새 버전이 생기면 직전 버전의 end_date를 닫고 is_current를 꺼 준다. 이 방식의 가장 큰 장점은 완전한 시점 재현이다. 팩트가 발생한 시점의 서러게이트 키를 그대로 참조하므로, 과거 주문은 영원히 "그 당시 고객의 서울 주소 버전"에 연결된다. 대가는 차원 행 수 증가와 "현재 버전만 조회" 쿼리의 복잡도(플래그·날짜 조건 필요)다. 금융 신용등급, 보험 계약 상태, 조직 소속 변경 등 이력이 곧 규제 요건인 영역에서 필수적으로 쓰인다.

라. Type 3 — 이전 값 컬럼 추가(Add New Attribute). 행이 아니라 컬럼을 추가하여 현재_지역과 직전_지역을 나란히 두는 방식이다. 전면적 조직 개편처럼 "변경 직전과 직후를 동시에 비교"해야 하되, 완전한 이력까지는 필요 없는 경우에 쓴다. 저장 비용이 적고 쿼리가 단순하지만, 직전 1단계(혹은 고정 N단계)만 보존하므로 두 번 이상의 변경 이력은 유실된다. 따라서 변경이 드물고 "이전↔현재" 비교가 분석의 핵심인 특수 상황에 국한된다.

네 가지 유형의 성격 차이를 정리하면 다음과 같다. 표는 비교의 보조 수단일 뿐이며, 실제 선택은 위에서 설명한 "이력 필요성·소급 허용 여부·저장/쿼리 비용"의 균형으로 결정된다.

구분 변경 처리 이력 보존 과거 팩트 영향 행/컬럼 증가 대표 적용
Type 0 반영 안 함 원본 고정 없음 없음 생년월일·가입일
Type 1 덮어쓰기 없음(현재만) 소급 변경됨 없음 오타·코드 정정
Type 2 새 행 추가 완전 보존 불변(시점 고정) 행 증가 신용등급·계약상태
Type 3 컬럼 추가 직전값만 혼재 컬럼 증가 조직 개편 전후 비교
Type 6 1+2+3 결합 완전+현재 동시 선택적 행+컬럼 증가 현재·시점 동시 분석

마. Type 4 · Type 6 — 결합형. Type 4는 현재 값은 메인 차원에, 이력은 별도 히스토리 미니 차원에 분리하여, 현재 조회의 성능과 이력 보존을 동시에 노린다. Type 6(이름은 1+2+3=6에서 유래)은 하나의 차원에 Type 2의 버전 행과 Type 1로 항상 최신값을 유지하는 "현재값" 컬럼, 그리고 Type 3식 이전값 컬럼을 함께 둔다. 이렇게 하면 같은 테이블에서 "그 시점의 값"(버전 행)과 "이 고객의 현재 값"(current 컬럼)을 모두 조회할 수 있어, 예컨대 "과거 서울 거주 시절 주문을, 현재 부산에 사는 고객 관점으로도 묶어 보는" 이중 분석이 가능해진다. 유연하지만 ETL 로직이 복잡해지는 것이 단점이다.

4. 구현 아키텍처와 ETL 처리 기법

실무에서 SCD는 ETL/ELT 파이프라인의 차원 적재 단계로 구현된다. 가장 전형적인 패턴은 SQL의 MERGE(UPSERT) 문으로, 원천 현재 스냅샷과 DW 차원을 조인하여 신규는 INSERT, 변경은 유형별로 처리한다. Type 2의 경우 단일 MERGE로는 "기존 버전 닫기 + 새 버전 열기"를 한 번에 처리하기 어려워, 보통 ① 변경된 자연키의 현재 행 end_date를 닫는 UPDATE → ② 새 버전 INSERT의 두 단계로 나눈다. 이 순서가 뒤바뀌면 한 자연키에 is_current 행이 둘 생기는 정합성 오류가 발생하므로, 트랜잭션으로 묶어 원자성을 보장해야 한다.

sequenceDiagram
    participant S as 원천 스냅샷
    participant E as ETL 엔진
    participant D as 차원 테이블
    S->>E: 현재 고객 레코드 전달
    E->>D: 자연키로 현재 버전(is_current=Y) 조회
    alt 신규 자연키
        E->>D: 새 서러게이트 키로 INSERT
    else 추적 속성 변경(Type 2)
        E->>D: 기존 행 end_date 설정·is_current=N
        E->>D: 새 버전 행 INSERT(is_current=Y)
    else 비추적 속성 변경(Type 1)
        E->>D: 현재 행 UPDATE(덮어쓰기)
    else 변경 없음
        E-->>E: 통과(스킵)
    end

변경 감지의 신뢰성을 높이기 위해 실무에서는 추적 대상 속성들을 이어 붙여 해시(예: SHA-256) 지문을 만들어 차원 행에 저장한다. 적재 시 원천 해시와 저장된 해시를 한 번만 비교하면 다수 컬럼을 일일이 대조하지 않고도 변경 여부를 즉시 판별할 수 있어, 수천만 행 차원의 비교 비용을 크게 줄인다. 또한 유효기간의 end_date를 실제 종료 시각 대신 9999-12-31 같은 무한대 표식으로 두면 "현재 유효" 조건을 범위 조인으로 단순하게 표현할 수 있다.

서러게이트 키 설계도 중요한 구현 포인트다. 운영계 자연키를 그대로 팩트 FK로 쓰면 이력 버전을 구분할 수 없을 뿐 아니라, 원천 시스템 통합·키 재사용·NULL 처리에 취약해진다. 서러게이트 키는 이러한 운영계 변화로부터 DW를 절연(insulation)하는 역할을 하며, 미지(unknown)·미도착(late arriving) 등 특수 상황을 위한 예약 키(예: -1)를 함께 운용한다. 원천보다 팩트가 먼저 도착하는 지연 도착 차원(late arriving dimension) 문제는 임시 자리표시 행을 먼저 만들고 이후 실제 값으로 채우는 방식으로 처리한다.

5. 유형 선택 비교와 적용 사례

같은 "주소 변경"이라도 분석 목적에 따라 올바른 Type이 달라진다. 이 차이가 생기는 근본 이유는, SCD가 데이터 저장 기법이 아니라 "과거를 어떻게 해석할 것인가"라는 비즈니스 규칙의 구현이기 때문이다. 아래 세 사례는 이 점을 분명히 보여 준다.

첫째, 금융권 신용평가 사례에서는 Type 2가 사실상 강제된다. 고객의 신용등급이 BBB에서 A로 상향되었을 때, 과거 대출이 어떤 등급 시점에 실행되었는지를 재현하지 못하면 감독당국의 리스크 재현 요구(예: 특정 분기말 포트폴리오의 등급 분포)를 충족할 수 없다. 실제로 바젤 규제 대응 DW는 등급·한도·담보 속성을 모두 Type 2로 적재하여, 임의 과거 시점의 익스포저를 정확히 재구성한다.

둘째, 이커머스 상품 카탈로그 사례에서는 속성별로 Type을 혼합한다. 상품명 오타 수정은 Type 1(덮어쓰기)로 충분하지만, 상품 카테고리 재분류는 Type 2로 남겨야 "재분류 이전 기준의 분기 매출"과 "재분류 이후 기준"을 모두 분석할 수 있다. 한 상품 차원 안에서 수십 개 속성이 Type 0/1/2로 섞이는 것이 보통이며, 이 설계 문서가 곧 데이터 거버넌스 산출물이 된다.

셋째, 인사·조직 분석 사례에서는 Type 3 또는 Type 6이 유용하다. 대규모 조직 개편 직후 "개편 전 소속 기준 성과"와 "개편 후 소속 기준 성과"를 나란히 비교해야 할 때, 직전 소속 컬럼(Type 3)이나 current/historical 컬럼 병존(Type 6)이 분석가의 쿼리를 크게 단순화한다. 다만 개편이 두 번 이상 반복되면 Type 3만으로는 중간 이력이 유실되므로 Type 2 병행이 불가피하다.

이 세 사례가 공통으로 시사하는 바는, 잘못된 Type 선택이 조용한 데이터 오류로 이어진다는 점이다. 이력이 필요한 속성을 Type 1로 덮어쓰면 과거 리포트가 소급 변조되지만 시스템은 아무 오류도 내지 않으며, 분석가는 수치가 바뀐 사실조차 모른 채 잘못된 의사결정을 내릴 수 있다. 반대로 단순 정정 속성을 Type 2로 처리하면 의미 없는 버전이 쌓여 차원이 비대해진다. SCD 설계 검토가 데이터 품질 관리의 핵심 활동으로 다뤄져야 하는 이유가 여기에 있다.

6. 심화 — 레이크하우스 시대의 SCD와 최신 동향

전통적 SCD는 RDBMS 기반 DW를 전제로 발전했으나, 데이터 레이크하우스([[data-lakehouse]])와 ELT 패러다임의 확산은 그 구현 양상을 바꾸고 있다. 과거에는 ETL 도구가 행을 직접 UPDATE/INSERT했지만, 오브젝트 스토리지 위의 파일은 전통적 UPDATE가 비싸거나 불가능했다. 이를 해결한 것이 Delta Lake·Apache Iceberg·Apache Hudi 같은 개방형 테이블 포맷으로, 이들은 ACID 트랜잭션과 MERGE INTO를 지원하여 레이크 위에서도 Type 2 적재를 선언적으로 수행할 수 있게 했다. 특히 Iceberg·Delta의 타임 트래블(time travel)은 테이블 스냅샷 자체에 버전을 부여하므로, 일부 시점 재현 요구를 SCD 없이 스토리지 계층에서 해결하는 보완 수단이 되기도 한다.

변환 계층에서는 dbt의 snapshot 기능이 SCD Type 2를 사실상 표준화했다. 분석 엔지니어가 추적 전략(check/timestamp)과 추적 컬럼만 선언하면, dbt가 유효기간·현재 플래그 관리와 변경 감지를 자동 생성해 준다. 이는 SCD 로직을 손으로 구현하던 과거에 비해 오류를 크게 줄였고, 버전 관리·테스트가 가능한 코드형 데이터 파이프라인(Analytics Engineering)의 핵심 구성요소가 되었다.

한편 SCD와 짝을 이루는 설계 과제로 급변 차원(RCD, Rapidly Changing Dimension) 처리가 있다. 고객의 나이대·구매 성향 점수처럼 자주 바뀌는 속성을 Type 2로 두면 버전이 폭발적으로 늘어난다. 이때는 급변 속성을 본 차원에서 떼어 내 미니 차원(mini-dimension)으로 분리하고, 팩트가 두 차원을 각각 참조하게 설계한다. SCD가 "느린 변경"을 다루는 기법인 만큼, 그 경계 밖의 빠른 변경을 식별해 분리하는 판단 역시 차원 설계자의 몫이다.

데이터베이스 표준 측면에서는 SQL:2011이 도입한 시스템 버전 시간 테이블(system-versioned temporal table)이 주목된다. PostgreSQL·SQL Server·MariaDB 등이 지원하는 이 기능은 FOR SYSTEM_TIME AS OF 구문으로 과거 시점 조회를 언어 수준에서 제공하여, 애플리케이션 관점에서는 Type 2와 유사한 이력 보존을 DBMS가 투명하게 처리한다. 다만 이는 "행 전체"의 시스템 시간 이력이어서, 분석 모델링에서 요구하는 "속성별 추적 전략 혼합"이나 서러게이트 키 기반 팩트 연결과는 지향점이 다르므로, 분석 DW의 SCD를 완전히 대체하기보다 보완하는 성격이 강하다.

7. 고려사항 및 시사점 (기술사 관점)

SCD는 단순한 적재 기법이 아니라 데이터 웨어하우스의 신뢰성·감사가능성·분석 정확성을 좌우하는 설계 결정이므로, 기술사 관점에서는 다음을 종합적으로 고려해야 한다.

  • 적용 전략(속성 단위 Type 매핑의 명시화): 차원 전체가 아니라 속성마다 Type을 결정하고 이를 데이터 사전·거버넌스 문서로 명문화해야 한다. "어떤 변경이 역사적 사실이고 어떤 변경이 단순 정정인가"는 IT가 아니라 현업이 답할 질문이므로, 비즈니스와의 합의 과정을 설계 프로세스에 포함해야 한다.
  • 트레이드오프(이력 완전성 ↔ 저장·쿼리 비용): Type 2는 이력을 완벽히 보존하는 대신 행 폭증과 "현재 버전 조회" 쿼리의 복잡도를 수반한다. 변경 빈도가 높은 속성을 무분별하게 Type 2로 설정하면 차원이 비대해지므로, 고빈도 속성은 미니 차원 분리나 팩트화로 설계를 조정해 균형을 맞춰야 한다.
  • 정합성·원자성 보장: Type 2의 "기존 버전 닫기 + 새 버전 열기"는 반드시 트랜잭션으로 묶어 is_current 중복·기간 겹침(overlap)·공백(gap)이 생기지 않게 해야 하며, 적재 후 유효기간 연속성 검증을 데이터 품질 테스트로 자동화해야 한다. 멱등성([[idempotency]]) 확보로 재적재 시 중복 버전 생성을 막는 것도 필수다.
  • 전망과 연계 기술: CDC([[change-data-capture]])와 결합하면 배치가 아닌 준실시간 SCD가 가능하고, 레이크하우스의 개방형 테이블 포맷·dbt snapshot·시스템 버전 테이블은 SCD 구현을 선언적·표준적으로 끌어올리고 있다. 다만 어떤 기술을 쓰든 "과거를 어떻게 해석할 것인가"라는 비즈니스 규칙 자체는 자동화되지 않으므로, 기술 선택보다 이력 정책의 명확한 정의가 선행되어야 한다.

참고자료


한 줄 요약: SCD는 데이터 웨어하우스 차원의 느린 속성 변경을 어떻게 저장·추적할지 규정하는 기법으로, 덮어쓰기(Type 1)·행 추가 완전이력(Type 2)·이전값 컬럼(Type 3)·혼합(Type 6)을 속성 단위로 선택하여 "과거를 어떻게 해석할 것인가"라는 비즈니스 규칙을 구현한다.