Airflow 3 실전 연재 0편: 왜, 그리고 언제 쓰나
워크플로 오케스트레이션이 푸는 문제부터 Airflow 3의 핵심 변화, 그리고 12편짜리 실전 연재 로드맵까지.
이 글은 "Airflow 3 실전 연재"의 0편입니다. 본격적인 아키텍처 해부나 클러스터 구성에 들어가기 전에, 한 발 물러서서 "그래서 왜 Airflow인가, 그리고 언제 쓰는 게 맞는가"를 먼저 정리합니다. 처음 워크플로 오케스트레이션을 접하는 분도 끝까지 따라올 수 있도록 천천히 풀어 쓰겠습니다. 다음 편부터는 1편: 아키텍처 해부에서 내부 구조를 파헤칩니다.
cron과 스크립트로 버티던 시절의 한계
데이터 파이프라인이 두세 개일 때는 cron 한 줄이면 충분합니다. "매일 새벽 3시에 추출 스크립트 실행, 4시에 적재 스크립트 실행." 문제는 파이프라인이 늘어나고, 단계 사이에 의존성이 생기는 순간 시작됩니다.
cron이나 셸 스크립트 묶음으로 일정 규모를 넘기면 거의 항상 다음 세 가지가 발목을 잡습니다.
- 의존성 표현이 안 된다. "추출이 끝나야 변환을 돌린다"를 cron은 모릅니다. 그래서 사람들은 "추출은 보통 30분 걸리니까 변환은 그 30분 뒤에 시작"처럼 시간으로 때웁니다. 추출이 35분 걸리는 날, 변환은 미완성 데이터를 읽습니다.
- 재시도와 실패 처리가 없다. 스크립트가 중간에 죽으면? cron은 그냥 다음 스케줄까지 침묵합니다. 재시도 로직, 백오프, 부분 재실행을 전부 스크립트 안에 손으로 짜 넣어야 합니다.
- 관측 가능성이 없다. 어제 밤 파이프라인이 어디까지 돌았는지, 어느 단계가 얼마나 걸렸는지, 왜 실패했는지를 보려면 서버에 SSH로 들어가 로그를 뒤져야 합니다. "지난주 화요일 적재가 왜 비었지?"에 답하기가 사실상 불가능합니다.
이 세 가지 결핍을 한 그림으로 비교하면 이렇습니다.
cron은 "언제 실행할지"만 압니다. 오케스트레이터는 "무엇이 무엇에 의존하고, 실패하면 어떻게 하고, 지금 어디까지 왔는지"를 압니다.
그래서 오케스트레이터가 푸는 문제
워크플로 오케스트레이터는 작업들을 **방향성 비순환 그래프(DAG, Directed Acyclic Graph)**로 표현합니다. 노드는 할 일(태스크), 엣지는 "이게 끝나야 저게 돈다"는 의존성입니다. 오케스트레이터는 이 그래프를 보고 다음을 자동으로 해줍니다.
- 의존성 순서대로 태스크 스케줄링 (병렬 가능한 건 병렬로)
- 실패 시 재시도, 백오프, 알림
- 실행 이력·로그·소요 시간의 중앙 집중 기록과 시각화
- 과거 구간 재실행(backfill), 수동 트리거, 일시 중지
Airflow는 이 영역에서 가장 널리 쓰이는 오픈소스이고, DAG를 Python 코드로 정의한다는 점이 특징입니다. YAML이나 GUI가 아니라 코드라서 버전 관리·테스트·코드 리뷰가 그대로 됩니다.
Airflow가 잘 맞는 경우 / 아닌 경우
도구는 만능이 아닙니다. Airflow가 빛나는 자리와 그렇지 않은 자리를 솔직히 구분하면 이렇습니다.
잘 맞는 경우
- 배치 중심 ETL/ELT, 데이터 웨어하우스 적재, 주기적 리포트 생성
- 단계가 여럿이고 서로 의존하는 파이프라인 (수십~수천 개 태스크)
- 이기종 시스템을 엮는 작업 (DB → S3 → Spark → BI)
- 스케줄 + 백필 + 재실행 이력이 중요한 운영
잘 안 맞는 경우
- 밀리초 단위 저지연 스트리밍 (그건 Kafka/Flink 영역)
- 요청-응답형 실시간 API 처리
- 초당 수천 건의 초경량 이벤트 처리 (스케줄러 오버헤드가 부담)
Airflow는 "스트리밍 엔진"이 아니라 "배치 오케스트레이터"입니다. 실시간 처리를 Airflow로 우겨넣으려는 순간이 보통 잘못된 도구 선택의 신호입니다.
대안과의 간단 비교
오케스트레이터 생태계에는 좋은 대안이 여럿 있습니다. 절대적 우열보다 "성향 차이"로 이해하는 편이 좋습니다.
| 도구 | 정의 방식 | 강점 | 주로 맞는 자리 |
|---|---|---|---|
| Airflow | Python DAG | 가장 넓은 생태계·통합(provider), 성숙한 운영 도구 | 범용 배치 ETL, 복잡한 의존성, 대규모 스케줄 |
| Prefect | Python 함수 데코레이터 | 동적 워크플로, 가벼운 개발자 경험 | 파이썬 중심 팀, 동적·이벤트성 흐름 |
| Dagster | Asset 중심 (소프트웨어 정의 자산) | 데이터 자산·타입·테스트 일급 지원 | 데이터 자산 계보·품질을 모델링하려는 팀 |
| Argo Workflows | Kubernetes CRD(YAML) | 컨테이너 네이티브, K8s 친화 | K8s 위 컨테이너 단위 파이프라인 |
흥미롭게도 Airflow 3은 뒤에서 볼 Asset(자산) 기반 스케줄링을 도입하면서, Dagster가 강조하던 "데이터 자산 중심" 사고를 상당히 흡수했습니다.
Airflow 2.x → 3.x, 무엇이 달라졌나
이 연재는 전부 Airflow 3.0/3.x(2025년 GA) 기준입니다. 2.x를 써본 분이라면 바뀐 지점을 먼저 머리에 넣어두면 나머지 편이 훨씬 수월합니다. 핵심만 추리면 다음과 같습니다.
| 영역 | 2.x | 3.x | 의미 |
|---|---|---|---|
| 웹 컴포넌트 | Webserver | API server (UI + REST API 통합) | UI와 안정·버전드 REST API를 한 컴포넌트가 제공 |
| DAG 파싱 | 스케줄러 내부 | DAG processor 분리 | DAG 파싱을 독립 프로세스로 격리(안정성·보안) |
| 태스크 실행 | 워커가 메타DB 직접 접속 | Task SDK + Task Execution API | 워커는 API server를 통해 통신 → 원격/언어 비종속 실행 |
| 데이터 인지 스케줄 | Dataset | Asset (@asset, schedule=[asset]) | 용어·기능 정리, 자산 중심 스케줄링 |
| 실행 추적 | 버전 개념 약함 | DAG versioning | 어떤 버전 DAG로 실행됐는지 UI에서 추적 |
| 백필 | CLI 위주 | 스케줄러 관리 backfill (UI/API) | UI/API에서 트리거하면 스케줄러가 수행 |
catchup 기본값 | True | False | 새 DAG가 과거 구간을 무더기로 돌리는 사고 예방 |
| 시간 필드 | execution_date | logical_date (또는 data_interval_*) | execution_date 제거, 수동/자산 트리거 시 None 가능 |
| 제거된 것 | SubDAG, SLA, 구 experimental REST API | TaskGroup/Asset, Deadline, 안정 REST API로 대체 | 혼란스러운 레거시 정리 |
| Executor | Local/Celery/Kubernetes | + EdgeExecutor, 그리고 hybrid(동시 다중) | HTTP 기반 원격/엣지 워커, 여러 executor 병행 |
임포트 경로도 바뀌었습니다. 3.x에서는 airflow.sdk가 권장 진입점입니다.
from airflow.sdk import dag, task, Asset
@dag(schedule="@daily", catchup=False) # 3.x에서 catchup 기본값은 False
def daily_etl():
@task
def extract():
return {"rows": 1000} # 예시 값
@task
def load(payload: dict):
print(f"적재 행 수(예시): {payload['rows']}")
load(extract())
daily_etl()이 코드가 왜 이렇게 생겼고 내부에서 무슨 일이 벌어지는지는 4편: DAG 작성의 정석에서 깊게 다룹니다. 여기서는 "워커가 더 이상 메타DB에 직접 붙지 않고 Task Execution API를 거친다"는 구조적 변화 하나만 기억하면 충분합니다.
이 연재에서 배울 것 (로드맵)
전체 흐름은 "왜 → 구조 → 구성 → 작성 → 연동 → 운영"의 순서로 짰습니다. 한 장으로 보면 이렇습니다.
각 편의 링크는 아래 목차에서 바로 이동할 수 있습니다.
| 편 | 제목 | 한 줄 요지 |
|---|---|---|
| 0 | (지금 이 글) 시리즈 개요 & 언제 쓰나 | 오케스트레이션의 문제 정의와 3.x 변화 개관 |
| 1 | 아키텍처 해부 | Scheduler·API server·DAG processor·Triggerer·Worker가 어떻게 맞물리나 |
| 2 | 클러스터로 구성하기 | 단일 노드를 넘어 컴포넌트를 분리 배치하기 |
| 3 | 환경설정 & 최적화 | 동시성 3계층·Pool·자원 격리 튜닝 |
| 4 | DAG 작성의 정석 | Task SDK, 데코레이터, TaskGroup, 베스트 프랙티스 |
| 5 | DAG 고급 테크닉 | 스크립트·파라미터·에러 처리·PostgreSQL·재실행·날짜 기준 |
| 6 | 스케줄링 & Asset | cron 스케줄과 자산 기반(asset-driven) 스케줄링 |
| 7 | XCom & 데이터 전달 | 태스크 간 데이터 전달과 그 한계 |
| 8 | 외부 시스템 연동 & 싱크 호출 | Connection·Hook·provider, deferrable 패턴 |
| 9 | REST API & 원격 스케줄 변경 | JWT 인증 REST API로 원격 트리거·제어 |
| 10 | 모니터링 & 운영 | 메트릭·알림·로그·SLA 대체(Deadline) |
| 11 | 테스트·CI/CD·보안 | DAG 테스트, 파이프라인, 권한·시크릿 관리 |
| 12 | 프로덕션 체크리스트 | 운영 투입 전 점검할 모든 것 |
정리하며
오케스트레이션의 본질은 "의존성·재시도·관측"이라는 cron의 세 결핍을 메우는 일입니다. Airflow는 그걸 Python 코드로 표현하는 성숙한 도구이고, 3.x에 와서 API server/DAG processor 분리, Task Execution API, Asset 스케줄링으로 구조가 한층 깔끔해졌습니다. 다만 실시간 스트리밍처럼 결이 다른 일에는 다른 도구를 쓰는 게 맞습니다.
도구 선택의 출발점은 "내 워크로드가 배치 의존성 그래프인가?"라는 질문입니다. 그렇다면 Airflow는 거의 항상 후보에 듭니다.
다음 편에서는 방금 표로 훑은 컴포넌트들이 실제로 어떻게 맞물려 돌아가는지를 뜯어봅니다.
➡️ 다음 편: 1편 — Airflow 3 아키텍처 해부