Post

[Airflow 101] Day 5: 과거를 지배하는 자, Backfill & Best Practices

[Airflow 101] Day 5: 과거를 지배하는 자, Backfill & Best Practices

들어가며: Airflow의 시간은 거꾸로 간다?

Airflow를 처음 접하는 분들이 가장 헷갈려 하는 것이 바로 ‘실행 기준 날짜(Logical Date)’입니다.

“매일 자정(00:00)에 도는 스케줄을 만들었어요. 지금이 1월 9일 00:00인데, 왜 로그에는 2026-01-08이라고 찍히죠?”

버그가 아닙니다. Airflow는 기본적으로 “데이터의 기간(Interval)이 끝나는 시점”에 실행되기 때문입니다. 1월 8일 하루치(00:00 ~ 23:59) 데이터를 처리하려면, 1월 8일이 다 지나간 1월 9일 00:00가 되어야 실행할 수 있겠죠? 그래서 실제 실행 시간은 9일이지만, 이 태스크가 처리해야 할 주인공(Logical Date)은 8일인 것입니다.

이 개념을 이해해야 오늘의 핵심 주제인 Backfill을 정복할 수 있습니다.


1. Backfill: 타임머신을 타고 과거 데이터 채우기

여러분이 오늘(1월 9일) 멋진 매출 집계 DAG를 완성해서 배포했습니다. 내일부터는 잘 돌겠죠. 하지만 팀장님이 묻습니다. “작년 1년 치 매출 데이터도 이 로직으로 다시 뽑아줄 수 있어?”

이때 사용하는 강력한 기능이 Backfill(백필)입니다.

(1) catchup=True의 함정

DAG 설정 중 catchup=True (기본값)로 두면, DAG를 켜는 순간 start_date부터 현재까지 밀린 작업 수백 개를 한꺼번에 실행하려고 듭니다. 서버가 터질 수 있으니, 기본적으로는 False로 두고 필요한 구간만 명령어로 실행하는 것이 안전합니다.

(2) CLI로 우아하게 Backfill 하기

Docker 컨테이너 내부로 들어가서 아래 명령어를 입력하면, 특정 기간의 데이터를 순차적으로 재처리할 수 있습니다.

1
2
3
4
5
6
7
# 1. 스케줄러 컨테이너 접속
docker exec -it <컨테이너_이름> bash

# 2. Backfill 명령어 실행
# 형식: airflow dags backfill -s <시작날짜> -e <종료날짜> <DAG_ID>
airflow dags backfill -s 2025-01-01 -e 2025-12-31 hello_airflow_v1

이 명령어를 치면 Airflow는 2025년 1월 1일부터 12월 31일까지의 날짜를 `` 변수에 차례대로 주입하며 태스크를 실행합니다. 코드를 수정할 필요 없이, 날짜만 바꿔가며 과거 데이터를 완벽하게 채워주는 것이죠.


2. 반드시 지켜야 할 Best Practices 3계명

Airflow를 운영하다 보면 서버가 이유 없이 느려지거나 멈출 때가 있습니다. 90%는 아래 3가지를 지키지 않아서입니다.

(1) Top-Level Code를 피하라 (가장 중요!)

Airflow 스케줄러는 새로운 DAG가 있는지 확인하기 위해 파이썬 파일들을 30초마다 한 번씩 파싱(실행)합니다.

나쁜 예:

1
2
3
4
5
6
7
# DAG 밖(Top-Level)에서 DB 연결이나 무거운 연산을 함
import requests
res = requests.get("http://heavy-api.com") # 매 30초마다 요청을 보냄 (서버 부하 유발)

with DAG(...) as dag:
    ...

좋은 예:

1
2
3
4
5
6
7
8
with DAG(...) as dag:
    def my_task():
        # 태스크(함수) 안에서 실행해야, 실제 작업이 돌 때만 1번 실행됨
        import requests
        res = requests.get("http://heavy-api.com")
    
    PythonOperator(..., python_callable=my_task)

Top-Level에는 import 문과 DAG 정의 등 최소한의 코드만 있어야 합니다.

(2) 멱등성(Idempotency)을 유지하라

Day 3에서도 강조했지만, 태스크는 몇 번을 재실행하든 결과가 같아야 합니다. INSERT 대신 DELETEINSERT (Overwrite) 방식을 사용하거나, UPSERT를 사용하세요. 재시도가 두려워서는 안 됩니다.

(3) 무거운 작업은 Airflow 밖에서

Airflow는 ‘지휘자’이지 ‘연주자’가 아닙니다. 수 기가바이트의 데이터를 Pandas로 Airflow 워커 메모리에서 직접 가공하면 안 됩니다.

  • Airflow: “Spark야, 이 데이터 좀 처리해줘.” (지시)
  • Spark/BigQuery/Snowflake: 실제 데이터 처리 (수행)
  • Airflow: “다 했니? 그럼 다음 단계 진행할게.” (확인)

3. 마무리: 튜토리얼 종료 및 리소스 정리

5일간의 긴 여정이 끝났습니다. 이제 실습을 위해 띄워둔 컨테이너들을 깔끔하게 정리해 봅시다.

1
2
3
4
5
6
# 컨테이너 종료 및 삭제
docker compose down

# (선택) 볼륨(DB 데이터)까지 깨끗하게 지우고 싶다면
docker compose down -v

This post is licensed under CC BY 4.0 by the author.