contract

contract

contract config를 적용(enforced)하면 dbt가 모델이 반환하는 데이터셋이 YAML에 정의한 속성과 정확히 일치하는지 강제로 확인해요. 다운스트림에서 모델을 조회하는 사람들이 예측 가능하고 일관된 컬럼 집합을 쓰도록 보장해 주는 기능이에요.

출처: 문서

본문

contract config를 적용하면 dbt는 모델이 반환하는 데이터셋이 YAML에 정의한 속성과 정확히 일치하는지 보장해요:

  • 모든 컬럼의 namedata_type
  • 이 materialization과 데이터 플랫폼에서 지원되는 추가 constraints

이는 다운스트림에서 dbt 안팎으로 모델을 조회하는 사람들이 분석에 쓸 수 있는 예측 가능하고 일관된 컬럼 집합을 갖도록 보장하기 위해서예요. boolean(true/false)에서 integer(0/1)로 바뀌는 것 같은 미묘한 데이터 타입 변화조차도 쿼리를 예상치 못한 방식으로 실패하게 만들 수 있어요.

Contract는 단일 모델 또는 프로젝트의 여러 모델에 걸쳐 일관되게 스키마가 어떻게 강제되는지 제어할 수 있게 해줘요.

전제 조건 (Prerequisites)

모델 contract를 지원하는 곳:

  • dbt_project.yml 파일
  • properties.yml 파일
  • SQL 모델
  • 다음 중 하나로 materialize된 모델:
    • table
    • view — view는 컬럼 이름과 데이터 타입을 지원하지만 constraints는 지원하지 않아요
    • incrementalon_schema_change: append_new_columns 또는 on_schema_change: fail과 함께
  • 특정 데이터 플랫폼이지만, 지원되고 적용되는 constraints는 플랫폼마다 달라요

모델 contract를 지원하지 않는 곳:

  • Python 모델
  • materialized view 또는 ephemeral — materialize된 SQL 모델
  • 커스텀 materialization (작성자가 추가하지 않는 한)
  • BigQuery에서 재귀적(recursive) CTE를 가진 모델
  • sources, seeds, snapshots 등과 같은 다른 리소스 유형

프로젝트에서 contract를 적용하는 방법은 예제를 참고하세요.

데이터 타입 별칭 (Data type aliasing)

dbt는 YAML에 정의된 data_type에 대해 내장 타입 별칭을 사용해요. 예를 들어 contract에 string을 지정하면 Postgres/Redshift에서는 dbt가 이를 text로 변환해요. dbt가 data_type 이름을 알려진 별칭 중에서 인식하지 못하면 그대로 통과시켜요. 이것은 기본적으로 활성화되어 있지만, alias_typesfalse로 설정해 끌 수 있어요.

비활성화 예제:

FOLDER_NAME/FILE_NAME.yml


models:
  - name: my_model
    config:
      contract:
        enforced: true
        alias_types: false  # true by default

크기, 정밀도, 스케일 (Size, precision, and scale)

dbt가 데이터 타입을 비교할 때 크기, 정밀도, 스케일 같은 세부 사항은 비교하지 않아요. varchar(256)varchar(257)의 차이에 신경 쓸 필요는 없다고 생각해요, 다운스트림 조회자 경험에는 실제로 영향을 미치지 않으니까요. 더 정밀한 검증은 커스텀 테스트 작성/사용으로 할 수 있어요.

varchar 크기나 숫자 스케일을 지정하지 않으면 dbt는 기본값에 의존한다는 점에 주의하세요. 예를 들어 numeric 타입이 정밀도 38, 스케일 0이 기본이면, numeric 컬럼은 소수점 오른쪽 0자리를 저장해요(정수만 저장). 이로 인해 contract 적용이 실패할 수 있어요. 이런 암시적 강제 변환을 피하려면 numeric(38, 6)처럼 0이 아닌 스케일로 data_type을 지정하세요. dbt 1.7 이상에서는 numeric 데이터 타입을 줄 때 정밀도/스케일을 지정하지 않으면 경고를 제공해요.

예제 (Examples)

models/dim_customers.yml

models:
  - name: dim_customers
    config:
      materialized: table
      contract:
        enforced: true
    columns:
      - name: customer_id
        data_type: int
        constraints:
          - type: not_null
      - name: customer_name
        data_type: string
      - name: non_integer
        data_type: numeric(38,3)

모델이 다음과 같이 정의되었다고 해볼게요:

models/dim_customers.sql

select
  'abc123' as customer_id,
  'My Best Customer' as customer_name

dbt run으로 모델을 실행하면, dbt가 데이터베이스에서 테이블로 materialize하기 전에 이 오류를 보게 될 거예요:

20:53:45  Compilation Error in model dim_customers (models/dim_customers.sql)
20:53:45    This model has an enforced contract that failed.
20:53:45    Please ensure the name, data_type, and number of columns in your contract match the columns in your model's definition.
20:53:45
20:53:45    | column_name | definition_type | contract_type | mismatch_reason    |
20:53:45    | ----------- | --------------- | ------------- | ------------------ |
20:53:45    | customer_id | TEXT            | INT           | data type mismatch |
20:53:45
20:53:45
20:53:45    > in macro assert_columns_equivalent (macros/materializations/models/table/columns_spec_ddl.sql)

