레거시 스냅샷 구성

레거시 스냅샷 구성 (Legacy snapshot configuration)

어떤 dbt 버전에서든 Jinja 블록이 있는 레거시 SQL 기반 스냅샷 구성을 쓸 수 있어요. dbt v1.9는 더 나은 가독성과 환경 인지(environment awareness)를 위해 YAML 기반 config를 도입했어요. 이 페이지는 레거시 SQL 기반 구성을 필요할 때 어떻게 쓰는지 설명해요.

출처: 문서

본문

어떤 dbt 버전이나 릴리스 트랙에서든 스냅샷에 레거시 문법을 쓰고 싶은 상황이 있어요. 이 페이지는 필요할 때 레거시 SQL 기반 구성을 어떻게 쓰는지 자세히 다뤄요.

dbt v1.9에서 이 문법은 dbt의 v1 Latest 릴리스 트랙에서 YAML 기반 구성으로 대체됐어요. YAML 기반 구성의 장점은 스냅샷이 환경을 인지한다는 거예요. 즉 schemadatabase를 지정하지 않아도 되고, 문법이 더 간결해요.

새 스냅샷에는 이 최신 YAML 기반 config를 권장해요. 기존 스냅샷을 YAML 기반 구성으로 옮기고 싶다면 마이그레이션할 수 있어요.

SQL 기반 문법과 YAML 기반 문법을 언제 쓰나요?

  • SQL 기반 문법:

    • 보통 snapshots 디렉터리에 있는 스냅샷 Jinja 블록 안의 .sql 파일에 정의돼요. 모든 버전에서 사용 가능해요.
    • 이미 이 문법을 쓰는 기존 스냅샷에 유용해요.
    • 아주 가벼운 변환을 수행하는 데 적합해요 (다만 유지보수성을 위해 변환은 별도의 ephemeral 모델로 만드는 걸 권장해요).
  • YAML 기반 문법:

    • whatever_name.yml 또는 선호하는 snapshots/models 디렉터리에 정의돼요. dbt의 v1 Latest 릴리스 트랙과 dbt v1.9 이상에서 사용 가능해요.
    • 새 스냅샷이나 마이그레이션이 필요한 기존 스냅샷에 이상적이에요.
    • ephemeral 모델을 만들어 스냅샷 파일과 별도로 변환을 수행하고, relation 필드로 스냅샷에서 그 모델을 참조해요.

스냅샷 구성

더 성능 좋은 YAML 기반 구성을 쓸 수 있지만, 필요에 맞다면 레거시 구성으로 스냅샷을 정의하고 싶을 수도 있어요.

스냅샷은 두 가지 주요 방식으로 구성할 수 있어요.

  • 스냅샷 전용 구성 사용
  • 또는 일반 구성 사용

이 구성들은 dbt가 데이터의 변경을 어떻게 감지하고 스냅샷이 어디에 저장되는지 제어할 수 있게 해줘요. 두 타입의 구성 모두 같은 config 블록 안에서(또는 dbt_project.yml 파일이나 properties.yaml 파일에서) 프로젝트에 공존할 수 있어요.

가장 중요한 구성 중 하나는 dbt가 수정된 행을 어떻게 감지하는지 알려주는 전략(strategy)이에요.

스냅샷 전용 구성

스냅샷 전용 구성은 여러 리소스 타입이 아니라 하나의 dbt 리소스 타입에만 적용돼요. 이 설정은 리소스 파일 안에서 {{ config() }} 매크로로 정의할 수 있어요(프로젝트 파일(dbt_project.yml)이나 property 파일(models/properties.yml, 다른 리소스도 비슷)에서도 가능해요).

snapshots/orders_snapshot.sql

{% snapshot orders_snapshot %}

{{ config(
    target_schema="<string>",
    target_database="<string>",
    unique_key="<column_name_or_expression>",
    strategy="timestamp" | "check",
    updated_at="<column_name>",
    check_cols=["<column_name>"] | "all"
    invalidate_hard_deletes=true | false
)
}}

