LLM WikiAccess-protected knowledge portal
← 스터디 홈
3편 · 약 19분

데이터 품질: completeness, freshness, uniqueness, validity, reconciliation

데이터 품질은 “좋은 데이터”라는 감상이 아니라 운영 계약이다

데이터 품질 문제는 보통 아주 조용하게 시작된다. 배치는 성공했고, 테이블도 존재하고, 대시보드도 새로고침된다. 그런데 어제 주문의 30%가 빠졌거나, 중복 이벤트가 두 번 집계됐거나, status에 새 값이 들어왔는데 downstream 모델은 그 값을 알지 못한다. 이런 문제는 파이프라인 실패보다 위험하다. 실패는 알림이 오지만, 잘못된 성공은 소비자가 의사결정을 한 뒤에야 발견된다.

그래서 데이터 품질은 “깨끗한 데이터”라는 추상적인 목표가 아니라 측정 가능한 운영 계약이어야 한다. 어떤 컬럼은 반드시 채워져야 하고, 어떤 테이블은 몇 분 안에 갱신되어야 하며, 어떤 키는 중복되면 안 되고, 어떤 수치는 원천 시스템과 맞아야 한다. 이 장은 데이터 플랫폼 운영에서 자주 쓰는 다섯 가지 축인 completeness, freshness, uniqueness, validity, reconciliation을 실무 기준으로 정리한다.


다섯 가지 품질 축의 위치

Completeness 필수 컬럼과 파티션이
빠지지 않았는가
Freshness 데이터가 SLA 안에
갱신되었는가
Uniqueness 식별자와 이벤트가
중복되지 않는가
Validity 타입·범위·enum·형식이
계약과 맞는가
Reconciliation 소스와 결과가
총량 기준으로 맞는가
좋은 품질 체계는 이 다섯 축을 ingestion, transform, serving 경계마다 다르게 배치한다.
데이터 품질 검사의 다섯 축

다섯 축은 서로 겹치지만 같은 말은 아니다. 예를 들어 order_id가 모두 채워져 있으면 completeness는 통과할 수 있다. 하지만 같은 order_id가 두 번 들어오면 uniqueness는 실패한다. created_at이 올바른 timestamp 형식이면 validity는 통과할 수 있지만, 마지막 데이터가 6시간 전이면 freshness는 실패한다. 운영자는 이 차이를 구분해야 알림을 줄이고 원인을 빨리 찾을 수 있다.


1. Completeness: 빠진 데이터는 조용한 장애다

Completeness는 데이터가 필요한 만큼 도착했는지 확인하는 축이다. 단순히 NOT NULL만 뜻하지 않는다. 필수 컬럼이 채워졌는지, 오늘 파티션이 만들어졌는지, 소스에서 기대한 행 수가 들어왔는지, 특정 지역이나 상태값이 통째로 빠지지 않았는지까지 포함한다.

Completeness 검사 예시

대상검사 질문예시 규칙
필수 컬럼키와 시간 컬럼이 비어 있지 않은가order_id IS NOT NULL, event_time IS NOT NULL
파티션오늘 처리해야 할 파티션이 존재하는가dt = current_date - 1 파티션 존재
행 수기대 범위만큼 들어왔는가최근 7일 평균 대비 60~140%
카테고리 커버리지특정 구간이 통째로 빠지지 않았는가국가별/서비스별 최소 1건 이상

운영에서 가장 흔한 실수는 “필수 컬럼 not null”만 보고 completeness를 다 봤다고 생각하는 것이다. 실제 장애는 행 수 급감이나 파티션 누락으로 자주 나타난다. API pagination 버그로 마지막 페이지가 빠지거나, CDC connector가 특정 table만 멈추거나, object storage 업로드가 일부 파일만 성공하는 경우가 여기에 해당한다.

-- 예시: 최근 7일 평균 대비 오늘 주문 수가 너무 작은지 확인
with daily as (
  select order_date, count(*) as row_count
  from mart_orders
  where order_date >= current_date - interval '8 day'
  group by order_date
), baseline as (
  select avg(row_count) as avg_7d
  from daily
  where order_date < current_date
)
select d.order_date, d.row_count, b.avg_7d
from daily d cross join baseline b
where d.order_date = current_date
  and d.row_count < b.avg_7d * 0.6;

이런 규칙은 고정 숫자보다 baseline 기반이 낫다. 월요일과 일요일의 트래픽이 다르고, 월말 정산일과 평일의 볼륨이 다르기 때문이다. 단, baseline 기반 알림은 계절성과 프로모션을 고려해야 한다. 대규모 이벤트가 예정되어 있다면 임계값을 임시 조정하거나 예상 볼륨을 별도 캘린더로 관리한다.


