Search
🔄

Flink CDC 파이프라인 구축

개요

항목
내용
기간
2025.08 ~ 2026.02
역할
설계 및 구축 주도
기술 스택
Kotlin, Apache Flink, Kafka, MongoDB, Debezium CDC, Kubernetes, ArgoCD
파이프라인
MongoDB → Kafka → Hadoop

배경

다나와 플랫폼에서 MongoDB에 입력되는 상품 데이터는 색인(검색), 통계, 레거시 시스템 역마이그레이션 등 다양한 후처리 작업이 필요했습니다. 이 데이터를 Hadoop 기반 후처리 시스템으로 실시간 적재해야 했으나, 기존에는 이를 위한 파이프라인이 존재하지 않았습니다.
flowchart LR
    A[상품 데이터 입력] --> B[MongoDB]
    B --> C[???]
    C --> D[Hadoop 후처리]
    D --> E[색인/검색]
    D --> F[통계]
    D --> G[레거시 역마이그레이션]
Mermaid
복사

문제

MongoDB의 변경 사항을 Kafka에 실시간으로 발행해야 함
Before/After Diff 처리가 핵심 요구사항 — 색인 및 역마이그레이션에서 변경된 필드만 처리해야 부하를 줄일 수 있음
안정적인 배포/운영 체계가 필요

무엇을 했나

1단계: 기술 선택 — Flink SQL vs Kotlin DataStream API

초기에는 Flink SQL(Streampark)로 프로토타이핑을 진행했습니다.
비교 항목
Flink SQL (Streampark)
Kotlin DataStream API
개발 편의성
높음 (SQL 기반)
보통 (코드 작성 필요)
Before/After Diff 처리
제한적
완전 제어 가능
MongoDB CDC 지원
Pipeline API 미지원
Debezium 직접 활용
커스텀 로직
어려움
자유로움
핵심 요구사항인 Before/After Diff 처리가 Flink SQL로는 제한적이라는 문제를 식별하고 보고하여, Kotlin DataStream API 기반으로 전환하는 의사결정을 주도했습니다.

2단계: 파이프라인 아키텍처

flowchart TD
    A[MongoDB Change Stream] --> B[Debezium CDC]
    B --> C[Flink DataStream<br>Kotlin 코드]
    C --> D[Before/After Diff 처리]
    D --> E[Kafka Producer]
    E --> F[Kafka Topics]
    F --> G[Hadoop 적재]
    
    subgraph Kubernetes
        H[Flink Operator] --> C
        I[ArgoCD] --> H
    end
Mermaid
복사

3단계: 배포 파이프라인

Kubernetes Flink Operator를 도입하여 Flink Job을 K8S 네이티브로 관리
ArgoCD 기반 GitOps 배포 파이프라인을 구축하여 기존 팀의 배포 워크플로우와 통합

4단계: 로그 정합성 확보

기존 코드에 "UPDATE_AFTER received without UPDATE_BEFORE — Emitting with before=null"라는 로그가 있었음
이 로그는 "before가 누락된 것"이라고 단정하는 내용이었으나, 실제 원인은 누락이 아니라 순서가 뒤바뀌는 것이었음 (아래 트러블슈팅 참조)
AI 에이전트(데빈, 클로드 코드 등)가 이 로그를 근거로 "누락"으로 판단하여 확증 편향의 환각을 유발 — 로그가 기술적으로 틀린 내용인 것을 확인하고 수정
로그 정합성을 확보하여 Diff 처리 정확도를 개선하고, 색인 및 레거시 시스템 역마이그레이션 부하 이슈를 해결

트러블슈팅

UPDATE_BEFORE/AFTER 순서 역전 문제