select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

일반 구성 (General configuration)

여러 리소스 타입에 걸쳐 적용되는 더 넓은 운영 설정에는 일반 구성을 사용해요. 리소스 전용 구성처럼, 이것도 프로젝트 YAML 파일, properties YAML 파일, 또는 config 블록을 사용한 리소스 전용 파일에서 설정할 수 있어요.

snapshots/snapshot.sql

{{ config(
    enabled=true | false,
    tags="<string>" | ["<string>"],
    alias="<string>",
    pre_hook="<sql-statement>" | ["<sql-statement>"],
    post_hook="<sql-statement>" | ["<sql-statement>"]
    persist_docs={<dict>}
    grants={<dict>}
) }}

스냅샷 전략

스냅샷 "전략(strategy)"은 dbt가 행이 바뀌었는지 어떻게 아는지 정의해요. strategy 파라미터가 필요한 내장 전략이 두 가지 있어요.

  • Timestampupdated_at 컬럼을 사용해 행이 바뀌었는지 판단.
  • Check — 컬럼 목록을 현재 값과 과거 값 사이에서 비교해 행이 바뀌었는지 판단. check_cols 파라미터를 사용.

timestamp 전략은 updated_at 필드로 행이 바뀌었는지 판단해요. 행의 설정된 updated_at 컬럼이 스냅샷이 마지막으로 실행된 때보다 더 최근이면, dbt는 이전 레코드를 무효화하고 새 레코드를 기록해요. 타임스탬프가 그대로면 dbt는 아무 조치도 하지 않아요.

예시

snapshots/timestamp_example.sql

{% snapshot orders_snapshot_timestamp %}

    {{
        config(
          target_schema='snapshots',
          strategy='timestamp',
          unique_key='id',
          updated_at='updated_at',
        )
    }}

    select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

check 전략은 신뢰할 수 있는 updated_at 컬럼이 없는 테이블에 유용해요. 변경을 확인할 스냅샷 쿼리 결과의 컬럼 목록인 check_cols 파라미터가 필요해요. 또는 all 값을 사용해 모든 컬럼을 사용할 수 있어요(다만 성능이 떨어질 수 있어요).

예시

snapshots/check_example.sql

{% snapshot orders_snapshot_check %}

    {{
        config(
          strategy='check',
          unique_key='id',
          check_cols=['status', 'is_cancelled'],
        )
    }}

    select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

예시

컬럼 목록의 변경 확인 — snapshots/check_example.sql

{% snapshot orders_snapshot_check %}

    {{
        config(
          strategy='check',
          unique_key='id',
          check_cols=['status', 'is_cancelled'],
        )
    }}

    select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

모든 컬럼의 변경 확인 — snapshots/check_example.sql

{% snapshot orders_snapshot_check %}

    {{
        config(
          strategy='check',
          unique_key='id',
          check_cols='all',
        )
    }}

    select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

구성 레퍼런스

스냅샷을 구성해 dbt에 레코드 변경을 어떻게 감지할지 알려주세요. 스냅샷은 .sql 파일(보통 snapshots 디렉터리나 다른 디렉터리)의 스냅샷 블록 안에 정의되는 select 문이에요.

다음 표는 스냅샷에 사용 가능한 구성을 요약해요.

(dbt v1.9 이상 적용)

  • tags, post-hook 같은 다른 구성도 많이 지원돼요. 전체 목록은 여기를 확인하세요.
  • 스냅샷은 dbt_project.yml 파일과 config 블록 둘 다에서 구성할 수 있어요. 자세한 내용은 configuration docs를 참고하세요.
  • 참고: BigQuery 사용자는 target_databasetarget_schema의 별칭으로 target_projecttarget_dataset을 쓸 수 있어요.
  • v1.9 이전에는 target_schema(필수)와 target_database(선택)가 스냅샷에 고정 스키마나 데이터베이스를 설정해 dev와 prod 환경을 구분하기 어려웠어요. v1.9에서 target_schema는 선택 사항이 되어 환경 인지 스냅샷이 가능해졌어요. 기본적으로 스냅샷은 이제 generate_schema_name이나 generate_database_name을 사용하지만, 다른 리소스 타입과 일관되게 schemadatabase로 커스텀 위치를 지정할 수도 있어요.

