dbt lint 명령어

dbt lint 명령어

dbt lint는 dbt v2에 내장된 빠른 SQL 린터예요. 로컬 또는 dbt platform에서 사용할 수 있고, SQLFluff와 호환돼서 .sqlfluff 설정을 읽고 같은 규칙 코드(CP01, RF03 등)와 -- noqa 억제 주석을 사용해요. 프로젝트 크기에 따라 SQLFluff보다 40~250배 이상 빠르게 동작해요.

출처: 문서

본문

dbt lint는 dbt v2에 내장된 빠른 SQL 린터로, 로컬이나 dbt platform에서 사용할 수 있어요. dbt lint는 v2 이상이 필요해요. 더 이전 버전이라면 업그레이드하거나 dbt를 설치하세요.

SQLFluff와 호환돼요: .sqlfluff config를 읽고, 같은 규칙 코드(예: CP01, RF03)를 사용하며, -- noqa 억제 주석을 존중해요. 호환된다는 건 동일하다는 뜻은 아니에요: 같은 파일·config에 대해 dbt lint와 SQLFluff가 다른 결과를 반환할 수 있어요. Rule parity with SQLFluff를 참고하세요.

기존 SQLFluff config를 최소 수정으로 사용할 수 있어요. dbt Labs는 앞으로 최신 SQLFluff 규칙 스펙을 추적할 계획이에요.

note dbt lint는 dbt v2의 일부이며 dbt platform CLI의 dbt sqlfluff lint와는 다르다는 점을 기억하세요. 플랫폼 CLI에서 SQLFluff는 Configure the dbt platform CLI를 참고하세요. Studio IDE의 린팅은 계속 SQLFluff를 사용해요.

벤치마크

1k10k 모델 프로젝트 크기에서 dbt lint는 모든 코어를 켠 SQLFluff보다 40250배, 단일 스레드 SQLFluff보다 280~1500배 빠르게 실행돼요.

dbt Labs는 12코어 Apple M4 Pro와 24GB RAM의 MacBook Pro에서 1k~10k 모델의 Snowflake 방언 dbt 프로젝트를 대상으로 SQLFluff 4.2.1과 비교해 이 벤치마크를 실행했어요.

사용법

dbt lint [FILE] [flags]

[FILE]은 선택이에요. [FILE]을 생략하면 dbt lint는 프로젝트의 모든 SQL 파일을 린트해요.

플래그

플래그 설명
--fix 자동 수정 가능한 규칙 위반에 수정을 자동 적용해요. 자동으로 수정할 수 없는 규칙은 Rules without autofix를 참고하세요.
--config <path> .sqlfluff config 파일 경로. 자동 발견(auto-discovery)을 override해요.
--rules 활성화할 규칙 코드의 쉼표 구분 목록. config를 override해요.
--exclude-rules 비활성화할 규칙 코드의 쉼표 구분 목록. config를 override해요.
--changed 현재 git 작업 트리에서 수정된 파일만 린트해요.
--format human|json|github-annotation 출력 형식. 기본값은 human. 머신 읽기 출력에는 json, GitHub Actions 통합에는 github-annotation을 사용하세요.
--jinja-render-mode <mode> 린트 전에 dbt lint가 Jinja를 렌더링하는 방식. symbolic(기본), rendered, turbo를 받아요. .sqlfluff config를 override해요. Jinja render modes를 참고하세요.

기본 규칙

대문자 규칙(접두사 DBT) 이름 설명
DBT01 dbt.import_ctes 모든 ref()/source()는 인라인으로 참조하지 말고 최상위 CTE를 통해 임포트해야 해요.
DBT02 dbt.join_condition_or JOINON 절에는 OR이 포함되지 않아야 해요.
DBT03 dbt.function_wrapped_filter_column 비교에서 함수 호출로 bare 컬럼 참조를 감싸면 안 돼요.
DBT04 dbt.leading_wildcard_like LIKE/ILIKE 패턴이 와일드카드로 시작하면 안 돼요.
DBT05 dbt.hard_coded_reference ref()/source()를 리터럴 문자열로 하드코딩하면 안 돼요.

Jinja 렌더 모드

dbt lint가 모델을 검사하려면 먼저 Jinja 템플릿 SQL을 일반 SQL로 바꿔야 해요. 대부분의 Jinja는 린트 시점에 깨끗하게 렌더링되지만, 일부 매크로는 테이블에 어떤 컬럼이 있는지처럼 데이터 플랫폼에 질문을 하며, dbt lint는 플랫폼에 절대 연결하지 않으므로 그 호출에는 실제 답이 없어요. jinja_render_mode 설정이 dbt lint가 이를 처리하는 방식을 제어하고, 그에 따라 보이는 위반 사항이 달라져요.

대부분의 프로젝트는 기본값 symbolic을 유지해야 해요. 세 가지 모드는:

모드 요약
symbolic (기본) Jinja를 정상적으로 렌더링하고, 플랫폼에서 얻을 수 없는 결과에는 플레이스홀더를 대체해요.
rendered 빈 스텁 값으로 Jinja를 렌더링하며, 값이 대체품이라는 신호는 없어요.
turbo 실행을 완전히 건너뛰고 리터럴 템플릿 텍스트를 린트해요.

