Materialization

Materialization (구체화 방식)

Materialization은 dbt 모델을 웨어하우스에 어떻게 영속화할지를 정하는 전략이에요. dbt에 기본 내장된 방식은 다섯 가지로, table, view, incremental, ephemeral, materialized view가 있어요. 어떤 방식을 쓰든 모델이 실제 데이터베이스 객체로 어떻게 저장되고 언제 다시 빌드되는지가 달라지죠.

기본 설정은 **뷰(view)**예요. 프로젝트나 모델의 성격에 맞춰 이 방식을 바꾸면, 쿼리 성능과 데이터 신선도 사이에서 원하는 균형을 잡을 수 있어요. 필요하면 직접 커스텀 materialization을 만들어 dbt의 기능을 확장할 수도 있답니다.

출처: Materializations

Materialization 구성하기

기본적으로 dbt 모델은 view로 materialize돼요. 다른 방식을 쓰려면 materialized 설정을 지정하면 되죠. 프로젝트 파일(dbt_project.yml)에서 폴더 단위로, 모델 파일 안의 config() 블록에서, 또는 모델의 properties.yml에서 각각 설정할 수 있어요.

프로젝트 파일에서 설정하는 예시는 다음과 같아요.

# The following dbt_project.yml configures a project that looks like this:
# .
# └── models
#     ├── csvs
#     │   ├── employees.sql
#     │   └── goals.sql
#     └── events
#         ├── stg_event_log.sql
#         └── stg_event_sessions.sql

name: my_project
version: 1.0.0
config-version: 2

models:
  my_project:
    events:
      # materialize all models in models/events as tables
      +materialized: table
    csvs:
      # this is redundant, and does not need to be set
      +materialized: view

모델 파일 안에서 설정할 수도 있어요. 특정 모델에 성능 최적화 설정(예: Redshift·BigQuery 전용 설정)을 함께 적용할 때 유용하죠.


{{ config(materialized='table', sort='timestamp', dist='user_id') }}

select *
from ...

properties.yml 파일에서도 구성할 수 있어요.


models:
  - name: events
    config:
      materialized: table

Materialization 종류

View

view materialization은 실행할 때마다 create view as 문으로 모델을 뷰로 다시 만들게 해요.

  • 장점: 추가 데이터가 저장되지 않고, 소스 데이터 위의 뷰는 항상 최신 레코드를 보여줘요.
  • 단점: 큰 변환을 수행하는 뷰나 다른 뷰 위에 쌓인 뷰는 쿼리가 느릴 수 있어요.
  • 조언: 일반적으로 모델을 뷰로 시작하고, 성능 문제가 생길 때만 다른 방식으로 바꾸는 걸 추천해요. 뷰는 열 이름 변경이나 타입 변환처럼 가벼운 변환에 가장 적합하죠.

Table

table materialization은 실행할 때마다 create table as 문으로 모델을 테이블로 다시 만들어요.

  • 장점: 테이블은 쿼리가 빨라요.
  • 단점: 복잡한 변환이면 재빌드에 오래 걸리고, 소스 데이터의 새 레코드가 자동으로 추가되지 않아요.
  • 조언: BI 도구가 쿼리하는 모델이나, 느린 변환을 수행하면서 많은 하위 모델이 쓰는 모델은 테이블로 만드는 게 좋아요.

Incremental

incremental 모델은 지난 실행 이후의 레코드만 테이블에 insert하거나 update해요.

  • 장점: 새 레코드만 변환하므로 빌드 시간을 크게 줄일 수 있어요.
  • 단점: 추가 설정이 필요하고 dbt의 고급 사용법에 속해요.
  • 조언: 이벤트성 데이터에 가장 적합하고, dbt run이 너무 느려졌을 때 도입하는 게 좋아요. 처음부터 쓸 필요는 없답니다.

Ephemeral

ephemeral 모델은 데이터베이스에 직접 빌드되지 않아요. 대신 그 코드가 의존 모델 안에 공통 테이블 표현식(CTE)으로 끼워 넣어져요. CTE 식별자는 모델 별칭으로 제어할 수 있지만, dbt는 항상 모델 식별자 앞에 __dbt__cte__를 붙여요.

  • 장점: 재사용 가능한 로직을 쓸 수 있고, 웨어하우스를 깔끔하게 유지하는 데 도움돼요.
  • 단점: 이 모델에서 직접 조회할 수 없고, dbt run-operation 같은 연산은 ephemeral 노드를 ref()할 수 없어요. 과도하게 쓰면 쿼리 디버깅이 어려워지고, 모델 컨트랙트도 지원하지 않아요.
  • 조언: DAG 초반의 아주 가벼운 변환이면서 하위 모델 한두 개에서만 쓰이고 직접 조회할 필요가 없는 경우에 적합해요.

Materialized View

materialized_view materialization은 대상 데이터베이스에 materialized view를 만들고 유지해요. 뷰와 테이블의 결합체로, incremental 모델과 비슷한 용도로 쓰여요.

  • 장점: 테이블의 쿼리 성능과 뷰의 데이터 신선도를 함께 얻을 수 있어요. incremental과 비슷하게 동작하지만, 데이터베이스에 따라 수동 개입 없이 주기적으로 자동 갱신될 수 있어요.
  • 단점: 더 복잡한 데이터베이스 객체라 플랫폼별 구성 옵션이 적고, 모든 플랫폼이 지원하는 건 아니에요.
  • 조언: incremental 모델로 충분하지만 증분 로직과 갱신을 데이터 플랫폼이 관리하길 원할 때 고려해 보세요.

on_configuration_change 설정을 쓰면 객체를 완전히 재생성하는 대신 가능한 경우 직접 구성 변경을 시도해요. dbt run 관점에서 materialized view는 뷰와 비슷하게, 구성이나 SQL 변경이 있을 때만 실행하는 배포 행위에요. 대부분 플랫폼(Postgres 제외)은 데이터를 자동 갱신할 수 있어서, 매번 dbt를 돌리지 않아도 된다는 장점이 있어요.

참고: dbt-snowflake는 materialized view를 지원하지 않고 Dynamic Tables를 대신 사용해요.

Python materialization

Python 모델은 tableincremental 두 가지 materialization을 지원해요. viewephemeral로는 만들 수 없고, 테스트·스냅샷 같은 비모델 리소스에는 Python이 지원되지 않아요. incremental Python 모델은 SQL과 동일한 incremental 전략을 지원하며, 어댑터에 따라 지원 전략이 달라져요.

incremental 모델에서는 SQL 모델처럼 들어오는 테이블을 새 행으로만 필터링해야 해요.

import snowflake.snowpark.functions as F

def model(dbt, session):
    dbt.config(materialized = "incremental")
    df = dbt.ref("upstream_table")

    if dbt.is_incremental:

        # only new rows compared to max in current table
        max_from_this = f"select max(updated_at) from {dbt.this}"
        df = df.filter(df.updated_at >= session.sql(max_from_this).collect()[0][0])

        # or only rows from the past 3 days
        df = df.filter(df.updated_at >= F.dateadd("day", F.lit(-3), F.current_timestamp()))

    ...

    return df

참고: incremental 모델은 BigQuery/Dataproc에서 merge 전략을 지원하고, insert_overwrite 전략은 아직 지원하지 않아요.

더 알아보기 (Learn more)