큐(Queue) 완벽 정리 — 자료구조에서 Kafka까지
비동기 처리에 큐가 왜 필요한지부터 SQS와 Kafka의 차이까지, 실무 관점에서 큐를 정리했습니다.
큐(Queue) 완벽 정리 — 자료구조에서 Kafka까지
1. 실무에서 큐를 왜 쓰나
실무에서 큐를 쓰는 이유는 단순합니다. 현실의 시스템에서는 요청이 들어오는 속도와 처리하는 속도가 일정하지 않기 때문입니다. 큐는 이 불균형을 완충해주는 버퍼 역할을 합니다.
1-1. 핵심 효용 3가지
- 비동기 처리: 사용자에게 즉시 응답하고, 무거운 작업은 뒤에서 천천히 처리. 예) 회원가입 후 환영 이메일 발송
- 부하 분산: 트래픽 스파이크가 발생해도 컨슈머는 자기 페이스대로 처리. 예) 티켓팅 폭주
- 결합도 낮추기: 생산자는 컨슈머의 존재를 몰라도 됨. 컨슈머가 죽어도 생산자는 영향 없음
1-2. 실무 활용 사례
메시지 큐 — 서비스 간 비동기 통신
MSA에서 가장 많이 사용하는 패턴입니다. 서비스 A가 서비스 B를 직접 HTTP로 호출하면 B가 죽었을 때 A도 함께 영향을 받습니다. 중간에 메시지 큐를 두면 이 의존성이 끊어집니다.
대표 시나리오: 주문 시스템
- 사용자가 '주문하기' 버튼 클릭 → 주문 서비스가 주문 정보를 DB에 저장하고 큐에 메시지 발행
- 결제 서비스, 재고 서비스, 알림 서비스, 배송 서비스가 각각 큐를 구독하며 자기 일만 처리
- 알림 서비스가 잠깐 죽어도 주문 자체는 정상 진행됨
대표 솔루션: Apache Kafka, RabbitMQ, AWS SQS, Google Pub/Sub, Redis Streams
작업 큐 (Job Queue) — 무거운 백그라운드 작업
API 응답 시간을 빠르게 유지하면서 시간이 오래 걸리는 작업을 처리해야 할 때 사용합니다.
- 이미지 / 동영상 인코딩
- PDF, 엑셀 리포트 생성 후 이메일 발송
- 머신러닝 모델 추론 / 배치 학습
- 대량 푸시 알림, SMS, 이메일 발송
- 외부 API 호출이 필요한 작업 (Webhook 재시도 포함)
대표 솔루션: Celery (Python), Sidekiq (Ruby), Bull / BullMQ (Node.js), Spring Batch (Java)
이벤트 스트리밍
- 사용자 클릭스트림 → 추천 / 광고 / 분석 시스템에 동시 전달
- CDC (Change Data Capture): DB 변경 사항을 검색 인덱스, 캐시, 데이터 웨어하우스에 동기화
- 실시간 모니터링 / 이상 탐지 시스템의 입력 소스
웹 서버 내부 — Request Queue
Nginx, Tomcat, Node.js 이벤트 루프 모두 들어온 요청을 큐에 담아두고 워커 스레드가 꺼내 처리합니다. 'Connection Pool', 'Thread Pool'은 큐로 관리합니다.
운영체제 / CPU 스케줄링
프로세스 스케줄러는 실행 가능한 프로세스를 'Ready Queue'에 보관합니다. 프린터의 인쇄 작업 큐, 디스크 I/O 요청 큐도 모두 같은 원리이며, BFS도 큐를 사용합니다.
1-3. 큐의 변형들
| 종류 | 특징 | 실무 활용 예시 |
|---|---|---|
| 우선순위 큐 (Priority Queue) | 우선순위가 높은 항목부터 처리. 내부적으로 힙(Heap) 구조 사용 | VIP 고객 요청 우선 처리, 응급 알림, Dijkstra 최단 경로 알고리즘 |
| 덱 (Deque) | 양쪽 끝에서 모두 삽입/삭제 가능 | LRU 캐시 구현, 슬라이딩 윈도우 알고리즘, 브라우저 히스토리 |
| 원형 큐 (Circular Queue) | 고정 크기 배열을 원형으로 사용. 메모리 효율적 | 네트워크 패킷 버퍼, 임베디드 시스템, 스트리밍 버퍼 |
| 지연 큐 (Delay Queue) | 일정 시간 후에야 메시지가 소비 가능해짐 | 결제 후 30분 미입금 시 자동 취소, 예약 알림, 재시도 백오프 |
| Dead Letter Queue (DLQ) | 처리에 실패한 메시지를 별도로 모아두는 큐 | 실패 원인 분석, 수동 재처리, 장애 알림 트리거 |
2. 왜 비동기 처리에 큐를 쓰는가
2-1. 비동기란?
비동기는 "나중에 처리해도 되는 일" 을 뜻합니다. 회원가입 직후 환영 이메일 발송 예시를 보면:
- 동기: 이메일 발송이 끝날 때까지 사용자가 빈 화면을 보며 기다림 (3초)
- 비동기: 일단 "가입 완료!" 응답부터 주고, 이메일은 뒤에서 처리 (0.1초)
그럼 "뒤에서 처리"라는 과정을 큐가 해결해줍니다.
2-2. 큐 없이 비동기를 하면 생기는 문제
문제 1. 작업을 어디에 보관할 것인가
비동기로 넘긴 작업은 누군가 처리할 때까지 어딘가에 적혀 있어야 합니다. 그냥 메모리 변수에 두면 서버 재시작 시 작업이 증발합니다. 큐는 디스크에 영속적으로 저장해줍니다.
문제 2. 누가 처리할지 어떻게 정하나
워커가 5대 있을 때 같은 메시지를 5대가 동시에 보내면 안 됩니다. 큐는 "한 메시지는 한 워커만 가져간다"를 보장합니다 (락, ack 메커니즘).
문제 3. 처리 속도가 다를 때
회원가입은 초당 1000건이 들어오는데, 이메일 발송은 초당 100건만 가능하다면? 큐가 없으면 워커 과부하 → 다운 → 작업 유실. 큐가 있으면 9000건이 큐에 쌓여 워커가 자기 페이스대로 천천히 소진합니다. 이게 버퍼(완충) 역할입니다.
문제 4. 실패하면?
이메일 서버가 잠깐 죽었을 때 재시도해야 하는데 작업이 어디 있죠? 큐에 있으면 "처리 실패 → 큐에 다시 넣기"가 가능합니다.
2-3. 비동기에 필요한 모든 것을 큐가 해결
| 비동기에 필요한 것 | 큐가 제공하는 것 |
|---|---|
| 작업을 보관할 장소 | 영속적 저장소 |
| 처리할 워커 분배 | 메시지 단일 소비 보장 |
| 속도 차이 흡수 | 버퍼링 |
| 실패 시 재시도 | DLQ, 재처리 메커니즘 |
| 순서 보장 (필요시) | FIFO 보장 |
3. 카프카도 큐인가?
본질적으로는 큐가 맞습니다. 하지만 "전통적인 메시지 큐"와는 다른, 큐의 진화형입니다.
3-1. 카프카는 "큐"보다 "분산 로그(Log)"
카프카 공식 문서에서도 자기 자신을 "메시지 큐"가 아니라 "분산 이벤트 스트리밍 플랫폼" 또는 "분산 커밋 로그" 라고 부릅니다.
전통 큐(RabbitMQ, SQS)의 동작
[메시지 A] → 큐에 들어감 → 컨슈머가 가져감 → 큐에서 삭제됨
메시지가 소비되면 사라집니다.
카프카의 동작
[메시지 A] → 로그에 추가 → 컨슈머가 읽음 → 로그에 그대로 남음
(컨슈머는 "내가 어디까지 읽었는지"만 기억)
메시지가 읽혀도 사라지지 않습니다. 일정 기간(기본 7일) 또는 용량 한도까지 보관됩니다. 마치 유튜브 영상처럼, 누가 보든 영상은 그대로 있고, 시청자마다 "어디까지 봤는지"만 따로 기록됩니다.
3-2. 이 차이가 중요한 이유
1. 여러 컨슈머가 같은 메시지를 읽을 수 있음
[사용자 클릭 이벤트] → Kafka
├─→ 추천 시스템이 읽음
├─→ 광고 시스템이 읽음
├─→ 분석 시스템이 읽음
└─→ 실시간 모니터링이 읽음
2. 시간을 되돌릴 수 있음 (Replay)
- "어제 오후 2시부터 다시 읽어줘" → 가능
- "신규 컨슈머인데 처음부터 다 읽을게" → 가능
3. 압도적인 처리량
전통 큐는 보통 초당 수만 건이 한계지만, 카프카는 초당 수백만 건도 처리합니다. 디스크 순차 쓰기는 메모리 랜덤 접근보다도 빠를 수 있다는 원리를 활용합니다.
3-3. 비교 정리
| 항목 | 전통 메시지 큐 (RabbitMQ, SQS) | 카프카 |
|---|---|---|
| 메타포 | 우편함 / 주문 전표 | 녹화 방송 / 로그북 |
| 소비 후 메시지 | 삭제됨 | 그대로 남음 |
| 여러 컨슈머가 같은 메시지 | 별도 설정 필요 | 기본 동작 |
| 과거 메시지 재처리 | 어려움 | 쉬움 |
| 처리량 | 중간 (수만 TPS) | 매우 높음 (수백만 TPS) |
| 메시지 단위 | 작업(Task) 중심 | 이벤트(Event) 중심 |
| 주 용도 | 작업 분산, RPC 대체 | 데이터 파이프라인, 이벤트 스트리밍 |
4. 메시지 큐 vs 카프카: 상태를 누가 관리하나
핵심을 한 단어로 표현하면 상태(state)를 어디가 들고 있느냐입니다.
4-1. 전통 메시지 큐: 큐(서버)가 상태를 들고 있음
큐: "다음에 줄 메시지는 #5번이야"
컨슈머: (그냥 받기만 함, 아무것도 기억 안 함)
- 큐가 "어디까지 줬는지"를 기억
- 컨슈머는 멍청해도 됨 (stateless) — 그냥 받아서 처리
- 메시지를 줬는데 컨슈머가 처리 실패하면 → 큐가 "ack 안 왔네, 다시 줄게"
4-2. 카프카: 컨슈머가 상태를 들고 있음
카프카: (메시지 1, 2, 3, 4, 5, 6, 7, 8, 9, 10... 그냥 쌓아둠)
컨슈머 A: "나 5번까지 읽었어" (offset = 5)
컨슈머 B: "나 8번까지 읽었어" (offset = 8)
컨슈머 C: "나 처음부터 다시 읽을래" (offset = 0)
- 카프카는 그냥 데이터를 쌓아둘 뿐
- 컨슈머가 자기 offset(읽은 위치) 을 직접 관리
- 같은 메시지를 누가 어떻게 읽든 카프카는 신경 안 씀
4-3. 이 차이가 만드는 시스템 성격
전통 큐 = "큐가 관리하고 컨슈머는 조회만 한다"
- 메시지 전달, 재시도, 순서 보장 다 큐가 해줌
- 큐가 부담을 다 짊어지니까 처리량 한계 있음
- 컨슈머마다 다르게 읽기 어려움
카프카 = "카프카는 넣기만 하고 컨슈머가 관리한다"
- 카프카는 그냥 디스크에 순차 추가만 함 → 빠름
- 컨슈머가 자기 책임 하에 offset 관리 → 자유도 높음
- 여러 컨슈머가 각자 다른 위치에서 읽을 수 있음
4-4. 비유로 이해하기
- 전통 큐 = 도서관 사서가 책을 한 권씩 건네줌
- 사서가 "다음 사람!" 하고 다음 책을 줌
- 손님은 받기만 하면 됨
- 카프카 = 도서관 책장에 책이 꽂혀 있고 각자 책갈피로 표시
- 책은 그 자리에 쭉 있음
- 손님 A는 5페이지 책갈피, 손님 B는 8페이지 책갈피
- 책갈피를 잃어버리면 손님 본인 책임
카프카도 컨슈머 offset을 카프카 안의 특별한 토픽(
__consumer_offsets)에 저장합니다. 그래서 컨슈머가 죽었다 살아나도 복구할 수 있습니다. 다만 그 offset의 주인은 컨슈머고, 카프카는 보관만 해주는 겁니다.
5. 왜 카프카 같은 구조가 필요한가
5-1. 시나리오: 쇼핑몰의 "주문 완료" 이벤트
"주문이 완료됐다!" 라는 사실을 알아야 하는 곳:
├─ 결제 시스템 → 결제 진행
├─ 재고 시스템 → 재고 차감
├─ 배송 시스템 → 배송 준비
├─ 알림 시스템 → 카톡/SMS 발송
├─ 추천 시스템 → "이 상품 산 사람이 함께 본 상품" 학습
├─ 분석 시스템 → 매출 대시보드 업데이트
├─ 마케팅 시스템 → "구매 감사" 쿠폰 발송 트리거
├─ 회계 시스템 → 세금 계산용 데이터 적재
├─ 데이터 웨어하우스 → BI 분석용 적재
└─ 사기 탐지 시스템 → 이상 패턴 감지
하나의 사실에 10개 시스템이 반응해야 합니다.
5-2. 방법 1: Point-to-Point (직접 호출) — 안티패턴
주문 서비스가 10개 시스템을 모두 직접 호출하는 방식. 처음엔 그럴듯하지만 지옥이 펼쳐집니다.
- 문제 1: 주문 서비스가 모든 시스템을 알아야 함 → 새 시스템 추가될 때마다 코드 수정
- 문제 2: 한 곳이 죽으면 전체가 영향받음 → 추천 시스템이 느리면 주문도 느려짐
- 문제 3: N×M 폭발 → 서비스 5개, 이벤트 10종이면 50개의 연결
5-3. 방법 2: 카프카에 한 번만 발행
주문 서비스 → Kafka에 "주문 완료" 이벤트 발행 (한 번)
│
├─→ 결제 시스템 (자기가 알아서 읽음)
├─→ 재고 시스템 (자기가 알아서 읽음)
├─→ 배송 시스템 (자기가 알아서 읽음)
└─→ ... 새 시스템도 그냥 구독만 추가
주문 서비스는 누가 듣는지 모릅니다. 그냥 "주문 완료됐다"고 외칠 뿐입니다. 이걸 "발행-구독(Pub/Sub)" 패턴이라고 합니다.
5-4. 카프카가 진짜로 빛나는 순간
1. 시간차 소비 — 같은 데이터를 다른 시점에 읽고 싶을 때
- 결제/배송 같은 실시간 처리 컨슈머: 즉시 읽음
- 분석 시스템: 1시간마다 배치
- 데이터 웨어하우스: 하루에 한 번 새벽
- 사기 탐지 모델: 1주일치 데이터를 한꺼번에 학습용으로
2. 새 시스템을 나중에 붙일 때 — Replay (킬러 기능)
6개월 후, 데이터 사이언스 팀이 새 추천 모델을 만들고 싶음
→ "지난 6개월치 주문 이벤트를 학습 데이터로 쓰고 싶다"
전통 큐: 데이터 다 사라짐
Kafka: 그냥 offset=0부터 다시 읽으면 됨
3. 이벤트를 진실의 원천으로 (Event Sourcing)
전통 방식: DB에 "최종 상태"만 저장
→ 사용자 잔액: 50,000원
이벤트 방식: 모든 변화를 카프카에 기록
→ +100,000 입금
→ -30,000 출금
→ -20,000 출금
→ (현재 잔액은 이걸 다 더한 결과)
이러면 "이 잔액이 왜 이렇게 됐는지"를 시간순으로 추적할 수 있고, 이벤트를 다시 재생해서 상태를 재구성할 수도 있습니다. 금융권에서 매우 중요합니다.
4. 시스템 간 데이터 동기화 (CDC)
상품 DB 변경 → Kafka (Debezium 같은 CDC 도구가 자동으로 흘려보냄)
├─→ Elasticsearch 인덱스 업데이트
├─→ Redis 캐시 무효화
└─→ 추천 시스템 피처 업데이트
5-5. 정리
| 상황 | 전통 메시지 큐로 충분 | 카프카가 필요 |
|---|---|---|
| 한 작업을 한 워커가 처리 | ✅ | (과한 도구) |
| 여러 시스템이 같은 사실에 반응 | △ (가능하지만 빡셈) | ✅ |
| 과거 데이터 재처리 필요 | ❌ | ✅ |
| 신규 시스템이 과거 데이터부터 받아야 함 | ❌ | ✅ |
| 데이터 파이프라인 (DB → 검색 → 캐시 → 분석) | ❌ | ✅ |
| 초당 수십만~수백만 이벤트 | ❌ | ✅ |
| 이벤트 자체를 진실의 원천으로 | ❌ | ✅ |
6. SQS vs Kafka 상세 비교
6-1. 한 줄 요약
- SQS: "작업을 안전하게 워커에게 분배하는 큐" — Task Queue
- Kafka: "이벤트를 영속 저장하고 여러 소비자가 공유하는 로그" — Event Log
6-2. 메시지 저장 방식
SQS — 소비되면 사라짐
[메시지 A] → 큐에 들어감
↓
컨슈머가 receive() → "처리 중" 상태 (Visibility Timeout)
↓
컨슈머가 delete() 호출 → 큐에서 영구 삭제
기본 보관 기간 4일(최대 14일), 처리되면 바로 사라집니다.
Kafka — 소비돼도 안 사라짐
[메시지 A] → 토픽의 파티션에 추가 (append)
↓
컨슈머 1이 읽음 → 메시지는 그대로 남아있음
컨슈머 2가 읽음 → 여전히 그대로 남아있음
↓
설정된 retention(기본 7일) 후 삭제
6-3. 컨슈머 모델
SQS — Competing Consumers (경쟁 컨슈머)
┌─→ 워커 1 (메시지 A)
[큐의 메시지들] ─────├─→ 워커 2 (메시지 B)
└─→ 워커 3 (메시지 C)
여러 워커가 같은 큐에서 경쟁적으로 메시지를 가져갑니다. 같은 메시지를 여러 시스템이 받으려면 SNS와 결합해야 합니다.
SNS ─┬─→ SQS 큐 1 → 결제 시스템
├─→ SQS 큐 2 → 알림 시스템
└─→ SQS 큐 3 → 분석 시스템
Kafka — Consumer Groups
┌─→ 컨슈머 그룹 A (결제 서비스)
[Kafka 토픽] ─────────────────├─→ 컨슈머 그룹 B (분석 서비스)
└─→ 컨슈머 그룹 C (알림 서비스)
같은 토픽을 여러 그룹이 독립적으로 소비할 수 있어 자연스럽게 fan-out이 됩니다.
6-4. 순서 보장
SQS
- 표준 큐(Standard): 순서 보장 안 함, 처리량 무제한
- FIFO 큐: 엄격한 순서 보장, 초당 3,000 메시지 제한
Kafka
- 파티션 단위로 순서 보장
- 같은 키(예: 주문 ID)를 가진 메시지는 같은 파티션으로 → 순서 보장
- 파티션 간에는 순서 무관
6-5. 처리량
| 항목 | SQS | Kafka |
|---|---|---|
| 처리량 | 표준 큐: 무제한 / FIFO: 3K TPS | 수백만 TPS |
| 지연시간 | 수십~수백 ms | 수 ms |
6-6. 운영 부담
SQS — 완전 관리형 (Serverless)
서버 없음, 클러스터 관리 없음, 스케일링 자동, 백업 자동, 패치 자동. 개발자는 메시지 send/receive만 하면 끝입니다.
Kafka — 직접 운영해야 함
- 브로커 클러스터 (보통 3대 이상)
- ZooKeeper 또는 KRaft 클러스터
- 토픽/파티션 설계
- 컨슈머 그룹 리밸런싱 이슈 대응
- 디스크/네트워크 모니터링
- 버전 업그레이드, 보안 (TLS, ACL)
매니지드 서비스(AWS MSK, Confluent Cloud)를 써도 SQS보다는 신경 쓸 게 많습니다.
6-7. 비용 모델
SQS
- 요청 건수당 과금 (100만 건당 약 $0.40)
- 메시지가 적으면 거의 공짜
- 메시지가 많아질수록 비례 증가
Kafka (MSK 기준)
- 인프라(브로커, 스토리지)에 대한 시간당 과금
- 트래픽 적어도 클러스터 유지 비용 발생 (월 수백 달러부터)
- 트래픽 많을수록 단위당 효율 좋음
작은 회사: SQS가 압도적으로 저렴 대용량 처리: Kafka가 단위당 더 저렴
6-8. 한눈에 보는 전체 비교
| 항목 | SQS | Kafka |
|---|---|---|
| 본질 | 분산 메시지 큐 | 분산 이벤트 로그 |
| 메시지 보관 | 처리 후 삭제 | 보관 (retention) |
| 컨슈머 모델 | 경쟁 컨슈머 | 컨슈머 그룹 + Pub/Sub |
| 여러 시스템 fan-out | SNS 결합 필요 | 기본 지원 |
| 순서 보장 | FIFO 큐만 (제한적) | 파티션 단위 |
| 처리량 | 표준은 무제한 / FIFO 3K TPS | 수백만 TPS |
| 지연시간 | 수십~수백 ms | 수 ms |
| 재처리(Replay) | 불가 | 가능 |
| 운영 부담 | 거의 없음 (Serverless) | 높음 (또는 매니지드) |
| 비용 모델 | 사용량 기반 (요청당) | 인프라 기반 (시간당) |
| 시작 비용 | $0부터 | 매니지드도 월 수백 달러 |
| 메시지 크기 | 256KB | 기본 1MB |
| AWS 종속성 | AWS 전용 | 어디서나 |
6-9. 언제 무엇을 쓸까
SQS가 좋은 경우
- 단순한 작업 분산 ("이메일 보내기", "썸네일 생성")
- 트래픽이 일정하지 않고 가끔 폭주
- AWS 생태계 안에 이미 있음
- 운영 인력이 부족한 스타트업
- "한 번 처리하면 끝나는" 작업
예시: 회원가입 환영 이메일, 영수증 PDF 생성, 비동기 결제 처리, 푸시 알림
Kafka가 좋은 경우
- 같은 이벤트를 여러 시스템이 소비
- 데이터 파이프라인 (DB → 검색엔진 → 데이터웨어하우스)
- 이벤트 소싱(Event Sourcing) 아키텍처
- 초당 수만 건 이상의 이벤트
- 과거 데이터 재처리 필요
- 마이크로서비스 간 이벤트 기반 통신
예시: 사용자 클릭 스트림 분석, IoT 센서 데이터 수집, CDC, 실시간 추천 시스템 입력
6-10. 현실에서는 둘 다 쓴다
큰 회사일수록 둘을 같이 사용합니다. 역할이 다르니까요.
사용자 클릭 → Kafka (이벤트 로그, 여러 분석 시스템이 공유)
↓
특정 분석 결과
↓
"이메일 발송 필요" → SQS (단순 작업 분산, 워커가 처리)
Kafka는 "무슨 일이 있었는지" 기록하는 데 쓰고, SQS는 "누가 이 일을 처리해줘" 라고 작업 시킬 때 씁니다.
7. 토론 주제 모음
기본 개념
Q1. 큐는 왜 '한 번만 처리(exactly-once)'가 어려운가?
네트워크는 불안정하고 컨슈머는 죽을 수 있습니다. 그래서 대부분의 메시지 큐는 '최소 한 번(at-least-once)' 을 기본으로 합니다. 즉, 같은 메시지가 두 번 도착할 수 있다는 뜻입니다. 이를 처리하려면 컨슈머 측에서 멱등성(idempotency) 을 보장해야 합니다. → 멱등키(Idempotency Key) 설계 이야기로 이어집니다.
Q2. 큐가 무한정 쌓이면 어떻게 되나?
프로듀서는 빠르고 컨슈머가 못 따라가는 상황(백프레셔, Backpressure). 메모리/디스크가 터지거나, 메시지 처리 지연이 사용자에게 전파됩니다.
대응책
- 컨슈머 오토스케일링
- 큐 크기 모니터링 + 알람
- 우선순위 큐로 중요한 것만 먼저
- 오래된 메시지는 버리기 정책
Q3. 메시지 순서 보장은 항상 가능한가?
FIFO이니 당연히 순서가 보장될 것 같지만, 컨슈머가 여러 개일 때부터 깨집니다. Kafka는 파티션 단위로만 순서 보장, AWS SQS는 표준 큐와 FIFO 큐가 분리되어 있습니다. '주문 ID 같은 키로 같은 파티션에 라우팅' 하는 패턴이 실무 표준입니다.
Q4. 큐 vs 직접 호출, 언제 무엇을 쓸까?
- 즉시 응답이 필요하다 → 동기 호출 (HTTP, gRPC)
- 응답이 필요 없거나 늦어도 된다 → 큐
- 실패해도 재시도하면 되는 작업 → 큐
- 강한 일관성이 필요한 트랜잭션 → 동기 호출 또는 사가(Saga) 패턴
Kafka 관련
Q5. 그럼 카프카가 무조건 좋은 거 아닌가? 왜 아직도 RabbitMQ 같은 전통 메시지 큐를 쓰는 회사가 많을까?
- 컨슈머가 똑똑해야 한다는 건 그만큼 복잡하다는 뜻 (offset 관리, 중복 처리, 컨슈머 그룹 리밸런싱 등)
- "이메일 보내기" 같은 단순 작업에 카프카 쓰는 건 닭 잡는 데 소 잡는 칼
- 운영 난이도도 카프카가 훨씬 높음
Q6. 그럼 처음부터 카프카 쓰면 되는 거 아닌가? 왜 어떤 회사는 RabbitMQ로 시작해서 나중에 카프카로 옮길까?
힌트: YAGNI(You Aren't Gonna Need It) 원칙. 미래를 너무 일찍 대비하면 지금이 무거워집니다. 카프카는 운영 난이도가 만만치 않습니다. 회사 규모와 데이터 흐름의 복잡도가 임계점을 넘었을 때 도입하는 게 정석입니다.
SQS 관련
Q7. SQS Standard 큐는 순서를 보장 안 한다는데, 그럼 회원가입 처리 같은 데 써도 되나?
힌트: 한 사용자의 이벤트들이 순서대로 처리돼야 하나? 보통은 각 이벤트가 독립적이라 괜찮습니다. 진짜 순서가 중요한 건 같은 엔티티(같은 주문 ID 등)에 대한 연속 작업입니다.
Q8. Kafka가 메시지를 7일 보관한다면 GDPR 같은 개인정보 삭제 요구는 어떻게 대응해?
힌트: 어렵고 중요한 문제. Tombstone 메시지, retention 정책, 암호화 키 폐기(crypto-shredding) 같은 패턴이 있습니다.
Q9. 스타트업이 처음 시스템 만들 때 SQS로 시작해서 나중에 Kafka로 옮길 수 있을까?
힌트: 가능하지만 마이그레이션 비용이 큽니다. 처음부터 "이벤트가 추후 여러 곳에서 소비될 가능성"이 보이면 Kafka 고려할 만합니다. 다만 너무 일찍 도입하면 운영 비용으로 회사가 흔들립니다.
작업 vs 이벤트
Q10. 주문 처리는 RabbitMQ가 어울릴까, Kafka가 어울릴까?
정답은 "보통 RabbitMQ가 더 자연스럽다" 입니다. 주문은 한 번만 처리하면 되는 작업(Task) 이지 여러 시스템이 함께 봐야 할 이벤트(Event) 가 아니거든요. 반면 "주문 완료됐다는 사실"은 카프카로 흘려보내서 분석/추천/마케팅 시스템이 각자 활용하기 좋습니다.
이렇게 "작업이냐, 이벤트냐" 로 구분하면 둘 중 뭘 쓸지 감이 잡힙니다.
마무리
큐는 단순히 "줄 세우는 자료구조"가 아니라, 분산 시스템의 속도 차이를 흡수하고 결합도를 낮추는 핵심 기반 인프라입니다. 정리하면:
- 비동기 처리에는 큐가 거의 필수 — 보관, 분배, 버퍼링, 재시도를 모두 해결
- 전통 큐(SQS, RabbitMQ)는 "작업 분산" — 한 번 처리되면 끝나는 일에 적합
- Kafka는 "이벤트 로그" — 여러 시스템이 같은 사실을 다양한 시점에 소비할 때 빛남
- 상태를 누가 관리하는가가 본질적 차이 — 큐는 서버가, Kafka는 컨슈머가 관리
- 무조건 Kafka가 정답은 아님 — 운영 난이도와 비용을 고려해 임계점 이후 도입
- "작업이냐, 이벤트냐" 로 구분하면 도구 선택이 명확해짐
처음부터 화려하게 가지 말고, 지금 풀려는 문제가 작업 분산인지 이벤트 스트리밍인지부터 명확히 하는 게 가장 중요합니다.