Symbolic (기본)

Jinja를 실행하되, adapter.execute, adapter.get_relation, adapter.get_columns_in_relation 같은 내부(introspective) 어댑터 호출에서 온 값은 추적해요. 그 호출은 린트 시점에 웨어하우스에 닿을 수 없으므로 dbt lint는 오해를 부르는 빈 값 대신 플레이스홀더로 대체해요. 나머지는 정상적으로 렌더링돼요.

기본값을 유지하세요. 내부 매크로를 사용하는 프로젝트에서 가장 적은 오탐(false positive)을 만들어요.

예를 들어 내부 호출 결과를 반복하는 모델:

{% set cols = adapter.get_columns_in_relation(ref('orders')) %}
select {{ cols | map(attribute='name') | join(', ') }}
from {{ ref('orders') }}

는 린트 용도로 이렇게 렌더링돼요:

select your_columns
from orders

dbt lint는 플레이스홀더 값 자체에 대해 위반을 보고하지 않지만, 쿼리의 나머지 부분은 정상적으로 린트해요.

Rendered

파싱 시점의 스텁 값으로 Jinja를 실행해요. 내부 어댑터 호출은 값이 실제가 아니라는 신호 없이 빈 결과를 반환하므로, 같은 모델이 이렇게 렌더링돼요:

select
from orders

select 목록은 프로젝트가 절대 실행하지 않을 SQL을 만들 수 있고, dbt lint는 그 비현실적인 결과를 검사해요. 예기치 않은 위반을 조사할 때 renderedsymbolic과 비교해 보세요.

Turbo

Jinja를 절대 실행하지 않아요. 템플릿을 문법적으로 읽고, 작성한 리터럴 SQL을 유지하며, 내부가 아닌 것까지 포함해 모든 {{ ... }} 표현식을 플레이스홀더로 바꿔요:

select your_expression
from your_expression

렌더링이 너무 느리거나 모델에서 완전히 실패할 때 사용하세요. 가장 빠른 모드지만 매크로가 생성하는 것은 볼 수 없어요.

렌더 모드 설정

단일 실행에 --jinja-render-mode로 모드를 설정하세요. 이 플래그는 dbt lintdbt format 모두에서 동작해요:

dbt lint --jinja-render-mode rendered
dbt format --jinja-render-mode turbo

프로젝트 전체는 .sqlfluff 파일의 [dbt] 섹션에서 설정하세요:

[dbt]
jinja_render_mode = rendered

CLI 플래그가 config 파일보다 우선해요.

렌더 변형(variants)

symbolicturbo 모드에서 단일 모델이 하나 이상의 후보 SQL 출력을 만들 수 있어요. dbt lint가 해결할 수 없는 조건의 {% if %} 블록에 도달하면, 어느 분기를 의도했는지 추측하지 않고 하나 이상의 분기를 린트해요. 각 후보가 렌더 변형이고, dbt lint는 모두에서 찾은 위반을 보고해요.

render_variant_limitdbt lint가 모델당 생성하는 변형 수를 제한하며 기본값은 5예요. .sqlfluff 파일의 [sqlfluff] 섹션에서 설정하세요:

[sqlfluff]
render_variant_limit = 10

한도를 올리면 각 추가 변형이 템플릿의 또 다른 렌더이므로 린트 시간을 희생하고 커버리지가 넓어져요. 1로 낮추면 dbt lint를 모델당 단일 변형으로 제한해요.

파일·디렉터리 무시

아직 린트할 준비가 안 된 경로(예: dbt_packages/, models/legacy/)를 제외하려면 프로젝트 루트에 .sqlfluffignore 파일을 사용하세요.

.sqlfluffignore.gitignore 스타일 문법을 사용해요. 전체 패턴 참조는 SQLFluff .sqlfluffignore 문서를 참고하세요.

# .sqlfluffignore
dbt_packages/
models/legacy/
snapshots/

그 경로들을 린트할 준비가 되면 .sqlfluffignore에서 항목을 제거하세요.

Studio IDE Problems 탭에서 노이즈 줄이기

Studio IDE는 SQL을 자동으로 린트하고 Problems 탭에 위반을 표시해요. 처리할 준비가 안 된 스타일 경고가 많다면 모델 디렉터리를 .sqlfluffignore에 추가해 Problems 탭에서 즉시 해당 위반을 제거하세요. 위반을 정리하면서 무시 항목을 점진적으로 제거하세요.

위반 억제

dbt lint는 전체 SQLFluff 억제 문법을 지원해요:

억제 범위
-- noqa 해당 줄의 모든 위반 억제
-- noqa: CP01, RF03 이 줄에서 특정 규칙 억제
-- noqa-file 파일의 모든 위반 억제
-- sqlfluff:disable CP01 파일에서 규칙 비활성화

지원 방언

dbt lint에서 현재 지원되는 방언:

  • Snowflake
  • BigQuery
  • DuckDB
  • Redshift
  • Databricks
  • SparkSQL (현재 Databricks에 별칭)