2. Freshness: 성공한 배치보다 최신 데이터가 중요하다

Freshness는 데이터가 얼마나 최근 상태를 반영하는지 측정한다. Airflow DAG가 성공했다고 freshness가 보장되는 것은 아니다. 잡은 성공했지만 소스 파일이 어제 것이었을 수 있고, CDC job은 돌고 있지만 offset이 밀려 있을 수 있다. 따라서 freshness는 잡 상태가 아니라 데이터 안의 시간 또는 수집 메타데이터로 측정해야 한다.

Freshness 기준을 정하는 방법

데이터 유형측정 기준권장 알림 방식
배치 테이블최신 파티션 또는 loaded_atSLA 초과 시 warning/error
CDC 테이블소스 commit time과 warehouse 반영 시각 차이lag threshold
이벤트 스트림event time, ingestion time, processing time 차이watermark/consumer lag
BI 마트최종 소비 테이블의 갱신 시각대시보드 SLA 기준

Freshness SLA는 소비자 기대와 연결해야 한다. 운영 리포트가 매일 오전 9시에 사용된다면 “전날 데이터가 오전 8시 30분까지 준비”되는 것이 SLA다. 단순히 “하루 한 번 실행”이라고 쓰면 장애 판단이 어렵다.

# dbt source freshness 예시
sources:
  - name: raw_orders
    config:
      freshness:
        warn_after: {count: 30, period: minute}
        error_after: {count: 60, period: minute}
      loaded_at_field: _etl_loaded_at
    tables:
      - name: orders

dbt는 source 선언에서 raw source의 freshness를 계산하고 SLA 알림에 활용할 수 있다. 하지만 모든 freshness를 dbt에만 맡기면 부족하다. Kafka/Flink 같은 스트리밍 경계에서는 consumer lag, watermark 지연, checkpoint 지연을 별도로 봐야 한다. 핵심은 “이 테이블을 쓰는 사람이 지금 봐도 되는가?”라는 질문에 수치로 답하는 것이다.


3. Uniqueness: 중복은 비용보다 신뢰를 먼저 망가뜨린다

Uniqueness는 특정 키 또는 키 조합이 중복되지 않는지 확인한다. 중복 데이터는 저장 비용을 조금 늘리는 정도로 끝나지 않는다. 주문 수, 매출, 활성 사용자 수 같은 지표를 직접 부풀린다. 특히 재시도, backfill, CDC 재연결, at-least-once 메시지 처리에서는 중복이 자연스럽게 생길 수 있다.

어떤 키를 검사할 것인가

레이어검사 대상설명
Raw 이벤트event_id, (source, source_offset)수집 재시도 중복 감지
Silver 엔티티비즈니스 키, 자연키같은 고객/상품/주문 중복 감지
Gold 마트집계 graindate, product_id, region 조합 중복 방지
CDC 테이블primary key + operation orderingout-of-order upsert 확인
-- 예시: 주문 마트의 grain 중복 검사
select order_date, order_id, count(*) as cnt
from mart_orders
group by order_date, order_id
having count(*) > 1;

중복 처리 정책은 데이터 계약에 포함되어야 한다. 중복을 발견했을 때 파이프라인을 실패시킬지, 최신 레코드를 남길지, 별도 quarantine 테이블로 보낼지 미리 정해야 한다. 모든 중복이 같은 위험도를 갖지는 않는다. raw layer에서는 중복을 보존하고 dedup 기준을 명시하는 것이 디버깅에 유리할 수 있다. 반대로 serving mart에서 같은 grain이 두 번 나오면 소비자 지표가 깨지므로 배포를 막는 편이 안전하다.


4. Validity: 형식이 맞아도 의미가 맞는 것은 아니다

Validity는 값이 정의된 타입, 범위, 패턴, enum, 비즈니스 규칙을 만족하는지 확인한다. 이메일 형식, 통화 코드, 상태값, 양수 금액, 날짜 범위 같은 검사가 여기에 들어간다.

검사 유형예시실패 시 의미
타입amount는 decimal파싱/스키마 변경 가능성
범위amount >= 0환불/취소 정책과 모델 정의 불일치 가능성
enumstatus in ('created','paid','cancelled')새 상태값 추가 또는 소스 버그
패턴email 형식, ISO country code입력 검증 누락
조건부 규칙status='paid'이면 paid_at not null비즈니스 이벤트 순서 오류