Project YAML

여러 모델에 걸쳐 일관되게 contract를 적용하려면 dbt_project.yml에서 적용 방식을 사용하세요:


models:
  property_management:  # replace with your dbt project name
    +contract:
      enforced: true

Properties YAML

properties.yml에서 예상 컬럼과 데이터 타입을 지정해 모델의 contract를 정의하세요:


models:
  - name: stg_rental_applications  # replace with your model name
    config:
      contract:
        enforced: true
    columns:
      - name: column_1_id  # example id column. Replace with your column
        data_type: int    # replace with your column's data type
      - name: column_2_created_at  # example column tracking when something was created
        data_type: timestamp
      - name: column_3_status      # example status column, which typically store text values ("active", "pending", "completed", etc.)
        data_type: string

SQL 파일 config

단일 모델에 적용하고 세밀하게 제어하고 싶을 때는 모델 SQL 파일에서 contract를 적용하세요:


{{ config(
  contract = { "enforced": true }  -- Enables contract enforcement for this model
) }}

select
  column_1_id,          -- replace with your column
  column_2_created_at,  -- replace with your column
  column_3_status       -- replace with your column
from {{ source('property_management', 'rental_applications') }}  -- replace with your source name and table

모델 SQL 파일, dbt_project.yml, properties.yml에 사용 가능한 지원 config에 대한 자세한 내용은 General configurations를 참고하세요.

Incremental 모델과 on_schema_change

왜 incremental 모델도 on_schema_change를 설정하고, append_new_columnsfail로 해야 할까요?

상상해 보세요:

  • SQL과 YAML 명세 양쪽에 새 컬럼을 추가했어요
  • on_schema_change를 설정하지 않았거나 on_schema_change: 'ignore'로 설정했어요
  • dbt는 실제로 그 새 컬럼을 기존 테이블에 추가하지 않아요 — 그리고 upsert/merge는 이미 존재하는 "destination" 컬럼만 기준으로 수행되므로 여전히 성공해요(오래된 동작)
  • 결과는 YAML 정의 contract와 데이터베이스의 실제 테이블 사이에 차이가 생겨요 — 즉 contract가 이제 틀렸어요!

sync_all_columns가 아니라 왜 append_new_columns(또는 fail)일까요? 기존 컬럼을 제거하는 것은 contract가 적용된 모델에 breaking change이기 때문이에요! sync_all_columnsappend_new_columns처럼 동작하지만 삭제된 컬럼도 제거하는데, 버전을 올리지 않는 한 contract 적용 모델에서는 그렇게 하면 안 돼요.

문제 해결 (Troubleshooting)

contract mismatch 오류가 나요

무엇이 잘못됐나요: 모델에 적용된 contract가 있으면 dbt는 모델이 반환하는 데이터셋이 모든 컬럼에서 YAML 정의 namedata_type과 정확히 일치하는지 확인하고, 일치하지 않으면 오류를 냅니다.

해결 방법: contract의 name, data_type, 컬럼 수가 모델 정의의 컬럼과 일치하는지 확인하세요. 자세한 내용은 contract docs를 참고하세요.

incremental 모델에 새 컬럼이 안 보여요

무엇이 잘못됐나요: incremental 모델의 contract는 on_schema_changeappend_new_columns 또는 fail로 설정될 때 지원됩니다. 새 컬럼을 추가했는데 on_schema_change를 설정하지 않으면(또는 ignore로 설정하면) dbt는 기존 테이블에 그 컬럼을 추가하지 않아, YAML 정의 contract와 실제 테이블 스키마 사이에 불일치가 생길 수 있습니다.

해결 방법: contract 적용 incremental 모델에는 on_schema_change: append_new_columns(또는 fail)를 설정하세요. 자세한 내용은 Incremental models and on_schema_change를 참고하세요.

incremental 모델 테이블에서 컬럼이 사라졌어요

무엇이 잘못됐나요: sync_all_columns는 새 컬럼을 기존 테이블에 추가하고, 이제 없는 컬럼을 제거합니다. 그래서 새 모델 쿼리에 없으면 타깃 테이블에서 컬럼이 제거될 수 있어요.

해결 방법: contract 적용 incremental 모델에는 sync_all_columns 대신 append_new_columns(또는 fail)를 사용하세요. 기존 컬럼 제거는 contract 적용 모델에 breaking change입니다. 자세한 내용은 Incremental models and on_schema_change를 참고하세요.

예상치 못한 데이터 타입 mismatch가 나요

무엇이 잘못됐나요: dbt는 YAML data_type 값에 내장 타입 별칭을 적용하고, 특히 numeric의 기본 정밀도/스케일에 의존하면 contract 적용이 실패하게 만드는 암시적 강제 변환이 생길 수 있습니다.

해결 방법: 별칭을 피하고 싶으면 alias_types: false를 설정하세요. 암시적 numeric 강제 변환을 피하려면 0이 아닌 스케일의 data_type(예: numeric(38, 6))을 지정하세요. 자세한 내용은 contract docs를 참고하세요.

더 알아보기 (Learn more)