
Kafka란?
Kafka는 서비스에서 발생한 이벤트를 전달하고 저장하는 분산 이벤트 스트리밍 플랫폼입니다. 주문, 결제처럼 서비스에서 발생한 일을 Kafka에 기록해두면, 필요한 다른 서비스가 이를 읽어 각자 처리할 수 있습니다.
kafka의 개념 자체는 DB에 로그 테이블을 만들고 각 서비스가 폴링하는 방식으로도 흉내 낼 수 있습니다. 다만 이 경우 Consumer가 늘어날수록 테이블에 폴링 쿼리가 늘어나 DB에 부하가 쌓이고, Partition 같은 병렬 처리 구조나 보관 기간 삭제 정책도 직접 구현해야 합니다. Kafka는 이런 것들을 시스템 레벨에서 이미 구현해놓은 "이벤트 로그 전용 인프라"에 가깝습니다. 그래서 Consumer가 1~2개 정도인 소규모 시스템은 DB Outbox 테이블로도 충분하고, Consumer 수와 트래픽이 늘어날 때 Kafka로 옮기는 경우가 많습니다.
왜 Kafka를 쓰는가
Kafka를 쓰지 않으면 주문 서비스가 결제, 알림, 통계 서비스를 직접 호출하는 구조가 됩니다. 이 구조에서는 주문 서비스가 호출 대상 서비스들의 API 스펙과 장애 상황까지 알아야 합니다. 서비스가 늘어날수록 이 결합은 점점 부담이 됩니다.
Kafka를 사용하면 주문 서비스는 "주문이 생성됐다"는 이벤트만 발행하면 됩니다. 결제, 알림, 통계 서비스는 각자 필요한 이벤트를 읽어 독립적으로 처리합니다. 이를 통해 서비스 간 결합도를 낮출 수 있습니다.
언제 Kafka가 적합한가
Kafka는 단순히 비동기 처리가 필요해서 쓰는 도구가 아닙니다. 다음 조건이 함께 있을 때 Kafka가 적합합니다.
- 하나의 이벤트를 여러 서비스가 사용해야 하는 경우
- 이벤트를 일정 기간 보관해야 하는 경우
- Consumer 장애 이후 이벤트를 다시 처리해야 하는 경우
- 많은 양의 이벤트를 처리해야 하는 경우
- Producer와 Consumer의 처리 속도가 다른 경우
예를 들어 사용자의 상품 조회 이벤트를 통계, 추천, 로그, 분석 서비스가 각각 사용하고, 나중에 통계를 다시 계산하기 위해 과거 이벤트도 다시 읽어야 한다면 Kafka가 적합한 선택입니다.
PROPERTY_VIEWED
↓
Kafka
↓
통계 / 추천 / 로그 / 분석
이 구조의 장점은 장애 격리입니다. 통계 서비스에 장애가 발생해도 추천이나 로그 서비스는 계속 동작합니다. 통계 서비스가 복구되면 이전 Offset부터 다시 읽어 누락된 데이터를 처리하면 됩니다.
반대로 "이메일 하나를 나중에 보내기" 정도의 단순 비동기 처리라면 Kafka는 과할 수 있습니다. 애플리케이션 내부의 간단한 작업은 @Async나 Thread Pool로 처리할 수 있고, 하나의 작업을 Worker에게 분배하는 목적이라면 RabbitMQ가 더 자연스러울 수 있습니다.
주요 구성 요소
- Producer : Kafka에 메시지(이벤트)를 발행하는 애플리케이션
- Consumer : Kafka에 있는 이벤트를 읽어 처리하는 애플리케이션
- Consumer Group : 같은 Topic을 함께 소비하는 Consumer의 묶음. 각 그룹은 자신만의 Offset을 유지
- Topic : 이벤트를 종류별로 구분하는 논리적인 저장 공간
- Partition : 하나의 Topic을 나눈 물리적 단위. 여러 Partition을 여러 Consumer가 나누어 처리하면 이벤트를 병렬로 처리할 수 있음
- Offset : Consumer가 어디까지 읽었는지를 나타내는 위치 값
Partition이 중요한 이유는 병렬 처리입니다. 하나의 Topic이 여러 Partition으로 나뉘어 있고, Consumer Group 안의 여러 Consumer가 이를 나누어 맡으면 대량의 이벤트를 동시에 처리할 수 있습니다.
여기서 주의할 점은 Kafka를 쓴다고 처리 속도가 자동으로 빨라지는 게 아니라는 겁니다. 실제 동시성은 Partition 수와 그만큼 붙어 있는 Consumer 수에 의해 결정됩니다. Partition이 10개인데 Consumer가 1개뿐이면 그 Consumer가 10개 Partition을 순차적으로 처리하게 되어 병렬 처리 효과가 없습니다. 반대로 Consumer가 Partition 수보다 많으면 남는 Consumer는 아무 Partition도 받지 못하고 유휴 상태가 됩니다. 그래서 Partition 수와 Consumer 수를 함께 설계해야 합니다.
RabbitMQ와의 차이
둘 다 메시지를 전달하지만 목적이 다릅니다.
- RabbitMQ: "이 작업을 누군가 처리해줘"에 가깝습니다. 이메일 발송, 이미지 변환, PDF 생성처럼 하나의 작업을 Worker에게 전달하고 처리 결과를 받는 상황에 적합합니다.
- Kafka: "이런 일이 발생했다"에 가깝습니다. 주문 생성 이벤트를 알림, 결제, 통계, 추천 서비스가 각각 독립적으로 구독하는 상황에 적합합니다.
- RabbitMQ도 Fanout이나 Topic Exchange를 쓰면 하나의 메시지를 여러 Consumer에게 뿌리는 것 자체는 가능합니다. 결정적인 차이는 "메시지가 소비되면 사라지는가"입니다. RabbitMQ는 Worker가 메시지를 ack하면 큐에서 바로 삭제됩니다. 메시지의 목적은 "누군가에게 한 번 전달되는 것"이고, 전달이 끝나면 존재 이유가 사라집니다. 반면 Kafka는 Consumer가 읽었는지 여부와 무관하게, Topic에 설정된 보관 기간 동안 이벤트가 그대로 로그에 남아 있습니다. Consumer는 그 로그를 얼마나 읽었는지(Offset)만 기억할 뿐 메시지 자체를 지우지 않습니다.
이 차이 때문에 "나중에 새로 생긴 서비스가 과거 이벤트를 처음부터 다시 읽을 수 있는가"가 갈립니다. RabbitMQ는 이미 ack되고 사라진 메시지를 되돌릴 수 없지만, Kafka는 보관 기간 내라면 Offset을 되돌려 과거 이벤트를 몇 번이든 다시 읽을 수 있습니다.
Kafka가 이벤트 기반 아키텍처에 강한 대신, RabbitMQ가 더 나은 지점들도 분명히 있습니다.
- 작업 단위 처리(Task Queue): 메시지 하나가 정확히 한 Worker에게 전달되고 ack로 처리 완료를 관리하는 모델에 최적화되어 있습니다.
- 유연한 라우팅: Exchange(Direct, Topic, Fanout, Headers)로 메시지를 조건별로 세밀하게 라우팅할 수 있습니다.
- 낮은 지연시간: 소비 즉시 큐에서 제거되는 구조라, 단순 전달-소비 시나리오에서는 지연이 낮은 경우가 많습니다.
- 낮은 운영 복잡도: Broker, Partition, Consumer Group, Offset 같은 개념 없이 상대적으로 단순하게 운영할 수 있습니다.
- 우선순위 큐, 지연 메시지: 플러그인으로 메시지 우선순위나 "N분 뒤에 처리" 같은 지연 큐를 비교적 쉽게 구현할 수 있습니다.
그래서 이벤트를 여러 서비스가 나눠 보고 오래 보관해야 한다면 Kafka, 작업을 안정적으로 한 곳에 전달하고 라우팅이 복잡하거나 운영 부담을 낮추고 싶다면 RabbitMQ가 더 실용적인 선택일 수 있습니다.
운영할 때 주의할 점
메시지 중복
Consumer 장애나 Offset Commit 시점에 따라 같은 이벤트가 다시 처리될 수 있습니다. 예를 들어 결제 완료 이벤트가 두 번 전달되면 포인트가 중복 지급될 수 있습니다. 따라서 Consumer는 같은 이벤트가 여러 번 들어와도 결과가 중복되지 않도록 설계해야 합니다. 이를 멱등성이라고 하며, 이벤트 ID를 기준으로 이미 처리한 이벤트인지 확인한 뒤 처리 여부를 결정하는 방식으로 구현할 수 있습니다.
메시지 순서
Kafka는 Topic 전체의 순서를 보장하지 않습니다. 대신 같은 Partition 안에서는 순서를 보장합니다. 주문 생성, 결제 완료, 주문 취소처럼 순서가 중요한 이벤트는 같은 주문 ID를 Key로 사용해 같은 Partition으로 전달되도록 설계할 수 있습니다.
Consumer Lag
Producer가 이벤트를 만드는 속도보다 Consumer가 처리하는 속도가 느리면, 아직 처리하지 못한 이벤트가 쌓입니다. 이를 Consumer Lag이라고 합니다. Lag이 계속 증가한다면 Consumer 성능을 개선하거나 Consumer 수와 Partition 구성을 조정해야 합니다.
Retry와 DLQ
특정 이벤트 처리에 계속 실패하는데 무한 Retry를 하면 다음 이벤트도 처리하지 못할 수 있습니다. 그래서 일반적으로 Retry 횟수를 제한하고, 계속 실패한 이벤트는 별도 Topic(Dead Letter Topic)으로 분리합니다. 이후 실패 원인을 분석하고 필요하면 다시 처리할 수 있습니다.
데이터는 언제 삭제되는가
Kafka는 Consumer가 메시지를 읽었다고 바로 데이터를 삭제하지 않습니다. 삭제는 Consumer의 처리 여부가 아니라 Topic에 설정된 보관 정책을 기준으로 결정됩니다. 데이터가 남아있는 동안에는 장애가 발생한 Consumer가 이전 이벤트를 다시 읽거나, 새 Consumer가 과거 이벤트를 처리할 수 있습니다.
- 시간 기준 삭제: 일정 시간이 지나면 삭제하는 방식. 예를 들어 보관 기간을 7일로 설정할 수 있습니다.
- 용량 기준 삭제: Topic이 사용하는 디스크 용량을 기준으로 오래된 데이터를 삭제합니다.
삭제 정책은 "이벤트를 얼마나 오래 다시 처리해야 하는가?"를 기준으로 정하는 것이 좋습니다.
- 장애 복구를 위해 하루 정도 재처리하면 충분한가?
- 통계를 다시 계산하기 위해 한 달치 이벤트가 필요한가?
- 감사 로그처럼 장기간 보관해야 하는가?
- 현재 상태만 유지하면 되는 데이터인가?
알림 발송 이벤트는 짧게 보관해도 되지만, 주문이나 결제 이벤트는 재처리와 분석을 위해 더 길게 보관하는 경우가 많습니다. Kafka는 이벤트를 영구 보관하는 데이터베이스가 아니므로, 장기 보관이나 분석이 필요하다면 별도의 저장소로 적재하는 구조도 함께 고려해야 합니다.
'Infra' 카테고리의 다른 글
| 헬스체크란? (0) | 2026.08.15 |
|---|---|
| 오라클 클라우드 Always Free 정책 변경 - ARM 인스턴스 4 OCPU/24GB → 2 OCPU/12GB로 축소하기 (0) | 2026.06.14 |
| 오라클 클라우드 VPN 서버 구축 (WireGuard VPN) (0) | 2026.05.26 |
| 오라클 클라우드에 접속하는 봇들을 막자 (0) | 2026.05.24 |