dbt에서 동적 테이블 사용하기

dbt에서 동적 테이블 사용하기

dbt-snowflake 어댑터를 사용하면 동적 테이블(dynamic tables)을 dbt 모델로 정의할 수 있어요. materialized='dynamic_table'을 설정하면 dbt는 CREATE TABLE 대신 CREATE DYNAMIC TABLE을 발행해요. 이를 통해 동적 테이블의 내장 증분 갱신과 함께 dbt의 소프트웨어 엔지니어링 규율(버전 관리, 테스트, 혈통)을 얻을 수 있어요. 두 가지 선택(스케줄링 제어의 주체, 데이터 처리 방식)은 독립적으로 유지돼요.

출처: Snowflake 문서

본문

사전 요구 사항

시작하기 전에 다음이 있는지 확인하세요:

  • dbt-snowflake 어댑터 v1.11.5 이상이 설치되어 있는지.
  • dbt 프로필(~/.dbt/profiles.yml)에 구성된 Snowflake 연결.
  • target 스키마에 CREATE DYNAMIC TABLE 권한이 있는 Snowflake 역할. 필요한 권한은 동적 테이블 접근 제어 문서를 참조하세요.
  • 동적 테이블을 공급하는 기본 테이블에 CHANGE_TRACKING = TRUE가 설정되어 있는지. 이게 없으면 업스트림 모델에서 CREATE OR REPLACE가 다운스트림 동적 테이블의 변경 추적을 깨뜨려요.

dbt 설치 지침은 dbt-snowflake 설정 가이드를 참조하세요.

스케줄링 모델 선택

처리 모델과 독립적으로 갱신을 트리거하는 주체에 대한 두 가지 옵션이 있어요.

dbt 관리 갱신 (기본)

config에서 target_lag를 생략하거나(또는 명시적으로 scheduler: DISABLE로 설정) dbt가 갱신 시점을 제어해요. 각 dbt run은 동적 테이블을 만들거나 변경한 다음 ALTER DYNAMIC TABLE ... REFRESH를 발행해 동기 갱신을 트리거해요.

models:
  - name: dt_orders
    config:
      materialized: dynamic_table
      snowflake_warehouse: transform_wh
      scheduler: DISABLE

dbt 관리 갱신 사용 시:

  • 각 모델은 dbt run 중 동기적으로 그리고 독립적으로 갱신돼요.
  • 캐스케이드가 없어요. 다운스트림 동적 테이블이 자동으로 갱신되지 않아요.
  • 기존 dbt 스케줄러(Airflow, dbt Cloud, cron)를 통해 오케스트레이션 타이밍에 대한 완전한 제어를 유지해요.

이미 외부 오케스트레이터(Airflow, dbt Cloud, cron)를 사용하고 갱신 타이밍을 그 오케스트레이터의 제어 아래 두길 원하는 팀에게 가장 적합해요.

Snowflake 관리 갱신

target_lag를 설정하면 Snowflake가 지정된 target lag를 충족하기 위해 동적 테이블을 자율적으로 갱신해요.

models:
  - name: dt_orders
    config:
      materialized: dynamic_table
      snowflake_warehouse: transform_wh
      target_lag: '10 minutes'

Snowflake 관리 갱신 사용 시:

  • Snowflake가 target_lag를 기준으로 언제·얼마나 자주 갱신할지 결정해요.
  • 캐스케이드 파이프라인이 자동으로 조정돼요. 업스트림 동적 테이블이 갱신되면 다운스트림 테이블이 의존성 순서대로 같은 스냅샷에서 갱신되어 파이프라인 전반의 일관성을 제공해요.
  • dbt run은 정의를 만들거나 변경하지만 갱신을 트리거하지 않아요. Snowflake가 이를 독립적으로 처리해요.

dbt run이 언제 실행되든 지속적인 신선도가 필요한 파이프라인(예: SLA 기반 지연 목표가 있는 대시보드)에 가장 적합해요.

처리 모델 선택

스케줄링을 제어하는 주체와 독립적으로 각 갱신에서 데이터를 처리할 방식을 선택해요.

CTAS 모델 교체 (마이그레이션 1단계)

간단한 마이그레이션 경로: SQL 변경 없이 materialized: table을 materialized: dynamic_table로 바꿔요. 기존 SELECT 문이 동적 테이블 정의가 되고, Snowflake는 소스 데이터가 변경되지 않았을 때를 감지해 추가 구성 없이 갱신을 건너뛰어요.

# Before
models:
  - name: dt_orders
    config:
      materialized: table

# After (zero SQL changes needed)
models:
  - name: dt_orders
    config:
      materialized: dynamic_table
      snowflake_warehouse: transform_wh
      refresh_mode: FULL

refresh_mode: FULL을 설정하면 처리 동작이 동일하므로 이것은 CTAS의 안전한 대체물이 돼요. refresh_mode를 생략하면 기본값은 AUTO이고 Snowflake가 생성 시점에 최적 전략을 선택해요.

증분 처리 (마이그레이션 2단계)