현상
MongoDB update 시 상당한 비율로 UPDATE_BEFORE가 누락된 채 Kafka로 전송됨
레거시 역싱크에서 diff만 업데이트하기로 했으나, before 없이는 레거시 DB를 조회해야 하므로 부하 우려
초기 오판 — AI 에이전트의 확증 편향
기존 로그: "UPDATE_AFTER received without UPDATE_BEFORE — Emitting with before=null"
이 로그를 근거로 AI 에이전트들이 "누락"으로 판단했으나, 실제로는 누락이 아니라 순서가 뒤바뀌는 것이었음
다양한 위치에 로그를 추가하여 실제로 UPDATE_BEFORE가 생성되지만 UPDATE_AFTER보다 늦게 도착하는 것을 확인
근본 원인 분석
flowchart TD
    A[MongoDB 커넥터] -->|"문제없음: BEFORE가 항상 먼저 생성"| B[Source Operator]
    B -->|"병렬도 차이로 REBALANCE 발생"| C[Map Operator]
    C --> D["SubTask A: UPDATE_AFTER 먼저 도착"]
    C --> E["SubTask B: UPDATE_BEFORE 나중에 도착"]
    D --> F["❌ 순서 역전!"]
Mermaid
복사
Source Operator → Map Operator 사이에 병렬도 차이로 인한 리밸런싱이 발생
리밸런싱의 기본 알고리즘이 라운드 로빈 방식이므로, 같은 Key(document _id)를 가져도 서로 다른 SubTask로 분배될 수 있음
Flink 네트워크 레벨에서 순서 보장은 동일한 input channel(upstream subtask → 동일 downstream subtask)에서만 보장됨
해결 방법 찾은 경위
1.
기존 로그를 보고 "BEFORE 누락"으로 판단하고 있었으나, 직접 다양한 위치에 로그를 추가하여 확인한 결과 누락이 아니라 순서 역전임을 발견
2.
대안 검토 — BEFORE만 저장하던 기존 방식에서, AFTER도 Flink State에 저장하고 TTL을 매우 짧게 설정하는 방안을 고려 → 같은 ID에 대해 빠른 변경이 연속 발생할 시 정합성 문제가 있어 기각
3.
MongoDB 커넥터 레벨 버그 가능성은 낮다고 판단 (지원 시작한 지 오래 되었으므로) → Flink Operator를 잘못 사용해 순서보장이 되지 않는 것이라 판단
4.
Flink의 병렬도 및 네트워크 레벨 순서 보장 메커니즘을 학습하여 근본 원인 파악
해결
Source와 Map 사이에 keyBy(_id)를 추가하여 동일한 document의 BEFORE/AFTER가 항상 같은 SubTask로 분배되도록 보장
flowchart TD
    A[Source Operator] -->|"keyBy(_id)"| B["동일 Key → 동일 SubTask 보장"]
    B --> C[Map Operator]
    C --> D["SubTask A: BEFORE → AFTER ✅ 순서 보장"]
Mermaid
복사
시사점
잘못된 로그가 AI 에이전트의 확증 편향을 유발할 수 있음 — 로그 정합성이 AI 활용의 전제 조건
Flink의 병렬도와 네트워크 레벨 순서 보장 메커니즘에 대한 깊은 이해가 필요했던 이슈

결과

MongoDB → Kafka → Hadoop 실시간 CDC 파이프라인 구축 완료
평균 초당 처리량 3만 건, 피크 시 6만 건까지 처리 가능
파이프라인 지연 시간 500ms 미만 유지
Before/After Diff 처리를 통해 후처리 시스템(색인, 역마이그레이션)의 불필요한 전체 필드 갱신 부하를 제거
ArgoCD 기반 GitOps로 배포 자동화, 롤백 용이

배운 점

잘못된 로그가 AI 에이전트의 확증 편향을 유발할 수 있음. 로그 정합성이 AI 활용의 전제 조건이고, 기술적으로 틀린 로그 메시지는 사람뿐 아니라 AI도 잘못된 방향으로 이끔다
Flink의 병렬도와 네트워크 레벨 순서 보장 메커니즘을 이해해야 했던 이슈. 프레임워크의 추상화 아래에서 실제로 데이터가 어떻게 흐르는지 아는 것이 트러블슈팅의 핵심
Flink SQL로 먼저 시도해보고 한계를 식별한 뒤 전환한 경험이 있어서, "왜 처음부터 DataStream API를 선택하지 않았나"라는 질문에 답할 수 있음