dbt (Data Build Tool)은(는) 무엇을 하나요?
dbt는 SQL을 사용하여 웨어하우스 내에서 데이터 변환을 관리합니다. v2.0은 Fusion Engine (Rust)을 도입하여 성능을 개선했습니다.
사용 시기
- 분석 엔지니어링 및 데이터 변환: 현대 클라우드 웨어하우스(Snowflake, BigQuery, Databricks, Redshift) 내에서 원시 데이터를 변환합니다.
- Jinja 및 버전 관리를 사용한 모듈식 SQL: DAG 의존성 및 매크로를 사용하여 재사용 가능한 SQL 모델을 구축합니다.
- 데이터 품질 및 스키마 테스트:
unique,not_null, 참조 무결성 및 사용자 정의 단일 테스트를 시행합니다. - 자동 데이터 문서화 및 계보: 코드베이스 스키마에서 직접 대화형 의존성 계보 그래프를 생성합니다.
빠른 시작
-- models/marts/fct_orders.sql
{{ config(materialized='table') }}
with orders as (
select * from {{ ref('stg_orders') }}
),
payments as (
select * from {{ ref('stg_payments') }}
)
select
orders.order_id,
orders.customer_id,
orders.order_date,
coalesce(payments.amount, 0) as total_amount
from orders
left join payments using (order_id)
핵심 개념
#ref() & source()를 사용한 선언적 SQL 모델링
의존성 해결을 통한 변환 모델 구축:
-- models/marts/core/fct_customer_orders.sql
{{
config(
materialized = 'incremental',
unique_key = 'order_id',
on_schema_change = 'fail'
)
}}
with orders as (
select * from {{ ref('stg_orders') }}
{% if is_incremental() %}
where order_date >= (select coalesce(max(order_date), '1970-01-01') from {{ this }})
{% endif %}
),
customers as (
select * from {{ ref('stg_customers') }}
)
select
o.order_id,
o.customer_id,
c.full_name as customer_name,
o.order_date,
o.total_amount_usd
from orders o
inner join customers c on o.customer_id = c.customer_id
#YAML에서 스키마 테스트 및 문서화
열 제약 조건 및 문서화 시행:
# models/marts/core/schema.yml
version: 2
models:
- name: fct_customer_orders
description: "일별 고객 주문 팩트 테이블로, 준비된 주문 및 고객 차원을 결합합니다."
columns:
- name: order_id
description: "주문의 기본 키입니다."
tests:
- unique
- not_null
- name: customer_id
description: "고객 테이블을 참조하는 외래 키입니다."
tests:
- not_null
- relationships:
to: ref('stg_customers')
field: customer_id
- name: total_amount_usd
description: "USD 기준 총 거래 금액입니다."
tests:
- not_null
#DRY 재사용 가능 로직을 위한 Jinja 매크로
사용자 정의 재사용 가능 SQL 유틸리티 생성:
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name, decimal_places=2) -%}
round(cast(({{ column_name }} / 100.0) as numeric), {{ decimal_places }})
{%- endmacro %}
-- 모델에서 사용:
select
id,
{{ cents_to_dollars('amount_in_cents') }} as amount_usd
from {{ ref('stg_payments') }}
일반적인 패턴
타임스탬프 필터링을 사용한 증분 모델
문제: dbt run이 실행될 때마다 수십억 행의 테이블을 처음부터 다시 빌드하는 것은 느리고 비용이 많이 듭니다.
해결책:
is_incremental()과 함께 incremental 구체화를 사용하세요:
{{ config(
materialized='incremental',
unique_key='event_id'
) }}
select * from {{ source('raw', 'events') }}
{% if is_incremental() %}
-- 대상 테이블에서 가장 최근 이벤트 이후에 생성된 행만 쿼리
where event_timestamp > (select max(event_timestamp) from {{ this }})
{% endif %}
모범 사례 (2026)
- 할 것: 프로젝트를
staging(원시 소스와 1:1 대응),intermediate,marts의 표준 레이어로 구성하세요. - 할 것: 항상
{{ ref('model_name') }}및{{ source('source_name', 'table_name') }}을 사용하여 DAG 계보를 유지하세요. - 할 것: 수백만 행 이상의 팩트 테이블에는 증분 모델(
materialized='incremental')을 구현하세요. - 할 것: 모든 풀 리퀘스트에서 데이터 스키마 회귀를 포착하기 위해 CI 파이프라인에서
dbt test를 실행하세요. - 하지 말 것: 원시 데이터베이스 테이블 참조(예:
analytics.raw.users)를 작성하지 말고 항상source()또는ref()를 사용하세요. - 하지 말 것: staging 모델 내에서 비즈니스 로직을 수행하지 마세요. staging은 열을 정리, 캐스팅 및 이름 변경만 해야 합니다.
- 하지 말 것: 환경(dev vs prod)을 하드코딩하지 말고 profiles.yml에서
target.name조건을 사용하세요.
문제 해결
| 오류 | 원인 | 해결책 |
|---|---|---|
Compilation Error: Model '...' depends on a node named '...' which was not found | {{ ref('model_name') }}의 오타 또는 모델 파일 누락. | 모델 파일 이름이 참조된 문자열과 정확히 일치하는지 확인하세요. |
dbt test failed: unique constraint violated | 고유하지 않은 조인 조건으로 인해 생성된 중복 키. | target/compiled/의 컴파일된 SQL에서 중복 키를 검사하세요. |
Schema change error during incremental run | 업스트림 모델이 기존 증분 테이블을 손상시키는 열을 추가/제거함. | dbt run --full-refresh를 한 번 실행하여 스키마를 다시 빌드하세요. |