초기 마이그레이션 후 변경된 행만 처리하면 이점이 있는 모델을 최적화해요. dbt의 내장 증분 materialization과 달리 {% if is_incremental() %} 블록이 필요 없어요. Snowflake가 변경을 내부적으로 추적하고 델타만 처리해요.

models:
  - name: dt_orders
    config:
      materialized: dynamic_table
      snowflake_warehouse: transform_wh
      refresh_mode: INCREMENTAL

증분 처리는 append-only 소스에 대한 집계, 조인, 필터에서 작동해요. 증분 갱신을 지원하는 SQL 구조물의 전체 목록은 동적 테이블 지원 쿼리 문서를 참조하세요.

갱신 사이에 소스 데이터의 많은 비율이 변경되면 REFRESH_MODE=FULL이 증분 처리보다 성능이 좋을 수 있어요. 그런 경우 개별 행 델타 추적이 전체 결과를 다시 계산하는 것보다 비용이 더 들기 때문이에요.

구성 참조

이 속성들을 모델의 YAML config 또는 SQL 파일 상단의 config() 블록에서 설정하세요.

dbt config Snowflake DDL 상당 기본값 설명
target_lag TARGET_LAG 없음 (dbt 관리 갱신) 허용 가능한 최대 데이터 지연(예: '10 minutes', '1 hour', 또는 DOWNSTREAM). 생략하면 dbt 관리 갱신 사용.
snowflake_warehouse WAREHOUSE 프로필 기본값 갱신에 사용되는 웨어하우스.
refresh_mode REFRESH_MODE AUTO Snowflake가 데이터를 갱신하는 방식: INCREMENTAL, FULL, 또는 AUTO.
initialize INITIALIZE ON_CREATE 초기 갱신이 실행되는 시점: ON_CREATE(즉시) 또는 ON_SCHEDULE(지연).
scheduler SCHEDULER ENABLE Snowflake가 자율적으로 갱신을 스케줄하는지 여부: ENABLE 또는 DISABLE. dbt 관리 갱신에는 DISABLE로 설정.
on_configuration_change 해당 없음 (dbt 전용) apply 기존 동적 테이블에서 config가 변경될 때 dbt가 하는 일: apply(ALTER 발행), continue(경고와 함께 건너뜀), 또는 fail(실행 실패).
snowflake_initialization_warehouse INITIALIZATION_WAREHOUSE 없음 초기 전체 갱신용 별도 웨어하우스. 초기화가 이후 갱신보다 더 비쌀 때 유용.
cluster_by CLUSTER BY 없음 동적 테이블의 클러스터링 키. 컬럼 이름 또는 컬럼 목록을 받아들임.
immutable_where FROZEN WHERE 없음 생성 시점에 적용되는 필터 조건. 이 조건과 일치하는 행은 다시 처리되지 않음.
transient TRANSIENT false 일시적(transient) 동적 테이블 생성(Fail-safe 없음, 더 낮은 스토리지 비용).

dbt-snowflake 동적 테이블 config의 전체 목록은 dbt-snowflake 문서를 참조하세요.

예시 모델

이 예시는 dbt 관리 갱신이 있는 최소한의 동적 테이블 모델을 보여줘요:

# models/staging/dt_orders.yml
models:
  - name: dt_orders
    config:
      materialized: dynamic_table
      snowflake_warehouse: transform_wh
      scheduler: DISABLE
      refresh_mode: INCREMENTAL
-- models/staging/dt_orders.sql
SELECT
    order_id,
    customer_id,
    order_date,
    TRIM(UPPER(product_name)) AS product_name,
    quantity,
    unit_price
FROM {{ source('raw', 'raw_orders') }}
WHERE order_status != 'returned';

dbt run을 실행하면 이것이 CREATE DYNAMIC TABLE 문으로 컴파일된 다음 갱신을 트리거해요. 이후 실행은 SQL이 변경되지 않았는지 감지하고 모델을 완전히 건너뛰어요.

스키마 변경 처리

동적 테이블은 구성 변경과 정의 변경을 다르게 처리해요.

구성 변경

config 속성만 변경하고(target lag, 웨어하우스, 갱신 모드) on_configuration_change가 apply로 설정되면, dbt는 테이블을 교체하지 않고 ALTER DYNAMIC TABLE을 발행해 새 설정을 적용해요. on_configuration_change를 continue로 설정하면 경고와 함께 변경을 건너뛰고, fail로 설정하면 실행을 중단해요.

SQL 변경

모델의 SELECT 문을 변경하면 CREATE OR REPLACE가 트리거되어 재초기화(reinitialization)가 발생해요. 동적 테이블이 처음부터 다시 빌드돼요.

기본 테이블의 CHANGE_TRACKING

dbt가 다운스트림 동적 테이블을 공급하는 스테이징 모델에 CREATE OR REPLACE를 발행하면 변경 추적 메타데이터가 손실돼요. 파이프라인 실패를 방지하려면:

  1. 기본 테이블에 CHANGE_TRACKING = TRUE를 명시적으로 설정하세요:
