이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Airflow에서 DAG, task, operator는 각각 무엇을 표현하는가?
- task boundary를 잘못 잡으면 retry, backfill, 장애 복구가 왜 어려워지는가?
- operator 선택은 업무 로직 위치와 운영 책임을 어떻게 드러내는가?
- Java/Spring batch를 Airflow에서 호출할 때 task와 parameter는 어떻게 잡아야 하는가?
개요
DAG는 Directed Acyclic Graph다.
Airflow에서는 작업 의존성을 표현하는 그래프다.
task는 DAG 안의 실행 단위다.
operator는 task가 실제로 무엇을 실행하는지 정하는 방식이다.
이 셋을 구분해야 Airflow DAG를 운영 가능한 단위로 설계할 수 있다.
DAG
DAG는 workflow의 구조다.
extract_orders -> validate_orders -> build_order_mart -> publish_reportDAG에는 schedule, start date, catchup, owner, default retry 같은 운영 설정이 붙는다.
DAG는 business 계산을 직접 담는 곳이 아니다.
어떤 작업이 어떤 작업을 기다리는지, 어떤 날짜 partition을 대상으로 하는지를 표현한다.
Task
task는 상태를 갖는 실행 단위다.
task는 success, failed, skipped, retry 같은 상태를 가진다.
task boundary는 재시도와 재실행 범위가 된다.
너무 큰 task는 실패 시 어디서부터 다시 해야 하는지 알기 어렵다.
너무 작은 task는 dependency가 복잡해지고 overhead가 커진다.
좋은 task는 입력과 출력, 재시도 가능성, 검증 기준이 명확하다.
Operator
operator는 task 실행 방식이다.
예시는 다음과 같다.
- Python task: Python 함수 실행
- Bash operator: command 실행
- HTTP operator: API 호출
- Kubernetes pod operator: container 실행
- External task sensor: 다른 DAG/task 대기
백엔드 batch를 실행한다면 Bash나 Kubernetes operator로 jar 또는 container를 실행할 수 있다.
중요한 것은 operator가 아니라 task가 재시도 가능하고 멱등한가다.
Task Boundary
다음은 boundary가 흐린 예다.
task: do_everything
extract
transform
load
validate
send_report이 task가 실패하면 어떤 단계가 실패했는지 DAG만으로 알 수 없다.
다음처럼 나누면 복구가 쉽다.
extract -> transform -> load -> validate -> publishvalidation 실패와 publish 실패도 분리된다.
Parameter
Airflow task는 execution date 또는 logical date를 parameter로 넘겨야 한다.
java -jar batch.jar build-order-mart --target-date {{ ds }}target date가 없으면 retry나 backfill 때 같은 범위를 다시 처리하기 어렵다.
now() 기준으로 처리하면 과거 DAG run을 실행해도 현재 날짜 데이터를 건드릴 수 있다.
task는 logical date를 기준으로 deterministic하게 동작해야 한다.
XCom 주의
Airflow XCom은 task 간 작은 metadata 전달에 적합하다.
대량 데이터를 XCom으로 넘기면 안 된다.
대량 데이터는 S3, DB table, warehouse partition 같은 외부 저장소에 두고 path나 partition key만 넘긴다.
extract task output: s3://raw/orders/dt=2026-06-30/이 값은 downstream task의 입력 계약이 된다.
실패 모델
- DAG 하나에 서로 관련 없는 workflow를 모두 넣는다.
- task 하나가 extract부터 report까지 모두 수행한다.
- operator 선택만 문서화하고 task input/output은 없다.
- logical date 대신 현재 시각으로 대상 데이터를 고른다.
- XCom에 대량 데이터를 넣는다.
이 문제들은 backfill과 retry 때 크게 드러난다.
운영 지표
- task duration: task boundary가 적절한지 본다.
- task retry count: 실패가 잦은 task를 찾는다.
- skipped task count: dependency 조건을 확인한다.
- XCom size or count: 잘못된 데이터 전달을 찾는다.
- backfill task failure count: 과거 실행 안정성을 본다.
task별 지표가 있어야 DAG를 쪼갤지 합칠지 판단할 수 있다.
개인 프로젝트 기준
개인 프로젝트에서는 DAG 하나에 task 4개면 충분하다.
extract, transform, validate, publish로 나눈다.
각 task가 target date를 parameter로 받게 한다.
한 task를 실패시켜 retry와 downstream skip을 확인한다.
이 정도면 DAG/task/operator 개념을 운영 관점으로 보여줄 수 있다.
위험 신호!
- DAG가 Python script 하나처럼 보인다.
- task input과 output이 문서화되어 있지 않다.
datetime.now()로 처리 날짜를 정한다.- XCom에 큰 payload를 담는다.
- operator 종류만 설명하고 retry 가능성을 설명하지 않는다.
확인 질문
확인 질문
- 이 DAG의 각 task는 어떤 입력과 출력을 가지는가?
- path, partition, date, table 같은 계약으로 답해야 한다.
- task가 실패하면 어디부터 재실행하는가?
- task boundary가 재실행 범위가 된다.
- backfill로 과거 날짜를 돌려도 현재 데이터를 건드리지 않는가?
- logical date parameter를 사용해야 한다.