여기서 중요한 구분은 validity와 accuracy는 다르다는 점이다. +82-10-1234-5678은 전화번호 형식으로는 valid할 수 있지만 실제 고객 번호와 일치한다는 보장은 없다. accuracy는 보통 별도 source of truth나 외부 검증이 필요하다. 따라서 데이터 플랫폼에서 자동화하기 쉬운 것은 validity이고, accuracy는 reference data나 reconciliation으로 보완하는 경우가 많다.

# dbt generic data tests 예시
models:
  - name: mart_orders
    columns:
      - name: order_id
        data_tests:
          - not_null
          - unique
      - name: status
        data_tests:
          - accepted_values:
              arguments:
                values: ['created', 'paid', 'cancelled', 'refunded']
      - name: customer_id
        data_tests:
          - relationships:
              arguments:
                to: ref('dim_customers')
                field: customer_id

dbt의 기본 generic test는 not_null, unique, accepted_values, relationships처럼 운영자가 자주 쓰는 validity·uniqueness·referential integrity 검사를 빠르게 붙일 수 있게 해준다. 더 복잡한 조건은 custom generic test나 SQL assertion으로 분리한다.


5. Reconciliation: 소스와 결과를 총량으로 맞춰본다

Reconciliation은 두 데이터셋이 같은 세계를 설명하고 있는지 비교하는 검사다. ingestion된 raw와 source export의 row count를 비교하거나, 주문 시스템의 일 매출 합계와 warehouse mart의 일 매출 합계를 비교하거나, CDC 적용 후 primary DB와 replica-derived table의 최신 상태가 맞는지 확인한다.

Completeness가 “충분히 들어왔는가”를 묻는다면, reconciliation은 “원천과 결과가 서로 맞는가”를 묻는다. 특히 재무, 결제, 정산, 보안 이벤트처럼 손실 허용도가 낮은 데이터에서는 reconciliation이 필수다.

Reconciliation 패턴

패턴비교 대상장점주의점
Row count matchsource row count vs raw row count빠르고 저렴함삭제/수정 반영은 놓칠 수 있음
Aggregate matchsource 금액 합계 vs mart 금액 합계비즈니스 영향이 보임통화, 세금, 취소 정책 정의 필요
Checksum/hashkey별 hash 합계 비교값 변경 감지 가능대용량에서는 비용 큼
Sample audit일부 key를 source와 warehouse에서 직접 비교원인 분석에 유리표본 설계 필요
-- 예시: 원천 주문 합계와 mart 주문 합계 비교
with source_daily as (
  select order_date, count(*) as orders, sum(amount) as revenue
  from raw_orders
  group by order_date
), mart_daily as (
  select order_date, count(*) as orders, sum(net_amount) as revenue
  from mart_orders
  group by order_date
)
select
  s.order_date,
  s.orders as source_orders,
  m.orders as mart_orders,
  s.revenue as source_revenue,
  m.revenue as mart_revenue,
  m.revenue - s.revenue as revenue_diff
from source_daily s
join mart_daily m using (order_date)
where abs(m.revenue - s.revenue) > 1000;

Reconciliation에서 가장 어려운 것은 SQL이 아니라 정의 합의다. source의 amount가 취소 전 금액인지, mart의 net_amount가 할인·쿠폰·세금을 반영한 금액인지 명확하지 않으면 차이가 나도 장애인지 정상인지 판단할 수 없다. 따라서 reconciliation 규칙은 데이터 계약과 metric definition에 연결되어야 한다.


어디에서 품질 검사를 실행할 것인가

품질 검사는 한 곳에서만 실행하면 늦거나 시끄럽다. 경계별로 역할을 나눠야 한다.

위치잡아야 할 문제실패 시 동작
Ingestion 전/중스키마 위반, 필수 필드 누락, 파일 손상거부, DLQ, quarantine
Raw → Silver타입 정규화, dedup, 기본 validity실패 또는 문제 레코드 격리
Silver → Gold비즈니스 규칙, referential integrity, grain uniquenessserving 배포 차단
Serving 이후freshness SLA, metric drift, 소비자 영향알림, incident triage

모든 규칙을 hard fail로 만들면 운영이 멈춘다. 반대로 모두 warning으로 두면 아무도 보지 않는다. 운영자는 규칙마다 severity를 정해야 한다.

  • Error: 배포하면 잘못된 의사결정이 발생하므로 파이프라인을 중단한다.
  • Warn: 품질 저하가 있지만 소비자에게 즉시 장애는 아니므로 알림과 추적을 한다.
  • Info: baseline 수집, 추세 분석, 향후 임계값 설정에 사용한다.

알림 설계: 실패 건수보다 소비자 영향이 중요하다

