dbt sql modeling

데이터 엔지니어링에서 dbt (Data Build Tool)는 데이터 변환과 모델링을 자동화하는 강력한 도구입니다. 특히 SQL 기반의 데이터 모델링을 체계적으로 관리하고, 변환 파이프라인을 효율적으로 구축할 수 있도록 돕습니다. 이번 포스트에서는 sql 모델, 검증을 구현하는 예시와 데이터 처리 방식과 연관된 몇가지 설정을 소개합니다.

필요한 파일

1. YAML 파일 (.yml)

  • 모델 설정: 모델의 메타데이터 및 설정을 정의합니다.
  • 테스트: 데이터 무결성을 검증하는 테스트를 추가할 수 있습니다.

2. SQL 파일 (.sql)

  • 최종 SELECT 절을 반드시 포함해야 합니다.
  • models 하위 디렉토리에 테이블명과 동일한 SQL 파일을 생성하여 관리합니다.

모델 명세

모델 파일({model}.sql) 상단에서 config() 함수를 사용하여 설정을 적용할 수 있습니다. 주요 설정 옵션은 다음과 같습니다.

  • materialized: 테이블 생성 방식 결정
  • schema: 모델이 배포될 데이터베이스 스키마 지정
  • tags: 특정 모델을 그룹화하여 필터링할 때 사용
  • enabled: 모델을 실행할지 여부 결정 (true 또는 false)
  • persist_docs: 모델의 설명을 데이터베이스에 저장할지 여부
  • alias: 데이터베이스에 저장될 모델의 실제 테이블/뷰 명
{{ config(
    materialized='incremental',
    schema='my_db',
    alias='log_table',
    tags=['my_tag'],
    enabled=true
) }}

위 내용중 materilaized 으로 설정할 값은 다음 내용을 고려해야합니다.

Materializations

Materialization은 dbt가 모델을 어떻게 실행하고 저장할지 결정하는 방식입니다. 

  • table
    실행 시 물리적인 테이블을 생성합니다.
    dbt run을 실행할 떄마다 기존 테이블이 삭제되고 새로운 테이블로 생성됩니다.
    사용 케이스: 정적 데이터(변경되지 않는 데이터) or 비교적 적은 양의 데이터를 저장할때 적합합니다.
  • view
    실행 시 뷰(View)를 생성합니다.
    물리적인 데이터 저장 없이 SQL 쿼리를 실행할 때마다 최신 데이터를 가져옵니다.
    사용 케이스: 자주 변경되는 중간 테이블을 뷰로 처리하여 조회한다면 데이터 저장 비용을 절감할 수 있습니다.
    주의점: 쿼리 실행 시마다 데이터를 다시 불러오기 때문에, 성능 및 비용이 중요한 환경에서는 주의해야 합니다.
  • incremental
    새로운 데이터만 추가(insert)하거나 업데이트(upsert)하는 방식으로 동작합니다.
    기존 데이터를 유지하면서 새로운 데이터를 적재하기 때문에 대용량 처리시 주로 이 설정으로 테이블을 저장합니다.
    사용 케이스: 대량의 데이터를 처리해야하며 모든 데이터를 매번 다시 생성하는 것이 불필요한 경우.
    어떤 방식으로 새로운 데이터를 넣을건지는 데이터베이스에 지원되는 strategy로 설정할 수 있습니다. 각 웨어하우스 데이터베이스마다 기본값이 다를 수 있습니다(dbt-spark default:append / snowflake default:merge ..)
  • ephemeral
    물리적인 테이블이나 뷰를 생성하지 않고, SQL 코드가 다른 모델 내에서 서브쿼리로 직접 삽입됩니다.
    실행 성능을 최적화하고 불필요한 중간 테이블 생성을 방지할 수 있습니다.
    주의점: 데이터가 많을 경우 성능 저하가 발생할 수 있으며, 복잡한 쿼리를 반복적으로 사용할 경우 table이나 view를 사용하는 것이 더 적절할 수 있습니다.

각 타이별 어떤 데이터 처리에 장단점을 보이는지 정리된 표를 참고해볼 수 있습니다.
https://docs.getdbt.com/best-practices/materializations/2-available-materializations#comparing-the-materialization-types

Configuration Precedence (config 값 적용 우선순위)

  1. 모델 SQL 파일 내부 ({model}.sql)에서 config() 선언
  2. 모델의 YAML 설정 파일 (models/{model}.yml) 내부의 config
  3. 프로젝트 설정 파일 (dbt_project.yml)models: 하위의 config

가장 세부적인 위치에서 선언된 설정값이 최종적으로 적용됩니다.
즉, SQL 파일에 설정이 포함되어 있다면 해당 설정이 적용되며, 설정이 없다면 .yml 또는 dbt_project.yml에서 설정된 값을 따릅니다.
dbt_project.yml은 여러 모델에 대한 공통 설정을 정의하는 데 사용되므로, 개별 모델에 대한 설정은 SQL 파일 내에서 직접 선언하는 것이 적절합니다.

모델 구성

  • 모델은 반드시 최종 SELECT 절을 포함하는 단일 파일로 구성됩니다.
  • models 디렉토리 내에서 관리되며, 모델명과 동일한 SQL 파일로 저장됩니다.
{{ config 블럭 }}

SELECT id, name, cost
FROM ref('my_source', 'item')
WHERE dt=current_date

데이터 리니지 (Lineage)

  • ref()source() 함수를 사용하여 해당 테이블이 어떤 테이블로부터 생성되는지 명시할 수 있습니다.
  • 위 함수들은 Relation 객체를 반환합니다.

테스트 추가

  • dbt는 모델에 대한 데이터 테스트 기능을 제공합니다.
  • unique, not_null, accepted_values, relationships 등의 테스트 유형을 사용할 수 있습니다.
  • .yml 파일 내 tests: 섹션을 활용하여 테스트를 정의할 수 있습니다.

이 문서를 통해 dbt 모델링을 보다 체계적으로 정리하고, 필요할 때 참고할 수 있도록 합니다.

version: 2

models:
  - name: my_model
    description: "This is a sample dbt model"
    columns:
      - name: id
        description: "Primary key for the model"
        tests:
          - unique
          - not_null
          - relationships:
              to: ref('items')
              field: id
      - name: name
        description: "User name"
      - name: cost
        description: "Purchase cost"
        tests:
          - accepted_values:
              values: [10, 20, 30, 40]

위 내용으로 가공된 데이터의 id 컬럼에 대해서 unique, not_null 검사와 참조한 소스 테이블에서의 id 컬럼값이 모두 존재하는지까지 검증할 수 있습니다.

 


이번 글에서는 dbt 모델링의 개념과 핵심 기능을 간략히 살펴보며, 실무에서 활용할 수 있는 기본적인 설정과 테스트 방법을 정리했습니다. 데이터 처리, 적재 방식에 대한 materialized 설정 이외에도 테이블 변경에 대한 처리 방식이나 고급 매크로 내용을 탐구해 볼 수 있습니다. 다양한 웨어하우스와 오케스트레이션 환경에서 dbt를 어떻게 최적화할 수 있는지 연구해보는 것도 좋습니다.