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

테스트와 문서화

왜 dbt 테스트가 필요한가

데이터 파이프라인은 실행은 되지만 결과가 조용히 오염되는 상황이 더 위험하다. ETL 잡이 실패하면 알람이 울리지만, customer_id에 NULL이 10%씩 섞여도 쿼리는 그냥 실행된다. dbt 테스트는 이 "조용한 오염"을 잡아내는 방어선이다.

dbt 테스트는 크게 세 종류다.

  • Generic 테스트: YAML 선언 한 줄로 사용하는 재사용 가능한 테스트. dbt 내장 4개와 패키지 확장이 있다.
  • Singular 테스트: 특정 비즈니스 규칙에 맞춤형으로 작성하는 SQL 파일.
  • Unit 테스트: dbt 1.8+에서 도입된 변환 로직 정적 검증.
Generic 테스트 schema.yml 선언 not_null / unique accepted_values relationships Singular 테스트 tests/*.sql 파일 맞춤형 SQL 쿼리 Unit 테스트 (dbt 1.8+) dbt test SQL 쿼리로 변환 (실패 행 반환 SELECT) 웨어하우스에서 실행 병렬 실행 지원 웨어하우스 쿼리 실행 실패 행 수 집계 store_failures → 실패 테이블 저장 PASS 실패 행 0개 FAIL / WARN 실패 행 1개 이상
세 가지 dbt 테스트 유형과 실행 흐름

Generic 테스트: 내장 4가지

dbt는 설치 즉시 사용 가능한 4가지 Generic 테스트를 제공한다. YAML에 선언하면 dbt가 SQL 쿼리로 변환해 웨어하우스에서 실행한다.

not_null

컬럼에 NULL이 없는지 검사한다. PK·FK·필수 비즈니스 컬럼에 반드시 건다.

unique

컬럼 값이 중복되지 않는지 검사한다. PK 후보 컬럼 검증에 쓴다.

accepted_values

컬럼 값이 허용 목록 안에만 있는지 검사한다. status 같은 열거형 컬럼에 유용하다.

relationships

외래 키 무결성을 검사한다. orders.customer_idcustomers.customer_id에 반드시 존재하는지 확인하는 식이다.

# models/staging/schema.yml
version: 2

models:
  - name: stg_orders
    columns:
      - name: order_id
        tests:
          - not_null
          - unique
      - name: status
        tests:
          - accepted_values:
              values: ['placed', 'shipped', 'returned', 'cancelled']
      - name: customer_id
        tests:
          - not_null
          - relationships:
              to: ref('stg_customers')
              field: customer_id

dbt test를 실행하면 각 테스트가 "실패 행을 반환하는 SELECT"로 변환된다. 반환 행이 0이면 통과, 1 이상이면 실패다.

Singular 테스트: 맞춤형 SQL

재사용하지 않는 단발성 비즈니스 규칙은 tests/ 디렉터리에 SQL 파일로 작성한다.

-- tests/assert_payment_positive.sql
-- 결제 금액은 항상 양수여야 한다
select
    order_id,
    payment_amount
from {{ ref('fct_orders') }}
where payment_amount <= 0

파일이 존재하는 것만으로 dbt test가 실행한다. Generic 테스트로 일반화하기 어려운 비즈니스 특화 규칙에 적합하다.

Unit 테스트 (dbt 1.8+)

dbt 1.8에서 도입된 Unit 테스트는 변환 로직 자체를 정적 입력으로 검증한다. 웨어하우스의 실제 데이터에 의존하지 않으므로, 로직이 맞는지 개발 단계에서 빠르게 확인할 수 있다.

# models/marts/schema.yml
unit_tests:
  - name: test_fct_orders_vip_discount
    model: fct_orders
    given:
      - input: ref('stg_orders')
        rows:
          - {order_id: 1, amount: 100, is_vip: true}
          - {order_id: 2, amount: 200, is_vip: false}
    expect:
      rows:
        - {order_id: 1, final_amount: 90}   # VIP 10% 할인 적용
        - {order_id: 2, final_amount: 200}

Unit 테스트는 CI 파이프라인에서 실행하기 좋다. DuckDB 어댑터 등에서는 웨어하우스 연결 없이도 실행 가능하다.

테스트 설정: severity, store_failures, where

테스트마다 실패 처리 방식을 세밀하게 조정할 수 있다.

columns:
  - name: email
    tests:
      - not_null:
          severity: warn          # 실패해도 파이프라인 중단 안 함
      - unique:
          severity: error         # 실패하면 즉시 오류 (기본값)
          store_failures: true    # 실패 행을 웨어하우스 테이블로 저장
          where: "created_at > '2024-01-01'"  # 특정 범위만 테스트

store_failures: true로 설정하면 dbt가 dbt_test__audit 스키마에 실패 행 테이블을 생성한다. 실패 원인 분석이 훨씬 빠르다.

설정설명권장 사용처
severity: error실패 시 파이프라인 종료 (기본)PK, 무결성 필수 컬럼
severity: warn실패해도 실행 계속레거시 데이터, 허용 가능한 불일치
store_failures: true실패 행을 테이블로 저장디버깅, SLA 추적
where테스트 범위 제한대용량 테이블 비용 최적화

dbt-utils: 확장 테스트 패키지

dbt Labs가 오픈소스로 배포하는 dbt-utils 패키지는 내장 4개 외에 16개 이상의 Generic 테스트를 추가한다.

# packages.yml
packages:
  - package: dbt-labs/dbt_utils
    version: [">=1.0.0", "<2.0.0"]

자주 쓰이는 테스트:

테스트설명
expression_is_true임의 SQL 표현식이 참인지 검사
not_constant컬럼 값이 하나뿐이 아닌지 검사
at_least_oneNULL이 아닌 값이 최소 1개 존재하는지
mutually_exclusive_ranges날짜 범위가 겹치지 않는지 검사
unique_combination_of_columns복합 PK 유니크 검사

문서화: YAML 설명과 dbt docs

dbt의 문서화는 모델·컬럼 description 필드 하나에서 시작한다.

version: 2

models:
  - name: fct_orders
    description: >
      주문 팩트 테이블. 주문 원본(stg_orders)과
      고객 정보(stg_customers)를 결합한 최종 분석용 테이블.
    columns:
      - name: order_id
        description: "주문 고유 식별자 (PK)"
      - name: final_amount
        description: "VIP 할인 적용 후 최종 결제 금액 (원화)"

설명이 길거나 여러 모델에서 공유해야 할 때는 .md 파일로 분리하고 doc() 함수로 참조한다.

{% docs fct_orders %}
# fct_orders
이 테이블은 **주문 팩트 테이블**입니다. ...
{% enddocs %}
models:
  - name: fct_orders
    description: "{{ doc('fct_orders') }}"

dbt docs generate를 실행하면 dbt는 YAML 설명, 컬럼 타입, 테스트 결과, ref()/source() 호출에서 추출한 DAG 정보를 모두 수집해 target/catalog.json을 생성한다. dbt docs serve를 실행하면 localhost:8080에서 로컬 웹 UI가 열린다.

작성 schema.yml 설명 .md docs 블록 ref() / source()
dbt docs generate catalog.json manifest.json DAG 계보 정보
dbt docs serve 모델 검색·탐색 컴파일된 SQL 확인 Lineage 그래프
dbt 문서 생성 흐름

Lineage 그래프에서는 소스부터 최종 mart까지 전체 계보를 클릭하며 탐색할 수 있다. 2025년 기준 dbt Fusion 엔진에서는 컬럼 단위 계보(Column-Level Lineage)도 지원된다.

테스트 전략: 어디에 무엇을 걸어야 하나

모든 컬럼에 모든 테스트를 거는 것은 비효율적이다. 실용적인 기준:

  1. 모든 PK 컬럼: not_null + unique
  2. 모든 FK 컬럼: not_null + relationships
  3. 열거형 컬럼: accepted_values
  4. 핵심 비즈니스 메트릭: Singular 테스트
  5. 변환 로직 분기: Unit 테스트 (dbt 1.8+)

Staging 레이어에서 소스 품질을 1차 방어하고, Mart 레이어에서 비즈니스 무결성을 2차 방어하는 2계층 전략이 일반적이다.

한 줄 정리

dbt 테스트는 YAML 선언(Generic), 맞춤형 SQL(Singular), 로직 검증(Unit) 세 계층으로 데이터 품질을 보증하고, dbt docs generate로 DAG 계보가 포함된 문서를 자동 생성한다.

References

  • https://docs.getdbt.com/docs/build/data-tests
  • https://docs.getdbt.com/reference/resource-properties/data-tests
  • https://docs.getdbt.com/best-practices/writing-custom-generic-tests
  • https://docs.getdbt.com/docs/build/documentation
  • https://docs.getdbt.com/docs/explore/build-and-view-your-docs
  • https://docs.getdbt.com/docs/explore/column-level-lineage
  • https://www.datafold.com/blog/7-dbt-testing-best-practices/
  • https://github.com/dbt-labs/dbt-utils
  • https://popsql.com/learn-dbt/dbt-docs
  • https://stellans.io/dbt-data-tests-null-accepted-values-relationships-patterns-stellans-best-practices/