데이터 품질 알림은 쉽게 과잉 생산된다. not_null 하나가 실패할 때마다 Slack 알림을 보내면 운영자는 곧 무시한다. 좋은 알림은 규칙 이름보다 영향을 먼저 말한다.

나쁜 알림:
- test_mart_orders_status_accepted_values failed

좋은 알림:
- 결제 주문 mart에 알 수 없는 status 값 2개가 들어왔습니다.
- 영향: daily revenue dashboard, settlement export
- 첫 발생: 2026-06-30 03:15 KST
- 후보 원인: payments API에 새 상태값 'chargeback_pending' 추가
- 권장 대응: 소스 owner 확인 후 enum 계약 업데이트 또는 레코드 격리

알림에는 최소한 데이터셋, 실패 규칙, 실패 수, 첫 발생 시각, 최근 변경 이력, downstream 영향, 담당 owner가 있어야 한다. Lineage와 catalog가 연결되어 있으면 “이 실패가 어떤 대시보드와 모델에 영향을 주는가”를 자동으로 붙일 수 있다.


실전 체크리스트

새 테이블을 운영 대상으로 올릴 때

  • [ ] 필수 컬럼과 grain이 문서화되어 있는가?
  • [ ] completeness 기준이 NOT NULL뿐 아니라 row count/partition 기준까지 포함하는가?
  • [ ] freshness SLA가 소비자 사용 시각 기준으로 정의되어 있는가?
  • [ ] uniqueness 검사의 키 조합이 실제 business grain과 일치하는가?
  • [ ] validity 규칙이 타입, enum, 범위, 조건부 규칙을 포함하는가?
  • [ ] source와 mart 사이 reconciliation 기준이 정의되어 있는가?
  • [ ] 규칙마다 error/warn/info severity가 정해져 있는가?
  • [ ] 실패 레코드 저장 위치(quarantine 또는 audit table)가 있는가?
  • [ ] 알림에 owner와 downstream 영향이 포함되는가?

장애 조사 시 먼저 볼 것

  1. Freshness: 최신 데이터인가, 아니면 오래된 데이터를 보고 있는가?
  2. Completeness: 특정 파티션/지역/소스가 통째로 빠졌는가?
  3. Uniqueness: 재시도나 backfill로 중복이 생겼는가?
  4. Validity: 새 enum, 타입 변경, 범위 이탈이 생겼는가?
  5. Reconciliation: 소스 총량과 mart 총량이 언제부터 벌어졌는가?

정리

데이터 품질은 한 번 테스트를 붙이고 끝나는 일이 아니다. 소스 변경, 소비자 기대, 트래픽 패턴, 비즈니스 정의가 바뀌면 품질 규칙도 함께 바뀐다. 운영 관점에서는 “어떤 도구를 쓰는가”보다 “어떤 질문을 숫자로 답하는가”가 더 중요하다.

Completeness는 빠진 데이터를 찾고, freshness는 오래된 데이터를 막고, uniqueness는 중복 집계를 막고, validity는 계약 위반 값을 잡고, reconciliation은 원천과 결과의 총량 불일치를 드러낸다. 이 다섯 축이 데이터 계약, lineage, 알림, runbook과 연결될 때 데이터 플랫폼은 단순한 파이프라인 묶음이 아니라 신뢰 가능한 운영 시스템이 된다.

References

  • IBM, “What are data quality dimensions?” https://www.ibm.com/think/topics/data-quality-dimensions
  • Informatica Documentation, “Data quality dimensions.” https://docs.informatica.com/data-governance-and-quality-cloud/data-quality/current-version/introduction/introduction-to-data-quality/data-quality/data-quality-dimensions.html
  • dbt Developer Hub, “Add sources to your DAG.” https://docs.getdbt.com/docs/build/sources
  • dbt Developer Hub, “About data tests property.” https://docs.getdbt.com/reference/resource-properties/data-tests
  • dbt Developer Hub, “Writing custom generic data tests.” https://docs.getdbt.com/best-practices/writing-custom-generic-tests
  • AWS Glue Documentation, “AWS Glue Data Quality.” https://docs.aws.amazon.com/glue/latest/dg/glue-data-quality.html
  • AWS Glue Documentation, “Data Quality Definition Language (DQDL) reference.” https://docs.aws.amazon.com/glue/latest/dg/dqdl.html
  • AWS Glue Documentation, “DQDL rule type reference.” https://docs.aws.amazon.com/glue/latest/dg/dqdl-rule-types.html
  • Great Expectations Documentation, “Great Expectations documentation.” https://docs.greatexpectations.io/docs/