EDGE 데이터 파이프라인 개발 계획
EDGE 데이터 파이프라인의 초기 개발 계획. 배치 수집에서 이벤트 처리와 장애 복구 검증까지 필요한 문제와 순서를 정리한다.
edge는 뉴스 기반 가격 변동 설명을 증권사 MTS에 제공하는 B2B SaaS 프로젝트다. SW마에스트로 17기에서 2026년 6월부터 백엔드와 데이터 파이프라인 개발을 맡고 있다.
초기에는 하루치 데이터를 수집하고 정규화하는 배치를 만들었다. 이후 가격 변동 감지와 분석을 분리하려면 같은 사건의 중복 처리, 저장과 발행 사이의 유실, 실패 이후 재처리를 다뤄야 한다.
아래는 프로젝트 초기에 세운 개발 계획이다. 당시 목표로 적은 항목을 완료된 성과와 구분하고, 각 단계가 어떤 문제를 해결하기 위해 필요한지 연결했다. 특정 기술의 도입보다 그 기술이 필요한 조건을 확인하는 데 초점을 둔다.
목표는 중복 처리와 유실을 막고 복구를 검증하는 것이다
재전달을 고려한 이벤트 처리 파이프라인 — 같은 가격 변동을 여러 번 전달받아도 결과가 중복되지 않도록 만든다. Kafka는 검토 후보이며, 실제 요구와 대안을 비교한 뒤 도입 여부를 결정한다.
Outbox Pattern 기반 이벤트 발행 정합성 개선 — 가격 변동 사건 저장과 이벤트 발행 사이의 dual-write 문제를 줄이기 위해
price_movement_trigger와 outbox 테이블을 하나의 DB transaction으로 저장하고, outbox 변경을 이벤트로 발행하는 구조를 만든다.Reliability Lab을 통한 장애 상황 재현 및 복구 전략 검증 — worker 강제 종료, 저장 후 커밋 전 장애, 외부 API timeout, poison message, DLQ replay를 재현하고, 위 방어 장치들이 실제로 중복·유실을 막는지 검증한다.
배치에서 드러나는 문제를 따라 기능을 늘린다
세 항목은 구현 목표다. 이 목표를 기술 목록으로 바로 옮기기보다, 현재 흐름에서 필요한 기능을 먼저 확인해야 한다. 초기 계획에서 가정한 트리거 규모는 하루 몇 건이어서 처리량만으로 Kafka 도입을 설명하기는 어렵다.
중복 분석과 발행 누락은 작은 규모에서도 생길 수 있다. 따라서 처리량과 별개로 저장·전달·복구 과정의 실패 조건을 확인할 필요가 있다.
그래서 각 단계가 앞 단계에서 생긴 문제 때문에 필요해지도록 순서를 잡았다.
| 단계 | 무엇이 이걸 필요하게 만드나 | |
|---|---|---|
| 1 | 배치 기본 흐름 | 일단 하루치가 끝까지 흘러야 한다 |
| 2 | 감지와 분석 분리 — SQS | 감지는 싼데 분석이 비싸다 (누가 큐에 넣는지는 5·6번) |
| 3 | 멱등성 | 같은 메시지가 다시 전달될 수 있다 |
| 4 | Retry / DLQ | 반복 실패를 격리하고 다시 처리해야 한다 |
| 5 | dual-write 문제 | 저장은 됐는데 발행이 안 됐다 |
| 6 | Outbox | 5번을 푼다 |
| 7 | Kafka | 독립 재생·순서·운영 요구를 대안과 비교한다 |
| 8 | Reliability Lab | 이제 검증할 게 쌓였다 |
1. 배치 기본 흐름 — 흐름은 깔렸다
하루 한 번 수집해 정규화하는 배치가 끝까지 흐른다. 여기까지는 이벤트도 큐도 없다. 다만 흐름이 깔린 것이지 안이 다 된 건 아니다 — 다듬을 곳은 아래 글 끝에 적었다.
→ 일 배치 파이프라인을 Python 워커와 ECS Task로 시작한 이유
2. 감지와 분석 분리 — SQS
ETF 가격이 진입 게이트를 통과할 만큼 움직이면 그 사건을 price_movement_trigger에 기록한다. 여기까지는 싸다. 문제는 그 뒤다 — 기여도 분해, 근거 문서 수집, LLM 설명 생성이 붙는다. 느리고, 돈이 든다.
이 둘을 동기로 묶어두면 LLM이 느릴 때 감지가 밀린다. 떼어내야 하고, 떼려면 큐가 필요하다.
여기서 Kafka가 아니라 SQS를 쓰려 한다. 지금 필요한 건 “감지와 분석을 떼는 것”뿐이고, 그건 SQS가 한다. 이 단계에서는 파티션이나 소비자 그룹을 관리하는 로그 구조까지 필요한지 확인되지 않았다.
3. 멱등성
SQS Standard 큐에서는 같은 메시지가 다시 전달될 수 있다. 따라서 메시지를 받았다는 이유만으로 매번 분석하면 같은 가격 변동에 대한 작업과 LLM 호출 비용이 중복될 수 있다.
price_movement_trigger에는 이미 (etf_instrument_id, trade_date, detected_at) unique 제약이 걸려 있다. 같은 변동이 두 번 기록되지 않는 지점이 여기다. 소비 쪽에서도 같은 이벤트를 두 번 받았을 때 결과가 하나로 수렴해야 한다.
여기서는 재전달 가능성을 전제로 두고, 같은 이벤트가 다시 와도 결과가 중복되지 않는 멱등성을 소비 단계에 적용하려 한다.
4. Retry / DLQ
반복 실패한 메시지는 재시도만 계속하기보다 별도 큐로 격리해 원인을 확인해야 한다. 이 격리 큐가 DLQ다. SQS에서는 재수신 횟수를 기준으로 DLQ로 옮기는 정책을 설정하고, 원인을 고친 뒤 다시 처리할 수 있다.
실패한 메시지가 항상 큐 맨 앞에서 모든 메시지를 막는 것은 아니다. 순서가 중요한 FIFO 큐에서는 실패 메시지를 건너뛰는 것이 업무 순서를 깨뜨릴 수도 있으므로, 격리 정책도 처리 요구에 맞춰야 한다.
5. dual-write 문제
price_movement_trigger를 저장하고 이벤트를 발행하는데, 저장은 성공했고 발행이 실패하면? 변동은 기록됐는데 아무도 분석하지 않는다. 반대로 발행만 성공하고 저장이 실패하면 분석은 도는데 근거가 되는 사건 기록이 없다.
DB 저장과 SQS 발행은 하나의 DB 트랜잭션으로 함께 커밋되지 않는다. 한쪽만 성공한 상태를 복구할 설계가 필요하다.
6. Outbox
그래서 사건과 발행 의도를 같은 트랜잭션으로 저장한다.
1
2
3
4
변동 감지
→ (1 DB transaction) price_movement_trigger + outbox 저장
→ outbox 변경을 읽어 큐로 발행
→ Downstream Worker
둘 다 커밋되거나 둘 다 롤백된다. 발행은 outbox를 읽어서 별도로 한다.
outbox를 읽는 방법으로는 주기적으로 DB를 조회하는 폴링과, DB 변경 기록을 전달받는 CDC를 검토할 수 있다. Debezium을 Kafka Connect로 구성한다면 그 실행 환경도 운영해야 한다. 처음엔 Debezium을 목표로 적었는데, 지금 규모엔 과해 보인다. 폴링으로 시작해서 폴링의 한계(간격, DB 부하)를 실제로 만나면 그때 CDC로 넘어가는 게 맞다고 본다. 만나지 않으면 안 넘어간다.
7. Kafka
Kafka를 검토할 조건은 처리량만이 아니다. 이미 처리한 이벤트를 다시 읽어야 하는지, 여러 업무가 같은 이력을 각자의 위치에서 소비해야 하는지, 어느 범위의 순서를 지켜야 하는지가 중요하다.
다만 순서가 필요하다는 이유만으로 Kafka가 필수인 것은 아니다. SQS FIFO도 메시지 그룹 단위 순서를 지원하고, 여러 업무로 메시지를 전달하는 구성은 SNS와 여러 SQS 큐로 만들 수 있다. 삭제한 메시지의 재생이 필요하다면 별도 보관 입력을 사용하는 대안도 함께 봐야 한다.
이 단계의 판단은 이런 요구를 각각 구현하고 운영할 때의 차이를 비교하는 것이다. Kafka가 더 적합한 조건이 확인되면 선택하고, 기존 구성으로 충분하면 그 근거를 남긴다.
8. Reliability Lab
Lab은 서비스 기능이 아니라 장애를 재현·검증하는 실험 환경이다. “이렇게 하면 안전하다”를 말로 주장하는 대신 깨지는 조건을 직접 만든다.
- worker 처리 중 강제 종료
- DB 반영 후 큐 메시지 삭제 또는 오프셋 커밋 전 장애 → 재처리 시 중복 가능성
- 완료 위치를 먼저 기록한 뒤 DB 반영 실패 → 업무 누락 가능성
- 외부 API timeout
- poison message
- DLQ replay
이게 마지막인 이유는 단순하다. 검증할 게 있어야 검증 환경을 만든다. 3~6번의 방어 장치가 없는데 Lab부터 지으면 확인할 대상이 없다.
물론 검증은 3번부터 이미 한다. 멱등성을 넣으면 그 자리에서 두 번 넣어보는 게 당연하다. 여기서 말하는 Lab은 그런 단발 확인이 아니라, 위 실험들을 한 번에 재현하는 통합 환경이다. DB 반영과 메시지 처리 완료 사이의 장애는 SQS 단계에서도 확인할 수 있다. Kafka를 도입한다면 오프셋 커밋을 포함한 실패 조건을 추가한다.
마무리
위 여덟 단계는 초기 계획이며, 각 단계의 필요성은 앞선 구현과 검증 결과에 따라 달라질 수 있다. 완료 여부와 도입 이유는 개별 작업 기록으로 구분한다.
순서가 도중에 바뀔 수도 있다. 7번처럼 아예 안 갈 수도 있다. 그러면 왜 안 갔는지를 적는다. 완료된 자랑보다 막힌 지점과 그때의 판단을 남기는 걸 우선한다.
참고 자료
- Amazon SQS Standard queues — 재전달과 순서의 특성
- Amazon SQS FIFO queues — 메시지 그룹 단위 순서
- Amazon SQS dead-letter queues — 반복 실패 격리와 순서 제약
- Amazon SNS와 SQS 팬아웃 — 여러 큐로 같은 메시지를 전달하는 구성