pre-hook & post-hook

pre-hook & post-hook

모델, 시드, 스냅샷이 빌드되기 전이나 후에 실행할 SQL 문(또는 SQL 문 목록)을 지정하는 설정이에요. dbt가 (아직) 내장 기능으로 제공하지 않는 데이터 플랫폼 특유의 SQL을 실행하고 싶을 때 유용해요.

출처: dbt 공식 문서

본문

모델

이 예시들에서 | 기호는 pre-hooks와 post-hooks의 SQL 문에 대한 두 가지 다른 형식 옵션을 구분해요. 첫 번째 옵션(대괄호 없음)은 단일 SQL 문을 문자열로 받고, 두 번째 옵션(대괄호)은 여러 SQL 문을 문자열 배열로 받아요. SQL-STATEMENT를 여러분의 SQL로 바꿔요.

dbt_project.yml


models:
  <resource-path>:
    +pre-hook: SQL-statement | [SQL-statement]
    +post-hook: SQL-statement | [SQL-statement]

models/<model_name>.sql


{{ config(
    pre_hook="SQL-statement" | ["SQL-statement"],
    post_hook="SQL-statement" | ["SQL-statement"],
) }}

select ...

models/properties.yml

models:
  - name: [<model_name>]
    config:
      pre_hook: <sql-statement> | [<sql-statement>]
      post_hook: <sql-statement> | [<sql-statement>]

시드

이 예시들에서 | 기호는 pre-hooks와 post-hooks의 SQL 문에 대한 두 가지 다른 형식 옵션을 구분해요. 첫 번째 옵션(대괄호 없음)은 단일 SQL 문을 문자열로 받고, 두 번째 옵션(대괄호)은 여러 SQL 문을 문자열 배열로 받아요. SQL-STATEMENT를 여러분의 SQL로 바꿔요.

dbt_project.yml


seeds:
  <resource-path>:
    +pre-hook: SQL-statement | [SQL-statement]
    +post-hook: SQL-statement | [SQL-statement]

seeds/properties.yml

seeds:
  - name: [<seed_name>]
    config:
      pre_hook: <sql-statement> | [<sql-statement>]
      post_hook: <sql-statement> | [<sql-statement>]

스냅샷

이 예시들에서 | 기호는 pre-hooks와 post-hooks의 SQL 문에 대한 두 가지 다른 형식 옵션을 구분해요. 첫 번째 옵션(대괄호 없음)은 단일 SQL 문을 문자열로 받고, 두 번째 옵션(대괄호)은 여러 SQL 문을 문자열 배열로 받아요. SQL-STATEMENT를 여러분의 SQL로 바꿔요.

dbt_project.yml


snapshots:
  <resource-path>:
    +pre-hook: SQL-statement | [SQL-statement]
    +post-hook: SQL-statement | [SQL-statement]

snapshots/snapshot.yml

snapshots:
  - name: [<snapshot_name>]
    config:
      pre_hook: <sql-statement> | [<sql-statement>]
      post_hook: <sql-statement> | [<sql-statement>]

정의

모델, 시드, 스냅샷이 빌드되기 전이나 후에 실행할 SQL 문(또는 SQL 문 목록)이에요.

pre-hooks와 post-hooks는 SQL 문을 반환하는 매크로도 호출할 수 있어요. 매크로가 실행 시점에만 사용 가능한 값(예: 모델 설정을 사용하거나 다른 리소스에 대한 ref() 호출을 입력으로 사용)에 의존한다면, 매크로 호출을 추가 중괄호 쌍으로 감싸야 해요.

왜 hooks를 쓰나요?

dbt는 필요한 모든 상용구 SQL(DDL, DML, DCL)을 기본 제공 기능으로 제공하는 것을 목표로 해요. 이를 빠르고 간결하게 구성할 수 있어요. 하지만 데이터 플랫폼의 특정 기능에 필요한 SQL이 있어서 dbt가 (아직) 내장 기능으로 제공하지 않는 경우가 있을 수 있어요. 그런 경우 dbt의 컴파일 컨텍스트를 사용해 필요한 정확한 SQL을 작성하고, 이를 pre- 또는 post- hook에 넣어 모델, 시드, 스냅샷 앞이나 뒤에서 실행할 수 있어요.

