← 학습 카테고리

Learn

dbt

11개 모듈 · 현재 9번째

dbt 모듈 9/11 dbt-learn-09

Pre/Post 훅, 스키마 오버라이드, 태그, var()

50 dbt Interview Questions for Data Engineers (Real Answers, 2026) — 미상 (데이터 엔지니어링 커리어 블로그, 2026) Section 4: Macros, Packages & Advanced, Q34-Q38 (pp.11-12)

이 모듈을 다 읽으면

  • pre-hook/post-hook의 용도와 트랜잭션 관점의 리스크를 웨어하우스별로 구분해 설명할 수 있다
  • generate_schema_name 매크로를 오버라이드해야 하는 이유와 방법을 설명할 수 있다
  • 태그와 var()가 각각 실행 범위 제어와 런타임 파라미터화에 어떻게 쓰이는지 판단할 수 있다

훅은 모델 실행 직전/직후에 실행되는 SQL로 권한 부여나 감사 로깅에 쓰이지만, 트랜잭션을 지원하는 웨어하우스에서는 기본적으로 모델과 같은 트랜잭션 안에서 실행된다는 점을 정확히 알아야 한다. 스키마 오버라이드는 기본적으로 타겟 스키마와 커스텀 스키마를 이어붙이므로, 정확한 이름을 원하면 generate_schema_name 매크로를 오버라이드해야 한다. 태그는 실행 범위를 제어하는 라벨이고, var()는 dbt_project.yml이나 --vars로 전달하는 런타임 변수를 읽는다.

Pre-hook / Post-hook

훅은 모델 실행 직전 또는 직후에 dbt가 실행하는 SQL 문이다. 흔한 용도는 테이블 생성 후 권한을 부여하는 것(post-hook: "GRANT SELECT ON {{ this }} TO ROLE analyst"), ANALYZE/VACUUM 실행, 실행 메타데이터를 감사 테이블에 로깅하는 것이다. 모델의 config 블록이나 dbt_project.yml에서 설정한다.

— 정정: 원문은 '훅은 dbt의 트랜잭션 바깥에서 실행되므로 실패한 post-hook이 모델을 롤백시키지 않는다'고 설명하지만, 이는 웨어하우스에 따라 다르다. dbt 공식 문서에 따르면, 트랜잭션을 지원하는 어댑터(Postgres, Redshift 등)에서는 pre-hook/post-hook이 기본적으로 모델 빌드와 '같은' 트랜잭션 안에서 실행된다 — 즉 이런 웨어하우스에서는 post-hook이 실패하면 실제로 모델 생성까지 함께 롤백될 수 있다(훅 설정에서 명시적으로 트랜잭션 밖으로 뺄 수도 있다). 반면 Snowflake, BigQuery, Databricks SQL 웨어하우스처럼 DDL을 트랜잭션으로 감싸지 않는 웨어하우스에서는 애초에 감쌀 트랜잭션이 없으므로 훅 실패가 모델 생성을 되돌리지 못하는 것이 사실이다. 즉 '트랜잭션 바깥에서 항상 실행된다'는 일반화는 틀렸고, 정확히는 '웨어하우스가 트랜잭션을 지원하는지에 따라 다르다'가 맞다.

핵심 포인트

  • 훅의 흔한 용도: 테이블 생성 후 권한 부여(GRANT), ANALYZE/VACUUM, 실행 메타데이터 감사 로깅
  • Postgres/Redshift처럼 트랜잭션을 지원하는 웨어하우스에서는 훅이 기본적으로 모델과 같은 트랜잭션 안에서 실행되어, 실패 시 모델 생성까지 롤백될 수 있다 — 정정
  • Snowflake/BigQuery/Databricks처럼 DDL을 트랜잭션으로 감싸지 않는 웨어하우스에서만 '훅 실패가 모델을 롤백시키지 않는다'는 설명이 실제로 성립한다

스키마 오버라이드, 태그, var()

기본적으로 dbt는 프로필 target에 정의된 스키마에 쓴다. 이를 오버라이드하려면 모델 config에서 schema를 설정한다: {{ config(schema='finance') }}. 하지만 함정이 있다 — dbt는 기본적으로 타겟 스키마와 커스텀 스키마를 이어붙여 finance가 아니라 dev_finance 같은 이름을 만든다. 정확히 원하는 스키마 이름을 얻으려면 generate_schema_name 매크로를 오버라이드해야 한다. 이는 가장 자주 나오는 고급 dbt 질문 중 하나다.

기본 generate_schema_name은 target_schema + '_' + custom_schema를 반환한다. 팀들은 프로덕션에서는 schema='finance'로 설정된 모델이 정확히 finance에 쓰이고, dev에서는 dev_finance에 쓰이도록 이를 오버라이드한다. 전형적인 오버라이드는 target 이름을 검사한다: {% macro generate_schema_name(custom_schema_name, node) %} {% if target.name == 'prod' and custom_schema_name is not none %} {{ custom_schema_name }} {% else %} {{ default__generate_schema_name(custom_schema_name, node) }} {% endif %} {% endmacro %}. 이렇게 하면 dev 실행이 프로덕션 스키마를 오염시키지 않으면서도 prod 스키마 이름은 깔끔하게 유지된다.

태그는 모델·테스트·스냅샷에 config나 YAML로 붙이는 라벨이다. 운영 관점에서는 dbt run --select tag:daily처럼 프로젝트의 일부만 실행할 수 있게 해준다. 팀들은 daily, hourly, critical, finance 같은 태그로 오케스트레이터의 실행을 조직화한다. 안티패턴은 모든 것에 태그를 붙이고 실제로는 그 태그를 전혀 활용하지 않는 것이다 — 태그 분류 체계는 작고 문서화된 상태를 유지해야 한다.

var()는 dbt_project.yml에 정의되거나 --vars로 런타임에 전달되는 프로젝트 레벨 변수를 읽는다. 예: dbt run --vars '{start_date: 2026-01-01}'과 모델 안의 {{ var('start_date') }}. 이는 백필과 환경별 로직에 유용하다. 변수가 전달되지 않아도 모델이 깨지지 않도록 항상 기본값을 정의해야 한다({{ var('start_date', '2026-01-01') }}).

핵심 포인트

  • config(schema=...)로 스키마를 오버라이드해도 기본적으로 타겟 스키마 접두사가 붙어 dev_finance처럼 되므로, 정확한 이름을 원하면 generate_schema_name을 오버라이드해야 한다
  • target.name을 검사하는 generate_schema_name 오버라이드로 dev는 접두사 유지, prod는 정확한 스키마명을 쓰게 나눌 수 있다
  • 태그는 dbt run --select tag:daily처럼 실행 범위를 제어하는 라벨이며, 태그 체계는 작고 문서화된 상태를 유지해야 한다
  • var()는 dbt_project.yml 기본값 또는 --vars 런타임 전달값을 읽으며, 기본값을 지정해야 변수 누락 시에도 모델이 깨지지 않는다