프로젝트에 스냅샷 추가

프로젝트에 스냅샷을 추가하려면:

  1. snapshots 디렉터리에 .sql 파일 확장자의 파일을 만들어요. 예: snapshots/orders.sql
  2. snapshot 블록으로 스냅샷의 시작과 끝을 정의해요.

snapshots/orders_snapshot.sql

{% snapshot orders_snapshot %}

{% endsnapshot %}
  1. 스냅샷 블록 안에 select 문을 작성해요 (좋은 스냅샷 쿼리 작성 팁은 아래에 있어요). 이 select 문은 시간이 지나며 스냅샷하려는 결과를 정의해요. 여기서 sourcesrefs를 쓸 수 있어요.

snapshots/orders_snapshot.sql

{% snapshot orders_snapshot %}

select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}
  1. 쿼리 결과 집합에 레코드가 마지막으로 갱신된 시점을 나타내는 신뢰할 수 있는 타임스탬프 컬럼이 포함되어 있는지 확인해요. 이 예시에서 updated_at 컬럼이 레코드 변경을 신뢰성 있게 나타내므로 timestamp 전략을 쓸 수 있어요. 쿼리 결과 집합에 신뢰할 수 있는 타임스탬프가 없다면 대신 check 전략을 써야 해요 — 자세한 내용은 다음 단계에 있어요.

  2. config 블록으로 스냅샷에 구성을 추가해요. dbt_project.yml 파일에서도 스냅샷을 구성할 수 있어요.

(dbt v1.9 이상 적용) snapshots/orders_snapshot.sql

{% snapshot orders_snapshot %}

