Stream Processing Essential Curriculum (Apache Flink) — 스트림 처리 심화 커리큘럼
소개
Data-Engineering-Essential 오버뷰 시리즈는 데이터 엔지니어링 수명주기 전체의 지도를 그렸습니다. 그 5단계 데이터 변환·처리(Processing)에서 우리는 스트림 처리를 다루며 이벤트 시간 vs 처리 시간, 워터마크, 윈도잉을 짚었습니다. 다만 거기서는 “처리 지도 안에서 스트림 엔진이 어디에 있는가”까지만, 즉 이 개념들을 소개 수준으로만 다루고 그 깊은 이야기는 별도 시리즈로 미뤄 두었습니다. 이 글이 바로 그 예고된 심화 스핀오프, Stream-Processing-Essential 시리즈의 마스터 로드맵입니다. 스트림 처리의 사실상 표준 엔진인 Apache Flink를 중심으로, 오버뷰가 개념만 소개한 것들을 실제로 손에 잡히게 파고듭니다.
배치 처리는 “끝이 있는 데이터”를 다룹니다. 파일이 다 모이면 처리를 시작하고, 다 처리하면 끝납니다. 반면 스트림 처리는 끝이 없는 데이터 — 계속 흘러 들어오고, 종종 순서가 뒤바뀌며, 언제 “다 왔다”고 말할 수 있는지조차 불분명한 데이터 — 를 다룹니다. 이 근본적인 차이 때문에 스트림 처리에는 배치에 없던 고유한 문제들이 생깁니다. 언제 집계를 마감할 것인가(워터마크), 처리 중인 상태를 어떻게 장애로부터 지킬 것인가(상태·체크포인트), 장애가 나도 결과를 정확히 한 번만 반영하려면 어떻게 해야 하는가(exactly-once)가 그것입니다. 이 시리즈는 바로 그 문제들을 정면으로 다룹니다.
범위 구분 — Kafka Streams와의 경계: 스트림 처리는 자매 시리즈 후보인
Kafka-Essential의 “Kafka Streams” 편과 주제가 인접합니다. 두 시리즈의 초점은 다릅니다. Kafka Streams는 Kafka에 밀착한 라이브러리형 스트림 처리이고, 이Stream-Processing-Essential시리즈는 소스·싱크에 얽매이지 않는 Flink 중심의 범용 스트림 처리 엔진 — 무거운 상태 관리, 정교한 이벤트 시간 처리, exactly-once, CEP까지 아우르는 독립 실행 엔진 — 에 초점을 둡니다. Kafka 시리즈가 “Kafka 생태계 안에서의 스트림 처리”를 맡는다면, 이 시리즈는 “스트림 처리 그 자체의 원리와 엔진”을 맡습니다.
이 시리즈는 그 원리 속으로 들어갑니다. 스트림 처리 모델(무한 데이터를 어떻게 볼 것인가)에서 출발해, 지연·역순 도착을 다스리는 이벤트 시간·워터마크로 “언제 계산하는가”를 잡고, 상태·체크포인트와 exactly-once로 “장애가 나도 정확한가”를 보장한 뒤, 윈도잉·조인·CEP로 표현력을 넓히고, 마지막으로 Flink SQL로 이 모든 것을 선언적으로 다루는 것으로 마무리합니다. 각 단계를 정복할 때마다 상세 딥다이브 포스트를 작성하고 체크박스를 채우는 도장깨기 방식으로 진행합니다.
학습 진행 현황
완료한 항목에는 상세 포스트 링크가 연결됩니다. 학습이 진행될 때마다 체크박스와 진행률을 갱신합니다.
- 현재 완료한 항목: 0개
- 전체 항목: 6개
- 진행률: 0%
1단계: 스트림 처리 모델 — 무한 스트림과 배치와의 차이
무엇을 배우나: 배치가 다루는 “끝이 있는(bounded) 데이터”와 스트림이 다루는 “끝이 없는(unbounded) 데이터”의 근본적 차이를 잡고, Flink가 스트림을 어떻게 실행 모델로 표현하는지 — 데이터플로우 그래프, 연산자 체이닝, 병렬성 — 를 익힙니다. 배치를 “유한한 스트림”으로 보는 통합 관점까지 이해하면 이후 모든 단계의 토대가 됩니다.
- 스트림 처리 모델: bounded vs unbounded 데이터, 데이터플로우 그래프와 연산자 병렬성, 배치를 스트림의 특수 사례로 보는 통합 관점
2단계: 이벤트 시간·워터마크 심화 — 지연·순서 뒤바뀜 처리
무엇을 배우나: 오버뷰에서 개념만 소개한 이벤트 시간(event time) vs 처리 시간(processing time)을 실제 문제 수준으로 파고듭니다. 네트워크 지연으로 이벤트가 늦게, 또 순서가 뒤바뀐 채 도착할 때, 워터마크가 어떻게 “이 시각까지의 데이터는 사실상 다 왔다”를 선언하며 집계 마감 시점을 정하는지, 그리고 지각 데이터(late data)와 allowed lateness를 어떻게 처리하는지를 익힙니다.
- 이벤트 시간·워터마크: event time vs processing time, 워터마크 생성 전략, 순서 뒤바뀜(out-of-order)과 지각 데이터·allowed lateness 처리
3단계: 상태(state)·체크포인트 — 상태 저장과 장애 복구
무엇을 배우나: 스트림 처리를 배치와 결정적으로 가르는 지점 — 상태입니다. 집계·조인·중복 제거는 모두 “지금까지 본 것”을 기억해야 하고, 그 상태는 장애가 나도 사라지면 안 됩니다. keyed state와 operator state, 상태 백엔드(RocksDB 등), 그리고 분산 스냅샷으로 상태를 안전하게 저장·복구하는 체크포인트(Chandy-Lamport 기반)와 세이브포인트를 익힙니다.
- 상태·체크포인트: keyed/operator state와 상태 백엔드, 분산 스냅샷 기반 체크포인트, 세이브포인트와 장애 복구
4단계: exactly-once·정확성 — 체크포인트 기반 정확히 한 번
무엇을 배우나: “장애가 나도 결과가 정확한가”에 답하는 단계입니다. at-most-once·at-least-once·exactly-once의 차이를 명확히 하고, Flink가 체크포인트와 트랜잭셔널 싱크(two-phase commit)를 결합해 어떻게 종단 간(end-to-end) 정확히 한 번을 보장하는지를 익힙니다. “정확히 한 번 처리”와 “정확히 한 번 반영(효과)”의 구분이 핵심입니다.
- exactly-once·정확성: 전달 보장 3종의 차이, 체크포인트 + 2PC 싱크로 만드는 end-to-end exactly-once, 처리 vs 효과의 정확성
5단계: 윈도잉·조인·CEP — 텀블링/슬라이딩/세션·스트림 조인·복합 이벤트
무엇을 배우나: 스트림에서 “무엇을 계산할 것인가”의 표현력을 넓히는 단계입니다. 시간을 구간으로 자르는 윈도잉(텀블링·슬라이딩·세션 윈도), 두 스트림을 시간 기준으로 잇는 스트림 조인(window join·interval join), 그리고 “A 다음 3초 안에 B가 오면 경보” 같은 패턴을 탐지하는 복합 이벤트 처리(CEP)를 익힙니다. 이벤트 시간·워터마크·상태가 여기서 하나로 맞물립니다.
- 윈도잉·조인·CEP: 텀블링/슬라이딩/세션 윈도, window/interval 스트림 조인, 패턴 매칭 기반 복합 이벤트 처리(CEP)
6단계: Flink SQL — 선언적 스트림 처리
무엇을 배우나: 마지막은 앞의 모든 개념을 선언적으로 다루는 단계입니다. 스트림을 계속 갱신되는 테이블로 보는 dynamic table과 stream-table 이원성, 이벤트 시간·워터마크·윈도를 SQL 구문으로 표현하기, 그리고 continuous query가 어떻게 결과를 갱신(changelog)하는지를 익힙니다. 저수준 API로 이해한 원리를 SQL이라는 접근하기 쉬운 표면으로 끌어올리며 시리즈를 마무리합니다.
- Flink SQL: dynamic table과 stream-table 이원성, SQL에서의 이벤트 시간·워터마크·윈도, continuous query와 changelog 결과
핵심 포인트
- 끝이 없다는 것이 모든 것을 바꾼다: 배치와 스트림의 차이는 규모가 아니라 경계입니다. 데이터에 끝이 없다는 사실 하나에서 워터마크·상태·exactly-once라는 스트림 고유의 문제가 전부 파생됩니다.
- 시간에는 두 종류가 있다: 이벤트가 “실제로 일어난 시각”(이벤트 시간)과 “시스템이 본 시각”(처리 시간)은 다릅니다. 정확한 스트림 처리는 거의 항상 이벤트 시간 위에서, 워터마크로 지연을 다스리며 이루어집니다.
- 상태가 스트림 처리의 심장이다: 의미 있는 스트림 연산은 대부분 상태를 가집니다. 그 상태를 체크포인트로 안전하게 지키는 능력이 곧 장애 복구와 정확성의 토대입니다.
- exactly-once는 마법이 아니라 설계다: 정확히 한 번은 체크포인트와 트랜잭셔널 싱크의 협업으로 만들어집니다. “처리를 한 번”이 아니라 “효과를 한 번”이 목표라는 점을 이해하는 것이 핵심입니다.
- 원리를 알면 SQL이 보인다: Flink SQL의 편리함 뒤에는 dynamic table·워터마크·상태라는 저수준 원리가 그대로 돌아갑니다. 아래를 이해한 사람만이 SQL 스트림 쿼리의 동작을 정확히 예측할 수 있습니다.
추천 학습 순서
위 단계 번호 순서대로 진행하는 것을 권합니다.
- 스트림의 본질(1~2단계) — 무한 스트림을 어떻게 보는가(모델), 그리고 언제 계산하는가(이벤트 시간·워터마크). 이 토대 없이 상태나 정확성부터 손대면 증상만 쫓게 됩니다.
- 정확성의 기둥(3~4단계) — 상태·체크포인트로 “장애가 나도 잃지 않는가”를, exactly-once로 “정확히 한 번인가”를 보장합니다. 스트림 처리를 신뢰할 수 있게 만드는 가장 어렵고 가장 중요한 단계입니다.
- 표현력(5~6단계) — 윈도잉·조인·CEP로 계산의 표현력을 넓히고, Flink SQL로 그 모든 것을 선언적으로 다루며 마무리합니다.
각 단계는 앞 단계의 토대 위에 쌓이므로, 순서대로 정복하며 체크박스를 채워 나가길 권합니다.
결론
스트림 처리는 “데이터를 실시간으로 처리한다”는 매력적인 문장 뒤에, 끝이 없는 데이터를 시간 기준으로, 상태를 지키며, 정확히 한 번 처리한다는 정교한 공학이 숨어 있는 분야입니다. 개별 API는 계속 진화하지만, 이벤트 시간·워터마크·상태·체크포인트라는 뼈대와 “무한 데이터가 모든 문제를 만든다”는 원리는 오래 갑니다. 이 6단계를 순서대로 정복하면, Flink 잡의 워터마크와 상태·체크포인트를 읽고 지연·정확성 문제를 짚어내는 안목, 그리고 저수준 API에서 Flink SQL까지 스트림 처리를 자유롭게 오르내리는 실무 감각을 갖추게 됩니다.
이 Stream-Processing-Essential 시리즈는 Data-Engineering-Essential 오버뷰가 예고한 심화 스핀오프 중 처리(스트림) 축을 맡습니다. 배치 축을 맡는 자매 시리즈 Spark-Essential의 5단계 Spark Structured Streaming이 “배치와 통합된 스트림”을 다룬다면, 이 시리즈는 스트림 네이티브 엔진의 원리를 다룹니다 — 둘을 함께 보면 스트림 처리의 두 접근을 입체적으로 이해할 수 있습니다. 오버뷰가 함께 약속한 수집의 Kafka(Kafka Streams 편에서 이 시리즈와 경계를 나눔) 역시 별도의 *-Essential 시리즈로 이어질 예정입니다.
다음 학습 (Next Learning)
- 데이터 변환·처리(Processing): 배치·스트림 엔진과 SQL 변환 — 이 시리즈가 갈라져 나온 오버뷰 5단계, 이벤트 시간·워터마크·윈도잉의 개념 소개를 복습
- Data Engineering Essential Curriculum — 전체 데이터 엔지니어링 로드맵으로 돌아가기
- Spark Essential Curriculum — 자매 시리즈. 5단계 Spark Structured Streaming에서 “배치와 통합된 스트림” 접근을 비교