← 학습 카테고리

Learn

dbt

11개 모듈 · 현재 8번째

dbt 모듈 8/11 dbt-learn-08

Jinja와 매크로, run_query(), dbt 패키지

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

이 모듈을 다 읽으면

  • Jinja의 컴파일 타임과 SQL의 쿼리 타임이 분리되어 있다는 멘탈 모델을 설명할 수 있다
  • 매크로를 언제 작성해야 하고 언제 과도한 추상화가 되는지 판단할 수 있다
  • run_query()의 동작과 트레이드오프, dbt-utils의 대표 매크로를 설명할 수 있다

Jinja는 dbt가 SQL을 동적으로 만드는 템플릿 언어로, 컴파일 타임에 실행되고 그 결과로 나온 순수 SQL만 쿼리 타임에 웨어하우스에서 실행된다. 매크로는 macros/ 아래 저장하는 재사용 가능한 Jinja 함수로 반복 SQL 패턴을 제거하지만 과도하게 추상화하면 오히려 가독성을 해친다. run_query()는 컴파일 시점에 SQL을 실행해 결과를 Jinja에서 순회할 수 있게 해주는 강력하지만 주의가 필요한 기능이며, dbt-utils 같은 패키지는 packages.yml과 dbt deps로 설치해 반복 SQL을 대체한다.

Jinja: 컴파일 타임 vs 쿼리 타임

Jinja는 dbt가 SQL을 동적으로 만들 수 있게 해주는 템플릿 언어다. 변수({{ }}), 제어 흐름({% if %}, {% for %}), 매크로 호출을 제공한다. dbt는 Jinja-SQL을 웨어하우스에 보내기 전에 순수 SQL로 컴파일한다. 핵심 멘탈 모델은 'Jinja는 컴파일 타임에 실행되고, SQL은 쿼리 타임에 실행된다'는 것이다. 이 두 단계를 혼동하면 — 예를 들어 SQL 쿼리 결과를 Jinja의 {% if %} 안에서 곧바로 쓰려고 하면 — 혼란스러운 에러를 만나게 된다.

핵심 포인트

  • Jinja는 변수·제어 흐름·매크로 호출을 제공하는 dbt의 SQL 템플릿 언어다
  • Jinja는 컴파일 타임에 실행되어 순수 SQL을 만들고, 그 SQL만 쿼리 타임에 웨어하우스에서 실행된다
  • SQL 실행 결과를 컴파일 타임 Jinja 로직 안에서 바로 쓰려는 시도는 두 단계를 혼동한 것이라 에러로 이어진다

매크로, run_query(), 패키지와 dbt-utils

매크로는 macros/ 아래 .sql 파일에 저장되는 재사용 가능한 Jinja 함수다. 예: {% macro cents_to_dollars(column_name) %} ({{ column_name }} / 100.0)::numeric(16,2) {% endmacro %}를 정의해 두면 모델에서 {{ cents_to_dollars('amount_cents') }}로 호출한다. 매크로는 복붙되는 SQL 패턴을 없애고 일관성을 강제한다. 피해야 할 실수는 원본 SQL보다 읽기 어려울 만큼 지나치게 추상적인 매크로를 만드는 것이다 — 매크로가 3줄 미만을 절약하거나 단 하나의 모델에서만 쓰인다면 그 간접화(indirection)는 대체로 가치가 없다.

run_query()는 컴파일 타임에 SQL 문을 실행하고 Jinja에서 순회할 수 있는 Agate 테이블 객체를 반환한다. 이는 동적 스키마 생성에 강력하다 — 예를 들어 information_schema를 조회해 컬럼 목록을 얻고 각각에 대한 SQL을 생성하는 식이다. 함정은 run_query()가 컴파일 도중 웨어하우스에 대해 실행되므로 매 dbt run마다 지연이 추가되고 웨어하우스가 다운되어 있으면 실패할 수 있다는 점이다. 아껴서 사용해야 한다.

dbt 패키지는 자신의 프로젝트에 import하는 재사용 가능한 dbt 프로젝트다. packages.yml에 선언하고 dbt deps로 설치한다. 가장 흔한 것은 dbt-utils(범용 SQL 헬퍼), dbt-expectations(Great Expectations 스타일 테스트), dbt-codegen(모델 YAML 자동 생성)이다. 패키지는 dbt_packages/ 아래 설치되며 gitignore 대상이어야 한다.

dbt-utils에서 자주 쓰는 매크로 세 가지: 여러 컬럼으로부터 결정론적 해시 키를 만들어 디멘셔널 모델의 서로게이트 키를 만드는 데 필수적인 매크로, 시계열 데이터의 빈 날짜를 채우는 데 쓰는 연속 날짜 범위 생성 매크로, 컬럼 순서 차이를 처리하면서 동일 스키마의 여러 테이블(예: 지역별로 샤딩된 테이블)을 union하는 매크로. 이 세 가지만으로도 대부분 프로젝트에서 수 시간의 보일러플레이트 SQL을 없앨 수 있다.

— 정정: 서로게이트 키를 만드는 매크로의 이름이 원문에서는 'surrogate_key'로 나오지만, 이는 옛 이름이다. dbt-utils는 이 매크로를 generate_surrogate_key로 개명하며 구현도 개선했다 — 옛 surrogate_key는 컬럼 값을 단순 연결(concat) 후 해시했는데, 이 방식은 NULL 값이 섞이거나 값 경계가 모호할 때(예: 'a'+'bc'와 'ab'+'c'가 같은 문자열이 되는 경우) 서로 다른 로우가 같은 키로 충돌할 수 있는 결함이 있었다. generate_surrogate_key는 컬럼 사이에 구분자를 넣고 NULL을 안전하게 처리해 이 문제를 해결한다. 최신 dbt-utils 버전에서는 surrogate_key가 deprecated이므로, 인터뷰에서는 dbt_utils.generate_surrogate_key로 답하는 것이 정확하다.

핵심 포인트

  • 매크로는 macros/ 아래 재사용 가능한 Jinja 함수이며, 3줄 미만을 절약하거나 한 곳에서만 쓰이면 오히려 과도한 추상화일 수 있다
  • run_query()는 컴파일 타임에 웨어하우스 SQL을 실행해 Agate 테이블을 반환하지만, 매 실행마다 지연·장애 위험을 추가하므로 아껴 써야 한다
  • 패키지는 packages.yml + dbt deps로 설치하며 대표적으로 dbt-utils, dbt-expectations, dbt-codegen이 있다
  • dbt-utils의 서로게이트 키 생성 매크로는 옛 이름 surrogate_key가 아니라 현재 generate_surrogate_key이며, NULL-안전 구분자 방식으로 개선되었다 — 정정