{{
    config(
      database='analytics',
      schema='snapshots',
      unique_key='id',

      strategy='timestamp',
      updated_at='updated_at',
    )

select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}
  1. dbt snapshot 명령을 실행해요. 이 예시에서 analytics.snapshots.orders_snapshot에 새 테이블이 만들어져요. target_database 구성, target_schema 구성, 그리고 스냅샷 이름({% snapshot .. %}에서 정의된)을 바꾸면 dbt가 이 테이블에 이름을 붙이는 방식이 달라져요.
Running with dbt=1.8.0

15:07:36 | Concurrency: 8 threads (target='dev')
15:07:36 |
15:07:36 | 1 of 1 START snapshot snapshots.orders_snapshot...... [RUN]
15:07:36 | 1 of 1 OK snapshot snapshots.orders_snapshot..........[SELECT 3 in 1.82s]
15:07:36 |
15:07:36 | Finished running 1 snapshots in 0.68s.

Completed successfully

Done. PASS=2 ERROR=0 SKIP=0 TOTAL=1
  1. dbt가 만든 테이블에서 select해서 결과를 검사해요. 첫 실행 후에는 쿼리 결과와 앞서 설명한 스냅샷 메타 필드를 볼 수 있어요.

  2. dbt snapshot 명령을 다시 실행하고 결과를 검사해요. 레코드가 갱신되었다면 스냅샷에 반영되어야 해요.

  3. 다운스트림 모델에서 ref 함수로 snapshot을 선택해요.

models/changed_orders.sql

select * from {{ ref('orders_snapshot') }}
  1. 스냅샷은 자주 실행할 때만 유용해요 — snapshot 명령을 정기적으로 실행하도록 스케줄링해요.

예시

이 섹션은 레거시 방식으로 스냅샷에 구성을 적용하는 몇 가지 예시를 설명해요.

하나의 스냅샷에만 구성 적용 — config 블록은 하나의 스냅샷에만 구성을 적용해야 할 때 써요. snapshots/postgres_app/orders_snapshot.sql

{% snapshot orders_snapshot %}
    {{
        config(
          unique_key='id',
          strategy='timestamp',
          updated_at='updated_at'
        )
    }}
    -- Pro-Tip: Use sources in snapshots!
    select * from {{ source('jaffle_shop', 'orders') }}
{% endsnapshot %}

updated_at 파라미터 사용 — updated_at 파라미터는 timestamp 전략을 쓸 때 필수예요. updated_at 파라미터는 레코드 행이 마지막으로 갱신된 시점을 나타내는 스냅샷 쿼리 결과의 컬럼이에요. snapshots/orders.sql

{{ config(
  strategy="timestamp",
  updated_at="column_name"
) }}

예시

updated_at 컬럼 이름 사용하기:

(dbt v1.9 이상 적용) snapshots/orders.sql

{% snapshot orders_snapshot %}

{{
    config(
      schema='snapshots',
      unique_key='id',

      strategy='timestamp',
      updated_at='updated_at'
    )
}}

select * from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

두 컬럼을 coalesce해 신뢰할 수 있는 updated_at 컬럼 만들기:

레코드가 갱신될 때만 updated_at 컬럼이 채워지는 데이터 소스를 생각해 볼게요 (null 값은 레코드가 생성된 뒤 갱신된 적이 없다는 뜻).

updated_at 구성은 표현식이 아니라 컬럼 이름만 받기 때문에, 스냅샷 쿼리를 coalesced 컬럼을 포함하도록 수정해야 해요.

(dbt v1.9 이상 적용) snapshots/orders.sql

{% snapshot orders_snapshot %}

{{
    config(
      schema='snapshots',
      unique_key='id',

      strategy='timestamp',
      updated_at='updated_at_for_snapshot'
    )
}}

select
    *,
    coalesce(updated_at, created_at) as updated_at_for_snapshot

from {{ source('jaffle_shop', 'orders') }}

{% endsnapshot %}

unique_key 파라미터 사용 — unique_key는 스냅샷 입력에 대해 고유한 컬럼 이름이나 표현식이에요. dbt는 결과 집합과 기존 스냅샷 사이에서 레코드를 매칭하기 위해 unique_key를 사용해 변경 사항을 올바르게 캡처해요. snapshots/orders.sql

{{ config(
  unique_key="column_name"
) }}

예시

id 컬럼을 unique key로 사용하기 — snapshots/orders.sql

{{
    config(
      unique_key="id"
    )
}}

이것을 YAML로 쓸 수도 있어요. 여러 스냅샷이 같은 unique_key를 공유한다면 YAML로 쓰는 게 좋을 수 있어요 (다만 위처럼 config 블록에서 구성하는 걸 선호해요).

두 컬럼의 조합을 unique key로 사용하기 — 이 구성은 유효한 컬럼 표현식을 받아요. 필요하다면 두 컬럼을 연결해 unique key로 쓸 수 있어요. 고유성을 보장하려면 구분자(예: -)를 쓰는 게 좋아요. snapshots/transaction_items_snapshot.sql

{% snapshot transaction_items_snapshot %}

    {{
        config(
          unique_key="transaction_id||'-'||line_item_id",
          ...
        )
    }}

select
    transaction_id||'-'||line_item_id as id,
    *
from {{ source('erp', 'transactions') }}

{% endsnapshot %}

다만, 이 컬럼을 쿼리에서 구성해 그걸 unique_key로 쓰는 게 더 나을 것 같아요.

snapshots/transaction_items_snapshot.sql

{% snapshot transaction_items_snapshot %}

    {{
        config(
          unique_key="id",
          ...
        )
    }}

select
    transaction_id || '-' || line_item_id as id,
    *
from {{ source('erp', 'transactions') }}

{% endsnapshot %}

더 알아보기 (Learn more)