ALTER TABLE raw_orders SET CHANGE_TRACKING = TRUE;
  1. 가능하면 업스트림 스테이징 테이블에 CREATE OR REPLACE 대신 INSERT OVERWRITE를 사용하세요. 이렇게 하면 테이블 객체 신원과 변경 추적 메타데이터가 보존돼요.
  2. CREATE OR REPLACE를 반드시 사용해야 하는 배포라면 실행 전에 다운스트림 동적 테이블을 일시 중단하고 실행 후 다시 시작하세요. 배포 시 일시 중단·다시 시작 문서를 참조하세요.

배포 시 일시 중단·다시 시작

dbt run --full-refresh를 실행하면 변경되지 않은 모델까지 모든 모델에 CREATE OR REPLACE를 발행해요. 각 CREATE OR REPLACE는 동적 테이블을 파괴하고 다시 만들어 재초기화를 트리거해요. 업스트림 기본 테이블도 교체되면 다운스트림 동적 테이블이 변경 추적 참조를 잃고 실패할 수 있어요.

배포 중 실패를 방지하려면 dbt 실행 전에 다운스트림 동적 테이블을 일시 중단하고 실행 후 다시 시작하세요:

# models/marts/dt_orders_daily.yml
models:
  - name: dt_orders_daily
    config:
      materialized: dynamic_table
      snowflake_warehouse: transform_wh
      target_lag: '30 minutes'
      +pre-hook:
        - "ALTER DYNAMIC TABLE IF EXISTS {{ this }} SUSPEND"
      +post-hook:
        - "ALTER DYNAMIC TABLE IF EXISTS {{ this }} RESUME"

IF EXISTS 절은 모델이 처음 생성되어 테이블이 아직 존재하지 않을 때 오류를 방지해요.

QUOTED_IDENTIFIERS_IGNORE_CASE를 FALSE로 설정

dbt-snowflake 어댑터는 SHOW DYNAMIC TABLES 결과에서 소문자 컬럼 이름을 기대해요. Snowflake 계정에 QUOTED_IDENTIFIERS_IGNORE_CASE=TRUE가 있으면 SHOW DYNAMIC TABLES가 대문자 컬럼 이름을 반환해 어댑터의 config 파서가 다음 오류로 실패해요:

SnowflakeDynamicTableConfig.__init__() missing 6 required positional arguments

이 문제는 dbt-snowflake v1.10.0에서 수정됐어요. 이전 버전에서는 계정 또는 세션 수준에서 매개 변수를 FALSE로 설정하세요:

# ~/.dbt/profiles.yml
my_project:
  target: dev
  outputs:
    dev:
      type: snowflake
      account: my_account
      # ... other connection settings ...
      session_parameters:
        QUOTED_IDENTIFIERS_IGNORE_CASE: FALSE

제한 사항

제한 사항 세부 내용
모델 계약 없음 dbt 모델 계약(컬럼 타입 강제)은 dynamic_table materialization에서 지원되지 않아요. 대신 dbt 테스트로 컬럼 타입을 검증하세요.
copy grants 없음 copy_grants config는 지원되지 않아요. grant는 각 CREATE OR REPLACE에서 재설정돼요. 배포 후 권한을 다시 부여하세요.
SQL 변경은 전체 교체 필요 모델 SQL 변경은 CREATE OR REPLACE를 트리거해 재초기화를 일으켜요. config 전용 변경(target lag, 웨어하우스)은 on_configuration_change가 apply로 설정되면 ALTER로 적용돼요.
지원되지 않는 업스트림 소스 타입 동적 테이블은 구체화된 뷰, 외부 테이블, 디렉터리 테이블, 스트림을 업스트림 소스로 참조할 수 없어요. 이는 Snowflake 플랫폼 제약이에요. 전체 목록은 동적 테이블 지원 쿼리 문서를 참조하세요.
--full-refresh가 모든 모델 재빌드 dbt run --full-refresh를 실행하면 변경되지 않은 모델을 포함한 모든 모델에 CREATE OR REPLACE를 발행해 불필요한 재초기화를 일으켜요. 위에서 설명한 SUSPEND/RESUME 패턴을 사용하거나, 변경되지 않은 모델을 full-refresh 실행에서 제외하도록 태그하세요.
dbt 테스트는 현재 상태에 대해 실행 dbt 테스트는 특정 갱신 결과가 아니라 현재 테이블 상태를 쿼리해요. dbt test가 실행될 때 동적 테이블이 갱신 중이면 테스트는 사용 가능한 상태를 읽어요. 갱신 완료를 확인한 후 테스트를 스케줄하세요.

다음 단계

  • 갱신 스케줄링에 대해 알아보려면, 동적 테이블의 target lag 설정 문서를 참조하세요.
  • 증분 갱신을 지원하는 SQL 구조물을 이해하려면, 동적 테이블 지원 쿼리 문서를 참조하세요.
  • dbt 배포 후 갱신 상태를 모니터링하려면, 동적 테이블 모니터링 문서를 참조하세요.
  • 동적 테이블 수명 주기 작업을 관리하려면, 동적 테이블 관리 문서를 참조하세요.

더 알아보기 (Learn more)