증분 모델의 함정 — unique_key, full-refresh, 스키마 변경
50 dbt Interview Questions for Data Engineers (Real Answers, 2026) — 미상 (데이터 엔지니어링 커리어 블로그, 2026) Section 3: Incremental Models & Performance, Q22-Q25 (pp.7-8)
이 모듈을 다 읽으면
- unique_key를 빠뜨렸을 때 발생하는 중복 문제와 예방법을 설명할 수 있다
- --full-refresh가 필요한 상황과 대용량 테이블에서의 비용 리스크를 판단할 수 있다
- insert_overwrite가 BigQuery에서 merge보다 저렴한 이유와 on_schema_change 옵션별 동작을 설명할 수 있다
merge 전략에서 unique_key를 빠뜨리면 증분 SELECT의 모든 행이 그대로 INSERT되어 실행할 때마다 중복이 누적된다. --full-refresh는 스키마 변경이나 증분 로직 변경 시 테이블을 처음부터 다시 만들지만 대형 테이블에서는 비용이 크다. insert_overwrite는 전체 테이블을 스캔하는 merge와 달리 영향받은 파티션만 건드려 BigQuery 같은 스캔-과금 웨어하우스에서 비용을 크게 줄이며, on_schema_change는 소스에 새 컬럼이 추가됐을 때 dbt가 취할 행동을 제어한다.
unique_key 누락과 --full-refresh
merge 증분 모델에서 unique_key를 설정하지 않으면 dbt는 증분 SELECT의 모든 행을 이미 존재하는 행인지 여부와 상관없이 그대로 INSERT한다. 그 결과 실행할 때마다 중복이 누적되며, 이는 dbt를 쓰면서 가장 흔하게 발생하는 프로덕션 버그 중 하나다. unique_key는 항상 같은 컬럼에 대한 unique 테스트와 함께 짝지어 중복을 조기에 잡아내는 것이 좋은 습관이다.
--full-refresh는 증분 테이블을 드롭하고 처음부터 다시 만든다. 모델의 스키마를 바꾸거나(컬럼 추가/제거), 증분 로직을 바꾸거나, 누적된 지연 도착 레코드로 인한 데이터 품질 문제가 의심될 때 사용한다. 리스크는 비용이다 — 5억 행짜리 팩트 테이블에서 전체 재구축은 몇 시간이 걸리고 웨어하우스 비용을 급증시킬 수 있다. 프로덕션에서는 트래픽이 적은 시간대나 주말에 전체 재구축을 스케줄링하는 것이 좋다.
핵심 포인트
- merge 전략에서 unique_key를 빠뜨리면 모든 증분 행이 무조건 INSERT돼 중복이 매 실행마다 누적된다
- unique_key 컬럼에는 항상 unique 테스트를 함께 걸어 중복을 조기에 발견해야 한다
- --full-refresh는 스키마·증분 로직 변경 시 테이블을 처음부터 재구축하며, 대형 테이블에서는 비용·시간이 크므로 오프피크에 스케줄링한다
insert_overwrite vs merge, on_schema_change
insert_overwrite는 행 단위로 병합하는 대신 전체 파티션을 교체한다. BigQuery에서는 바이트 스캔량 기준으로 과금하기 때문에 이 차이가 극적으로 저렴해진다 — MERGE는 대상 테이블 전체를 스캔하지만 insert_overwrite는 영향받은 파티션만 건드린다. 매일 파티션 단위로 처리하는 모델을 BigQuery에서 운영한다면 insert_overwrite를 기본값으로 삼아야 하며, 대형 테이블에서는 이 전환 하나로 웨어하우스 비용을 60~80% 절감할 수 있다.
on_schema_change는 소스에 새 컬럼이 추가됐을 때 증분 모델의 기존 테이블에는 그 컬럼이 없는 상황을 어떻게 처리할지 제어한다. ignore(기본값)는 새 컬럼을 증분 INSERT/MERGE 문에서 제외해 반영하지 않고, fail은 에러를 발생시키며, append_new_columns는 새 컬럼을 추가하고 기존 행은 NULL로 채우며, sync_all_columns는 새 컬럼 추가와 제거된 컬럼 삭제를 모두 수행한다. 대부분 팀은 append_new_columns를 쓰는데, ignore는 스키마 드리프트를 조용히 숨기는 데이터 버그로 이어지기 때문이다.
— 정정: 원문은 ignore가 새 컬럼을 '조용히 드롭한다(silently drops)'고 표현하는데, 이는 오해의 소지가 있다. 정확히는 새 컬럼이 삭제되는 것이 아니라, 증분 실행의 INSERT/MERGE 문 컬럼 목록에서 그냥 제외되어 반영되지 않는 것이다(원천 테이블 자체는 그대로 있다). --full-refresh를 실행하면 그 시점의 소스 스키마 전체를 다시 읽으므로 새 컬럼도 정상적으로 포함된다. '드롭'이 아니라 '증분 갱신에서 누락'이라고 이해하는 것이 더 정확하다.
핵심 포인트
- BigQuery는 스캔 바이트 기준 과금이므로, 전체 테이블을 스캔하는 merge보다 파티션만 건드리는 insert_overwrite가 훨씬 저렴하다
- 파티션 단위로 처리하는 BigQuery 모델은 insert_overwrite를 기본값으로 삼는 것이 좋고, 대형 테이블에서 비용을 60~80% 절감할 수 있다
- on_schema_change: ignore(기본, 새 컬럼 미반영)/fail(에러)/append_new_columns(NULL로 채워 추가)/sync_all_columns(추가+삭제 모두 반영)
- ignore의 '새 컬럼을 드롭한다'는 표현은 부정확하며, 실제로는 증분 INSERT/MERGE 문에서 그 컬럼이 제외되는 것뿐이다 — 정정
- 대부분 팀은 스키마 드리프트를 숨기지 않기 위해 append_new_columns를 기본으로 쓴다