render 메서드

.render() 메서드는 일반적으로 런타임 중에 Jinja 표현식(예: {{ source(...) }})을 해석하거나 평가하는 데 쓰여요.

--empty 플래그를 사용하면 dbt가 최적화를 위해 ref()source() 처리를 건너뛸 수 있어요. 컴파일 오류를 피하고 특정 관계(ref() 또는 source())를 명시적으로 처리하라고 dbt에 알리려면 모델 파일에서 .render() 메서드를 사용해요. 예를 들어:

models.sql

{{ config(
    pre_hook = [
        "alter external table {{ source('sys', 'customers').render() }} refresh"
    ]
) }}

select ...

예시

[Redshift] 모델 하나를 S3로 언로드

model.sql

{{ config(
  post_hook = "unload ('select from {{ this }}') to 's3:/bucket_name/{{ this }}"
) }}

select ...

참고: Redshift의 UNLOAD 문서

[Apache Spark] 테이블 생성 후 분석

dbt_project.yml


models:
  jaffle_shop: # this is the project name
    marts:
      finance:
        +post-hook:
          # this can be a list
          - "analyze table {{ this }} compute statistics for all columns"
          # or call a macro instead
          - "{{ analyze_table() }}"

참고: Apache Spark의 ANALYZE TABLE 문서

추가 예시

더 심층적인 예시는 여기에 정리해 두었어요.

사용 시 참고 사항

Hooks는 누적돼요

dbt_project.yml과 모델의 config 블록 양쪽에 hooks를 정의하면 두 hooks 집합 모두 모델에 적용돼요.

실행 순서

어떤 hooks의 인스턴스가 여러 개 정의되면 dbt는 다음 순서로 각 hook을 실행해요.

  1. 의존 패키지의 hooks가 활성 패키지의 hooks보다 먼저 실행돼요.
  2. 모델 자체 안에 정의된 hooks는 dbt_project.yml에 정의된 hooks보다 나중에 실행돼요.
  3. 특정 컨텍스트 안의 hooks는 정의된 순서대로 실행돼요.

트랜잭션 동작

트랜잭션을 사용하는 어댑터(특히 Postgres나 Redshift)를 쓰고 있다면, hooks가 기본적으로 모델이 생성되는 것과 동일한 트랜잭션 안에서 실행된다는 점을 알아두는 게 좋아요.

hooks를 트랜잭션 밖에서 실행해야 하는 경우가 있을 수 있어요. 예를 들어:

  • post-hook에서 VACUUM을 실행하고 싶은데, 이는 트랜잭션 안에서 실행할 수 없어요. (Redshift 문서)
  • 실행 시작 시 감사(audit) 테이블에 레코드를 삽입하고 싶은데, 모델 생성이 실패해도 그 문이 롤백되길 원하지 않을 때.

이 동작을 얻으려면 다음 구문 중 하나를 사용할 수 있어요.

  • 중요 참고: dbt가 트랜잭션을 지원하지 않는 데이터베이스(예: Snowflake, BigQuery, Spark 또는 Databricks)를 쓰고 있다면 이 구문을 사용하지 마세요.
before_begin과 after_commit 사용하기
설정 블록: before_beginafter_commit 헬퍼 매크로 사용

models/.sql

{{
  config(
    pre_hook=before_begin("SQL-statement"),
    post_hook=after_commit("SQL-statement")
  )
}}

select ...
딕셔너리 사용하기
설정 블록: 딕셔너리 사용

models/.sql

{{
  config(
    pre_hook={
      "sql": "SQL-statement",
      "transaction": False
    },
    post_hook={
      "sql": "SQL-statement",
      "transaction": False
    }
  )
}}

select ...
dbt_project.yml 사용하기
dbt_project.yml: 딕셔너리 사용

dbt_project.yml


models:
  +pre-hook:
    sql: "SQL-statement"
    transaction: false
  +post-hook:
    sql: "SQL-statement"
    transaction: false

더 알아보기 (Learn more)