Dasis AI Blog
← 목록으로

Databricks 위에서 dbt를 쓸 때, 아키텍처 결정이 성패를 가른다

· DASIS Team · #dbt #Databricks #데이터파이프라인

Databricks 환경에서 dbt 도입을 검토 중이라면, "도입할지 말지"보다 "어떻게 구성할지"가 더 중요한 질문이다. dbt를 얹는 방식에 따라 데이터 신뢰도와 운영 비용이 달라진다. dbt Labs 솔루션 아키텍트가 실전에서 반복적으로 목격한 실수와 그 대응 방법을 정리했다.

dbt가 Databricks에 필요한 이유

Databricks는 데이터를 저장하고 처리하는 데 강하다. 하지만 "이 테이블이 어디서 왔고, 누가 언제 바꿨으며, 지금 믿어도 되는가"를 추적하는 기능은 기본 제공하지 않는다. dbt는 바로 그 빈자리를 채운다.

dbt(Data Build Tool)는 SQL 기반의 데이터 변환 레이어다. 원시 데이터를 분석 가능한 형태로 바꾸는 SQL 로직을 코드로 관리하고, 테스트하고, 문서화한다. Git 기반 버전 관리, 자동 데이터 리니지(lineage) 추적, 빌드 단계별 테스트가 핵심이다.

Databricks 위에서 dbt 없이 운영하는 조직은 처음에는 멀쩡해 보인다. 문제는 조용히 쌓인다. 같은 지표를 두 팀이 다르게 계산하거나, 누군가 노트북에서 테이블을 덮어쓴 시점부터 리포트 숫자가 어긋나기 시작한다. 이 복잡도는 팀이 커질수록 기하급수적으로 늘어난다.

핵심 아키텍처 결정 세 가지

1. dbt Core vs. dbt Cloud 선택

dbt는 두 가지 실행 방식이 있다. dbt Core는 오픈소스로, CLI로 직접 실행한다. dbt Cloud는 관리형 SaaS로, 스케줄링·모니터링·협업 기능을 포함한다.

Databricks와 통합할 때 dbt Cloud를 선택하면 Databricks Jobs와의 네이티브 연동이 가능하다. dbt Core를 쓴다면 Databricks Workflows나 Airflow 같은 오케스트레이터를 직접 연결해야 한다. 팀 규모가 작고 DevOps 역량이 충분하다면 dbt Core로 시작해도 된다. 하지만 여러 팀이 같은 파이프라인을 건드리는 구조라면 dbt Cloud의 협업 기능이 운영 비용을 낮춘다.

2. 실행 모델 — Python 모델 vs. SQL 모델

dbt는 전통적으로 SQL을 변환 언어로 사용한다. Databricks 환경에서는 Python 모델도 지원한다. 두 가지를 섞을 수 있지만, 원칙 없이 섞으면 관리 부담이 커진다.

원문이 권장하는 기준은 단순하다. 변환 로직이 SQL로 표현 가능하면 SQL 모델을 쓴다. ML 전처리, 복잡한 배열 연산, Spark API가 필요한 경우에만 Python 모델을 사용한다. Python 모델은 테스트·문서화 지원이 SQL 모델보다 얕기 때문에, 무분별하게 쓰면 dbt가 주는 신뢰성 보장이 희석된다.

3. Unity Catalog와의 통합

Databricks의 메타데이터 관리 레이어인 Unity Catalog를 쓰고 있다면, dbt 모델의 네임스페이스를 Unity Catalog의 3단계 구조(catalog.schema.table)에 맞춰 설계해야 한다.

# dbt_project.yml 예시
models:
  my_project:
    staging:
      +schema: staging
      +catalog: dev_catalog
    marts:
      +schema: marts
      +catalog: prod_catalog

이 설정은 개발 환경과 운영 환경을 카탈로그 단위로 분리하고, Unity Catalog의 접근 제어(ACL)를 dbt 빌드 단계에 그대로 적용할 수 있게 한다. 카탈로그 분리를 처음부터 하지 않으면, 나중에 수십 개 모델의 참조 경로를 일괄 수정해야 하는 상황이 생긴다.

자주 놓치는 트레이드오프

dbt의 증분(incremental) 모델은 Databricks에서 자주 쓰인다. 전체 테이블을 매번 재구성하지 않고 새로 들어온 데이터만 처리해 비용을 줄이는 방식이다. 그런데 증분 모델은 초기 설계가 잘못되면 중복 데이터가 조용히 쌓인다.

Databricks의 Delta Lake는 MERGE 연산을 지원하고, dbt의 증분 모델 전략에서 merge를 선택하면 이를 활용한다. 하지만 unique_key를 잘못 지정하거나 파티션 구조가 맞지 않으면 MERGE가 전체 테이블 스캔으로 동작해 오히려 비용이 더 나올 수 있다.

-- dbt 증분 모델 예시 (Delta MERGE 사용)
{{ config(
    materialized='incremental',
    unique_key='event_id',
    incremental_strategy='merge'
) }}

SELECT
    event_id,
    user_id,
    event_type,
    created_at
FROM {{ ref('stg_events') }}
{% if is_incremental() %}
    WHERE created_at > (SELECT MAX(created_at) FROM {{ this }})
{% endif %}

증분 모델을 쓸 때는 unique_key가 실제로 고유함을 보장하는 dbt 테스트를 함께 걸어야 한다. 테스트 없는 증분 모델은 시간이 지날수록 데이터 품질이 저하되는 구조를 만든다.

반대로, 증분 전략이 항상 답은 아니다. 원본 데이터가 자주 수정되거나 히스토리 재적재가 필요한 경우에는 table materialization으로 전체 재구성하는 게 더 단순하고 안전하다.

실전에서 바로 쓰는 설정 포인트

Databricks 연결 프로파일은 HTTP 경로와 클러스터 타입 선택이 핵심이다.

# profiles.yml
my_databricks_profile:
  target: dev
  outputs:
    dev:
      type: databricks
      host: <workspace-host>
      http_path: /sql/1.0/warehouses/<warehouse-id>
      token: "{{ env_var('DBT_DATABRICKS_TOKEN') }}"
      catalog: dev_catalog
      schema: dbt_dev
      threads: 4

http_path에 SQL Warehouse를 지정하는 게 일반적이다. All-Purpose 클러스터를 연결할 수도 있지만, 비용 구조가 달라진다. SQL Warehouse는 쿼리 실행에 최적화되어 있고 자동 일시 중지를 지원하므로 dbt 변환 작업에는 SQL Warehouse가 적합하다.

토큰은 반드시 환경변수로 분리한다. profiles.yml에 직접 하드코딩된 토큰이 Git에 올라가는 사고는 실제로 자주 발생한다.

정리

Databricks 위에서 dbt를 쓰는 것은 "도입하면 자동으로 좋아지는" 종류의 기술이 아니다. SQL 모델과 Python 모델을 구분하는 기준, Unity Catalog 네임스페이스 설계, 증분 모델의 unique_key 검증, SQL Warehouse 연결 — 이 네 가지 결정이 잘못되면 파이프라인이 커질수록 운영 부담이 커진다. 반대로 처음부터 이 구조를 잡으면, 데이터 신뢰도와 팀 생산성이 함께 올라간다.

더 읽을 자료