💡 오늘 학습 키워드
- Kafka
🎯 학습 내용 정리
웹 서비스를 운영하다 보면 하나의 서비스가 모든 작업을 직접 처리하기보다 여러 서비스가 역할을 분담하여 비동기적으로 처리해야 하는 상황이 많다.
예를 들어 주문이 생성되면
- 주문 저장
- 결제 요청
- 재고 차감
- 알림 발송
- 포인트 적립
등의 작업이 동시에 발생한다.
이러한 작업을 HTTP 요청으로 순차적으로 호출하면 서비스 간 결합도가 높아지고 장애가 쉽게 전파된다.
Kafka는 이러한 문제를 해결하기 위한 분산 이벤트 스트리밍 플랫폼(Distributed Event Streaming Platform)이다.
Producer가 이벤트를 발행하면 Kafka가 이를 저장하고, Consumer가 필요한 시점에 메시지를 가져와 처리하는 구조를 제공한다.
즉, 서비스 간의 직접적인 호출 대신 이벤트 기반(Event-Driven Architecture)으로 데이터를 전달할 수 있도록 만들어 주는 핵심 인프라이다.
Kafka는 단순한 메시지 큐가 아니라 대용량 데이터를 안정적으로 저장하고 전달하는 역할을 수행한다.
대표적인 역할은 다음과 같다.
1) 실시간 데이터 처리
대량의 이벤트를 실시간으로 전달하고 처리할 수 있다.
예를 들어
- 주문 생성
- 사용자 활동 로그
- 결제 이벤트
- IoT 센서 데이터
등을 실시간으로 처리할 수 있다.
2) 데이터 통합
여러 시스템에서 발생하는 데이터를 하나의 플랫폼으로 수집하여 필요한 서비스가 구독하도록 만들 수 있다.
3) 장애 허용(Fault Tolerance)
메시지를 디스크에 저장하고 여러 브로커에 복제(Replication)하기 때문에 브로커 하나가 장애가 발생하더라도 데이터를 안전하게 유지할 수 있다.
Kafka의 장점과 단점에 대해서 알아보자.
먼저 다음과 같은 장점이 있다.
높은 신뢰성
- 메시지를 디스크에 저장한다.
- Replication을 통해 데이터 손실을 최소화한다.
- Consumer가 직접 Offset을 관리하므로 원하는 시점부터 다시 읽을 수 있다.
뛰어난 확장성
Broker와 Partition을 추가하여 손쉽게 Scale-Out이 가능하다.
높은 처리량
Sequential Write 방식으로 디스크에 저장하기 때문에 매우 높은 성능을 제공한다.
다양한 Consumer 지원
동일한 이벤트를 여러 Consumer Group이 동시에 소비할 수 있다.
예를 들어
주문 생성 이벤트 하나로
- 결제 서비스
- 배송 서비스
- 통계 서비스
가 각각 독립적으로 처리할 수 있다.
반면에 다음과 같은 단점들도 존재한다.
높은 운영 난이도
Broker, Partition, Replication, Offset 등 이해해야 하는 개념이 많다.
리소스를 많이 사용
대용량 데이터를 저장하고 처리하기 때문에 메모리와 CPU 사용량이 비교적 높다.
복잡한 초기 설정이 복잡
운영 환경에서는 Broker 수, Replication Factor, Partition 수 등을 신중하게 설계해야 한다.
Kafka의 핵심 구성 요소에 대해서 알아보자.
Message
Kafka를 통해 전달되는 데이터 단위이다.
메시지는
- Key
- Value
- Timestamp
등으로 구성된다.
Producer
메시지를 생성하여 Kafka Topic으로 전송하는 역할을 한다.
예를 들어 주문 서비스는 주문 생성 이벤트를 Producer를 통해 Kafka로 전달한다.
Order Service
↓
Producer
↓
Kafka
Topic
메시지를 저장하는 논리적인 공간이다.
예를 들어
- order-created
- payment-success
- user-signup
과 같이 이벤트 종류별로 Topic을 분리하여 관리한다.
Partition
Topic을 여러 개로 나눈 단위이다.
Partition이 존재하기 때문에 Kafka는 병렬 처리가 가능하다.
Topic
├── Partition 0
├── Partition 1
└── Partition 2
각 Partition 내부에서는 메시지 순서가 보장된다.
Key
메시지가 어느 Partition으로 저장될지를 결정한다.
동일한 Key를 사용하면 항상 같은 Partition으로 전달된다.
예를 들어
UserId = 1
을 Key로 사용하면
사용자 1의 모든 이벤트는 동일한 Partition에 저장되어 순서를 보장받는다.
Consumer
Topic에서 메시지를 읽어 처리하는 역할을 한다.
Producer가 메시지를 보내면 Consumer는 필요한 시점에 해당 메시지를 가져와 처리한다.
Consumer Group
Kafka에서 매우 중요한 개념이다.
동일한 Group의 Consumer들은 Partition을 나누어 처리한다.
Topic
Partition0 → Consumer1
Partition1 → Consumer2
Partition2 → Consumer3
반면 서로 다른 Group이라면 동일한 메시지를 각각 모두 소비할 수 있다.
즉,
Topic
│
┌────┴────┐
| |
Group A Group B
처럼 하나의 이벤트를 여러 서비스에서 독립적으로 사용할 수 있다.
Broker
Kafka 서버를 의미한다.
Broker는 메시지를 저장하고 Consumer에게 전달하는 역할을 수행한다.
여러 Broker를 구성하여 Cluster를 만들 수 있다.
ZooKeeper
기존 Kafka에서는 Broker 관리와 메타데이터 저장을 위해 ZooKeeper를 사용하였다.
다만 최신 Kafka는 KRaft(Kafka Raft) 모드를 지원하여 ZooKeeper 없이도 클러스터를 운영할 수 있다.
Kafka와 RabbitMQ의 차이에 대해서 정리해보면 다음과 같다.
| 항목 | RabbitMQ | Kafka |
| 목적 | 메시지 전달 | 이벤트 스트리밍 |
| 메시지 저장 | 단기 저장 | 장기 저장 가능 |
| 처리 방식 | Queue 기반 | Topic 기반 |
| 사용 사례 | 작업 큐, 요청/응답 | 로그, 이벤트, 데이터 스트리밍 |
| 성능 | 낮은 지연 | 높은 처리량 |
RabbitMQ는 작업(Task)을 전달하는 데 강점을 가지며,
Kafka는 이벤트를 저장하고 여러 서비스가 동시에 소비하는 구조에 강점을 가진다.
이번에는 Kafka와 server를 띄워서 직접 테스트를 진행해보자.
services:
kafka:
image: confluentinc/cp-kafka:8.1.0
container_name: kafka
hostname: kafka
ports:
- "9092:9092"
environment:
# KRaft
CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
# Listener
KAFKA_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://0.0.0.0:9092,CONTROLLER://kafka:29093
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT,CONTROLLER:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
# Controller
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:29093
# Single Node
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
volumes:
- kafka-data:/var/lib/kafka/data
kafka-ui:
image: provectuslabs/kafka-ui:latest
container_name: kafka-ui
depends_on:
- kafka
ports:
- "8080:8080"
environment:
KAFKA_CLUSTERS_0_NAME: local
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:29092
volumes:
kafka-data:
다음과 같은 Docker Compose를 이용하여
- Kafka
- Kafka UI
를 실행하였다.
docker compose up -d
실행 후 Kafka UI(localhost:8080)에서
- Broker
- Topic
- Consumer
상태를 확인할 수 있었다.
Producer 프로젝트에는
spring-kafka
의존성을 추가하였다.
KafkaTemplate을 Bean으로 등록한 뒤
kafkaTemplate.send(topic, key, message);
를 통해 메시지를 전송하였다.
실습에서는 반복문을 이용하여 동일한 Topic으로 10개의 메시지를 발행하였다.
Consumer에서는
@KafkaListener
를 이용하여 Topic을 구독하였다.
@KafkaListener(
groupId = "group_a",
topics = "topic1"
)
Listener가 실행되면 Producer가 보낸 메시지를 자동으로 수신할 수 있었다.
실습 결과를 확인보았다.
Topic 확인
Kafka UI에서 Topic을 생성하고 Producer가 보낸 메시지가 정상적으로 저장되는 것을 확인하였다.
Consumer Group 확인
topic1으로 메시지를 전송한 결과
- Group A
- Group B
모두 동일한 메시지를 수신하였다.
즉,
같은 Topic이라도 Consumer Group이 다르면 각각 모든 메시지를 소비한다는 점을 확인할 수 있었다.
topic2와 topic3은 동일한 Group C가 각각 다른 Topic을 구독하도록 구성하였다.
각 Topic의 메시지는 해당 Listener에서 정상적으로 처리되는 것을 확인하였다.
topic4 역시 Group D가 정상적으로 메시지를 수신하였다.
Kafka에서 가장 중요한 개념은 Topic, Partition, Consumer Group이다.
- Topic은 이벤트를 저장하는 공간이다.
- Partition은 병렬 처리를 위한 저장 단위이다.
- Consumer Group은 여러 Consumer가 메시지를 나누어 처리하도록 만든다.
- 서로 다른 Group은 동일한 메시지를 각각 모두 소비할 수 있다.
이러한 구조 덕분에 하나의 이벤트를 여러 서비스에서 독립적으로 활용할 수 있으며, 이는 MSA 환경에서 Kafka가 널리 사용되는 가장 큰 이유이다.
이번 실습에서는 Docker를 이용하여 Kafka 환경을 구축하고, Spring Boot 기반의 Producer와 Consumer를 구현하여 메시지 발행과 소비 과정을 직접 확인하였다.
또한 Consumer Group과 Topic의 동작 방식을 실습을 통해 검증하면서 Kafka가 단순한 메시지 큐가 아니라 대규모 이벤트 스트리밍 플랫폼이라는 점을 이해할 수 있었다.
향후 프로젝트에서는 주문 생성, 결제 완료, 리뷰 등록과 같은 이벤트를 Kafka로 전달하여 서비스 간 결합도를 낮추고 비동기 아키텍처를 구현하는 데 활용할 예정이다.
📚 한줄 정리
Kafka는 Topic과 Partition을 기반으로 대용량 이벤트를 안정적으로 저장하고, Consumer Group을 통해 여러 서비스가 데이터를 독립적으로 소비할 수 있도록 지원하는 분산 이벤트 스트리밍 플랫폼이다.
'Bootcamp > Fundamentals' 카테고리의 다른 글
| 대규모 시스템 설계와 RabbitMQ (0) | 2026.07.27 |
|---|---|
| Redis를 활용한 Session Clustering, Leaderboard 그리고 Cache (0) | 2026.07.24 |
| Redis (0) | 2026.07.23 |
| CI/CD와 GitLab CI + AWS ECS 자동배포 (1) | 2026.07.22 |
| Docker와 Docker Compose (0) | 2026.07.21 |