개요
항목 | 내용 |
기간 | 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
endMermaid
복사
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를 선택하지 않았나"라는 질문에 답할 수 있음