추가 방언 지원이 곧 제공될 예정이에요.

dbt format

dbt format(dbt fmt로도 제공)은 .sqlfluff 파일의 레이아웃(LT*) 규칙에 따라 SQL 파일을 자동으로 포맷해요. dbt lint와 달리 진단을 내보내지 않아요. 명령어를 실행할 때 조용히 제자리에서 수정을 적용해요.

dbt format [FILE] [flags]
dbt fmt [FILE] [flags]

[FILE]은 선택이에요. 생략하면 dbt format은 프로젝트의 모든 SQL 파일을 포맷해요.

SQLFluff와의 규칙 동등성

dbt lint는 SQLFluff와의 높은 겹침을 목표로 하지만 규칙 대 규칙 동등성을 보장하지는 않으며, 작은 차이는 항상 존재해요. LT02 같은 레이아웃·들여쓰기 규칙이 알려진 차이 영역이에요.

Studio IDE의 린팅은 여전히 SQLFluff를 사용하므로, Studio IDE의 Lint file과 dbt lint가 같은 프로젝트 코드에 대해 다른 위반을 보고할 수 있어요. 마찬가지로 dbt v2 버전의 CI 작업은 SQLFluff 대신 dbt lint를 호출하므로 CI 작업 결과가 SQLFluff 결과와 다를 수 있어요.

SQLFluff 동작이 필요하다면, 계속 SQLFluff를 실행하는 Studio IDE에서 린트하거나, 독립형 dbt v1 엔진 템플레이터로 SQLFluff를 로컬에서 실행할 수 있어요. 자세한 내용은 dbt v2 limitations를 참고하세요.

제한 사항

다음 제한 사항을 염두에 두세요:

자동 수정이 없는 규칙

다음 규칙은 위반을 보고하지만 --fix로 자동 수정할 수 없어요. SQL 조각의 재정렬이나 더 광범위한 리플로우가 필요하며, macro_spans에 기반한 소스 매핑이 Jinja 템플릿 SQL 내부에서는 안전하게 수정할 수 없기 때문이에요:

  • Aliasing: AL03, AL04, AL06, AL08
  • References: RF01, RF02, RF04, RF05
  • Structure: ST03, ST04, ST05, ST06, ST07, ST09, ST10, ST11
  • Ambiguity / convention: AM01, AM06, CV08, CV09, CV12

단일 수정 패스

--fix는 단일 패스를 실행해요. 파일이 깨끗해질 때까지 반복하지 않아요. 한 규칙이 적용한 수정이 다음 실행에서 다른 규칙의 위반을 노출할 수 있어요. 예를 들어 AL09가 셀프 별칭을 제거하면, 이제 정규화되지 않은 참조를 RF02가 지적할 수 있어요. 출력이 깨끗해질 때까지 dbt lint --fix를 다시 실행하세요.

FAQ

dbt lint가 Jinja의 모든 가능한 출력을 검사하지 않는 이유는 무엇인가요?

dbt lint는 모델당 제한된 렌더 변형 집합을 린트해요. 매크로가 입력의 모든 조합에서 만들 수 있는 모든 SQL 출력을 린트하지 않아요. 이는 의도적인 선택이에요. 가능한 출력 수는 템플릿의 해결되지 않은 조건 수에 따라 조합적으로 늘어나므로, 전부 린트하는 것은 비용이 크고 프로젝트가 절대 실행하지 않을 SQL에서 위반을 표면화해요. 대신 dbt lint는 한 번에 하나의 해결되지 않은 조건을 최대 render_variant_limit까지 변형해요. 한 가지 결과를 기억하세요: dbt lint가 프로젝트가 절대 취하지 않는 분기를 탐색할 때, 그 사용되지 않는 경로(대부분 서드파티 패키지 매크로 내부)의 위반을 표면화할 수 있어요. 이 접근에 대한 피드백이 있으면 dbt-core GitHub 저장소에 Linter 라벨로 이슈를 열어주세요.

dbt lint가 일부 매크로의 위반을 보고하지 않는 이유는 무엇인가요?

dbt lint는 모든 매크로가 만드는 SQL을 린트하지만, 웨어하우스 조회에 의존하는 매크로는 해결할 수 없어요. adapter.execute, adapter.get_columns_in_relation 같은 내부 어댑터 호출은 린트 시점에 실제 결과가 없어요. dbt lint가 이를 처리하는 방식은 렌더 모드에 달려 있어요. 기본 symbolic 모드에서 dbt lint는 내부 결과가 나타날 곳마다 플레이스홀더를 대체하고 그 플레이스홀더에 대해서는 위반을 보고하지 않아요. 가짜 출력을 지적하는 것은 신호보다 노이즈를 만들기 때문이에요. 매크로의 나머지는 정상적으로 린트돼요. 이 동작은 SQLFluff의 ignore_templated_areas 설정과 유사해요.

피드백

예기치 않은 동작을 발견하거나 제안이 있다면 dbt-labs/dbt GitHub 저장소에 이슈를 열고 Linter 라벨을 적용하세요.

더 알아보기 (Learn more)