Kafka Shallow Dive - Kafka는 왜 쓰는가
Published:
Kafka를 제대로 들여다보기로 했다. 이름만 알고 썼다가 장애 한 번 맞으니 속이 허해져서, 10주짜리 커리큘럼을 짰다. 첫 주는 “Kafka가 뭐고 왜 쓰는가”.
Kafka ?
분산 이벤트 스트리밍 플랫폼. 이름이 길지만 뜯어보면 3가지다.
- 분산(Distributed) - 여러 서버(브로커)가 클러스터로 묶여 동작
- 이벤트(Event) - “무슨 일이 언제 일어났다”는 사실(fact)을 기록
- 스트리밍(Streaming) - 끝없이 이어지는 이벤트 흐름을 실시간으로 처리
즉, “여러 서버에 걸쳐서 이벤트 로그를 쌓고 흘려보내는 시스템”.
왜 만들어졌나
LinkedIn이 2010년경에 만듦. 당시 문제:
- 서비스가 수십 개인데 서로 데이터를 주고받아야 함 (검색, 추천, 모니터링, DW 등)
- 각 서비스마다 별도 파이프라인 만들다 보니 N×M 연결지옥
- 실시간 분석도 하고 싶은데 배치로만 돌고 있음
[App1] → [Search]
↘ [Analytics]
↘ [Monitoring]
↘ [DW]
[App2] → [Search]
↘ [Analytics]
...
해결책으로 “중앙 로그(central log)” 컨셉 도입. 모든 이벤트를 Kafka에 던지면, 필요한 쪽이 가져다 읽음.
[App1] ↘ ↗ [Search]
[App2] → [ Kafka (logs) ] → [Analytics]
[App3] ↗ ↘ [DW]
N×M이 N+M으로 줄어들고, 새 consumer를 추가해도 producer는 안 바꿔도 된다.
기존 메시지 큐랑 뭐가 다름?
RabbitMQ, ActiveMQ 같은 전통 메시지 큐가 이미 있는데 왜 Kafka?
| 구분 | 전통 MQ (RabbitMQ 등) | Kafka |
|---|---|---|
| 모델 | Queue (consume 시 삭제) | Log (append-only, 보존) |
| 라우팅 | Exchange/Queue 복잡 | Topic/Partition 단순 |
| 메시지 보존 | 소비되면 사라짐 | 시간/용량 기반 보존 |
| 재처리 | 어렵거나 불가 | offset 되감기로 재처리 가능 |
| 처리량 | 수만/초 | 수십만~수백만/초 |
| 순서 보장 | 큐 단위 | 파티션 단위 |
| 용도 | 작업 분배, RPC-like | 이벤트 로그, 스트리밍 |
포인트는 “소비 = 삭제”가 아니라 “소비 = 읽기” 라는 것. Kafka는 메시지를 로그처럼 append-only로 쌓고, consumer는 자기 위치(offset)만 기억하면 됨. 같은 데이터를 여러 consumer가 각자 속도로 읽을 수 있고, 필요하면 과거 시점으로 되감을 수도 있다.
어떤 문제에 쓰는가
대표적인 유스케이스:
- 로그/메트릭 수집 - 앱 수백 대에서 나오는 로그를 한 곳으로 모음
- 이벤트 소싱 - 상태를 DB에 저장하는 대신 이벤트로 기록 (감사, 재구성 용이)
- CDC(Change Data Capture) - DB 변경분을 실시간으로 빼서 다른 시스템에 전파
- 실시간 분석 - 클릭스트림/IoT 데이터를 Kafka Streams/Flink로 처리
- 마이크로서비스 간 비동기 통신 - 서비스가 이벤트만 발행하고 나머진 subscribe
반대로 Kafka가 부적절한 경우도 있음:
- 요청/응답 패턴(RPC) — gRPC나 HTTP가 낫다
- 개별 메시지마다 복잡한 우선순위/지연 큐가 필요한 경우 — RabbitMQ가 낫다
- 초저지연(수 ms) — 가능은 하지만 Kafka의 강점은 처리량 쪽
핵심 용어 한 장 요약
이번 주는 이 정도만 잡고 가자. 다음 주에 파고든다.
- Broker - Kafka 서버 한 대. 여러 대 모아서 클러스터
- Topic - 이벤트를 분류하는 논리적 이름 (e.g.
orders,clicks) - Partition - Topic을 나눈 물리적 로그. 병렬성의 단위
- Offset - Partition 안에서 메시지의 위치 (0부터 증가)
- Producer - 이벤트를 topic에 쓰는 쪽
- Consumer - 이벤트를 topic에서 읽는 쪽
- Consumer Group - 같은 topic을 나눠 읽는 consumer 묶음
설치 & 첫 메시지
로컬에서 그냥 한 번 띄워봄. Confluent에서 제공하는 Docker Compose가 제일 편함.
# docker-compose.yml (최소)
services:
kafka:
image: apache/kafka:3.7.0
ports:
- "9092:9092"
environment:
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@localhost:9093
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
CLUSTER_ID: "MkU3OEVBNTcwNTJENDM2Qk"
docker compose up -d
# topic 생성
docker compose exec kafka \
kafka-topics.sh --bootstrap-server localhost:9092 \
--create --topic hello --partitions 1 --replication-factor 1
# 메시지 쓰기
docker compose exec -it kafka \
kafka-console-producer.sh --bootstrap-server localhost:9092 --topic hello
# 메시지 읽기 (다른 터미널)
docker compose exec kafka \
kafka-console-consumer.sh --bootstrap-server localhost:9092 \
--topic hello --from-beginning
--from-beginning을 빼면 consumer 실행 이후의 메시지만 받는다. 이게 offset의 첫 느낌.
정리
Kafka는 “분산된 append-only 로그”로 요약됨. 큐가 아니라 로그라서 여러 소비자가 독립적으로 읽고 되감을 수 있고, 그 특성 덕분에 실시간 파이프라인의 허브로 자리잡음. 왜 썼는지도 모르고 YAML만 복붙했던 지난 주와 달리, 이제 “로그로 쌓는다”는 문장이 감이 온다.
다음 주는 내부 구조 — Broker/Topic/Partition이 어떻게 파일시스템 위에 올라가 있는지.
