처음엔 Kafka가 왜 필요한지 진짜 이해가 안 갔다. 그냥 API 호출 잘 하면 되는 거 아닌가 싶었다. 근데 주문 하나에 결제, 배송, 이메일까지 다 엮이기 시작하면 얘기가 달라진다.
처음에 "메시징 큐"라는 단어를 들었을 때, 그냥 HTTP 요청이랑 뭐가 다른 건지 몰랐다. 어차피 뭔가 보내고 받는 거 아닌가.
핵심은 보내는 쪽이 받는 쪽을 기다리지 않는다는 거다.
예를 들어 쇼핑몰에서 주문이 들어오면 이런 일들이 일어나야 한다:
REST API로 순서대로 처리하면? 주문 API가 이 5개를 전부 기다려야 한다. 배송 서버가 잠깐 죽으면 주문 자체가 실패한다.
Kafka를 쓰면 주문 서버는 "주문 들어왔어" 하고 Kafka에 이벤트만 던진 뒤 바로 응답을 반환한다. 나머지 5개 서버가 각자 알아서 메시지를 꺼내 처리한다. 배송 서버가 잠깐 죽어도 메시지가 Kafka에 남아있으니까 살아나면 그때 처리하면 된다.
이게 핵심이다. 느슨한 결합, 장애 내성, 대용량 처리.

LinkedIn이 2011년에 내부 데이터 파이프라인 문제를 해결하려고 만들었다. 당시 LinkedIn은 수억 명의 유저 활동 데이터를 실시간으로 처리해야 했는데, 기존 메시징 시스템으로는 처리량이 턱없이 부족했다.
그래서 직접 만든 게 Kafka다. 이름은 작가 Franz Kafka에서 따왔다고 한다. (이유는 딱히 없고 그냥 마음에 들어서라고.) 이후 Apache 재단에 오픈소스로 기증됐고, 지금은 Netflix, Uber, Airbnb 같은 대규모 서비스에서도 이벤트 처리와 데이터 파이프라인 용도로 널리 사용되고 있다.
최근에는 Uber가 Kafka 기반 이벤트 처리를 더 효율적으로 운영하기 위해 자체 Consumer Proxy 시스템(uForwarder)을 공개하기도 했다.
참고: Uber Engineering - uForwarder
처음에 진짜 이게 제일 헷갈렸다. 발행/구독 개념도 같고, 실시간이라는 말도 같다.
| 웹소켓 | Kafka | |
|---|---|---|
| 대상 | 클라이언트 ↔ 서버 (사람) | 서버 ↔ 서버 (시스템) |
| 연결 | 지속적인 연결 유지 | 연결 불필요, 비동기 |
| 메시지 유실 | 연결 끊기면 유실 | 브로커에 저장, 유실 없음 |
| 주 용도 | 채팅, 실시간 UI 업데이트 | 서비스 간 이벤트 전달 |
웹소켓은 사람한테 실시간으로 보여주는 거고, Kafka는 시스템끼리 이벤트를 안전하게 주고받는 거다. 목적 자체가 다르다.
참고 다이어그램: Kafka Architecture Guide (Solace)
메시지를 발행하는 주체. 어떤 Topic으로 보낼지, Key는 뭔지 지정해서 Broker에 전달한다.
Key를 지정하면 같은 Key의 메시지는 항상 같은 Partition으로 간다. 순서 보장이 필요할 때 활용한다.
Kafka 서버 자체. 메시지를 실제로 저장하고 Consumer에게 전달하는 역할이다.
보통 여러 대를 클러스터로 묶어서 운영한다. 한 대가 죽어도 나머지가 메시지를 들고 있으니까 서비스가 중단되지 않는다.
메시지를 분류하는 채널. 이름만 같으면 누구든 발행하고 구독할 수 있다.
종류별로 분리해야 소비하는 쪽이 자기한테 필요한 것만 가져갈 수 있다. 예를 들어 order-created, payment-completed, delivery-started를 따로 두면 배송 서버는 delivery-started만 구독하면 된다.
Topic을 물리적으로 나눈 것. 하나의 Topic이 여러 Partition으로 쪼개진다.
Partition 하나당 Consumer 하나가 붙어서 병렬로 처리할 수 있다. 대신 같은 Partition 안에서는 메시지가 순서대로 처리된다. Partition이 1개면 Consumer를 아무리 늘려도 순서대로 처리할 수밖에 없고, Partition이 3개면 Consumer 3개가 동시에 처리 가능하다.
결국 Kafka의 핵심 트레이드오프는 여기에 있다. 병렬 처리를 높이면 Partition이 늘고, Partition이 늘면 전체 순서 보장은 포기해야 한다. 순서가 중요한 데이터라면 같은 Key로 묶어서 항상 같은 Partition으로 보내는 방식으로 설계한다.
메시지를 소비하는 주체.
Consumer Group이 핵심 개념이다:
topic1을 group_a와 group_b가 동시에 구독하면 두 그룹 다 메시지를 받는 이유가 이것이다.
같은 메시징 시스템 계열인데 철학이 다르다.
Kafka 클러스터의 메타데이터 관리자 역할을 하던 별도 시스템이다. "어떤 Broker가 살아있는지", "어떤 Partition의 리더가 누구인지" 같은 정보를 담당했다.
원래 Kafka를 띄우려면 Zookeeper도 반드시 함께 띄워야 했다. 설정도 두 시스템을 따로 관리해야 했고, 둘 사이에 상태 불일치가 생기는 문제도 있었다.
이를 대체한 게 KRaft (Kafka Raft) 모드다. 기존처럼 Zookeeper에 메타데이터를 맡기는 대신, Kafka 내부의 Controller quorum이 직접 관리하는 방식이다. 덕분에 Zookeeper 없이 Kafka만으로 클러스터를 운영할 수 있게 됐다.
다만 최근 Kafka는 KRaft 기반으로 전환되는 흐름이라, 실습에서 사용한 Zookeeper 기반 구성은 현재 최신 방식과는 조금 차이가 있다. 지금 새 프로젝트를 시작한다면 KRaft 기반으로 세팅하는 경우가 더 많다.
참고: Apache Kafka 4.0 릴리즈 공식 문서