Kafka 완벽 가이드 | 이벤트 스트리밍·Producer·Consumer·Partition·Replication
이 글의 핵심
Apache Kafka로 대규모 이벤트 스트리밍을 구축하는 완벽 가이드입니다. Producer, Consumer, Topic, Partition, Replication, Consumer Group까지 실전 예제로 정리했습니다.
실무 경험 공유: 일 10억 건의 이벤트를 처리하는 Kafka 클러스터를 운영하면서, 실시간 데이터 파이프라인을 구축하고 데이터 유실 없이 안정적으로 처리한 경험을 공유합니다.
들어가며: “로그가 너무 많아요”
실무 문제 시나리오
시나리오 1: 초당 10만 건의 로그가 발생해요
데이터베이스로 감당 안 됩니다. Kafka로 처리합니다.
시나리오 2: 여러 서비스가 같은 데이터를 필요로 해요
각각 API를 호출합니다. Kafka로 한 번 발행, 여러 곳에서 소비합니다.
시나리오 3: 실시간 분석이 필요해요
배치 처리는 느립니다. Kafka Streams로 실시간 처리합니다.
1. Kafka란?
핵심 특징
Apache Kafka는 분산 이벤트 스트리밍 플랫폼입니다.
주요 개념:
- Topic: 메시지 카테고리
- Partition: 병렬 처리 단위
- Producer: 메시지 발행
- Consumer: 메시지 소비
- Broker: Kafka 서버
성능:
- 처리량: 100만+ msg/sec
- 지연 시간: < 10ms
2. 설치
Docker Compose
# docker-compose.yml
version: '3.8'
services:
zookeeper:
image: confluentinc/cp-zookeeper:latest
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
kafka:
image: confluentinc/cp-kafka:latest
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
docker-compose up -d
3. Node.js 클라이언트
설치
npm install kafkajs
Producer
// producer.ts
import { Kafka } from 'kafkajs';
const kafka = new Kafka({
clientId: 'my-app',
brokers: ['localhost:9092'],
});
const producer = kafka.producer();
async function sendMessage() {
await producer.connect();
await producer.send({
topic: 'user-events',
messages: [
{
key: 'user-123',
value: JSON.stringify({
type: 'user_created',
userId: 123,
email: '[email protected]',
timestamp: new Date().toISOString(),
}),
},
],
});
console.log('Message sent');
await producer.disconnect();
}
sendMessage();
Consumer
// consumer.ts
import { Kafka } from 'kafkajs';
const kafka = new Kafka({
clientId: 'my-app',
brokers: ['localhost:9092'],
});
const consumer = kafka.consumer({ groupId: 'user-service' });
async function consumeMessages() {
await consumer.connect();
await consumer.subscribe({ topic: 'user-events', fromBeginning: true });
await consumer.run({
eachMessage: async ({ topic, partition, message }) => {
const event = JSON.parse(message.value.toString());
console.log(`Received from partition ${partition}:`, event);
// 이벤트 처리
await handleEvent(event);
},
});
}
async function handleEvent(event: any) {
console.log('Processing:', event);
// 비즈니스 로직
}
consumeMessages();
4. Consumer Group
병렬 처리
// consumer1.ts
const consumer1 = kafka.consumer({ groupId: 'analytics' });
await consumer1.subscribe({ topic: 'events' });
// consumer2.ts
const consumer2 = kafka.consumer({ groupId: 'analytics' });
await consumer2.subscribe({ topic: 'events' });
// 같은 그룹의 Consumer는 Partition을 나눠서 처리
// Partition 0 → Consumer 1
// Partition 1 → Consumer 2
5. Partition
Topic 생성
# 3개의 Partition으로 생성
kafka-topics.sh --create \
--topic orders \
--bootstrap-server localhost:9092 \
--partitions 3 \
--replication-factor 1
Key로 Partition 지정
// 같은 userId는 같은 Partition으로
await producer.send({
topic: 'user-events',
messages: [
{
key: 'user-123', // Partition 결정
value: JSON.stringify(event),
},
],
});
6. 실전 예제: 주문 처리 시스템
Producer (주문 서비스)
// services/order-service.ts
import { Kafka } from 'kafkajs';
const kafka = new Kafka({
clientId: 'order-service',
brokers: ['localhost:9092'],
});
const producer = kafka.producer();
export async function createOrder(order: any) {
await producer.connect();
// 주문 이벤트 발행
await producer.send({
topic: 'orders',
messages: [
{
key: order.userId.toString(),
value: JSON.stringify({
type: 'order_created',
orderId: order.id,
userId: order.userId,
amount: order.amount,
timestamp: new Date().toISOString(),
}),
},
],
});
console.log('Order created:', order.id);
}
Consumer (결제 서비스)
// services/payment-service.ts
const consumer = kafka.consumer({ groupId: 'payment-service' });
await consumer.connect();
await consumer.subscribe({ topic: 'orders' });
await consumer.run({
eachMessage: async ({ message }) => {
const event = JSON.parse(message.value.toString());
if (event.type === 'order_created') {
console.log('Processing payment for order:', event.orderId);
// 결제 처리
const paymentResult = await processPayment(event);
// 결제 완료 이벤트 발행
await producer.send({
topic: 'payments',
messages: [
{
value: JSON.stringify({
type: 'payment_completed',
orderId: event.orderId,
status: paymentResult.status,
}),
},
],
});
}
},
});
7. 에러 처리
재시도
await consumer.run({
eachMessage: async ({ message }) => {
try {
await processMessage(message);
} catch (error) {
console.error('Failed to process:', error);
// Dead Letter Queue로 전송
await producer.send({
topic: 'failed-messages',
messages: [message],
});
}
},
});
정리 및 체크리스트
핵심 요약
- Kafka: 분산 이벤트 스트리밍
- Topic: 메시지 카테고리
- Partition: 병렬 처리
- Consumer Group: 부하 분산
- Replication: 고가용성
- 고성능: 100만+ msg/sec
프로덕션 체크리스트
- Kafka 클러스터 구성
- Topic 및 Partition 설계
- Producer 구현
- Consumer Group 구현
- 에러 처리 및 재시도
- 모니터링 설정
- 백업 전략
같이 보면 좋은 글
- RabbitMQ 완벽 가이드
- Elasticsearch 실전 가이드
- Redis 고급 가이드
이 글에서 다루는 키워드
Kafka, Event Streaming, Message Queue, Big Data, Microservices, Real-time
자주 묻는 질문 (FAQ)
Q. Kafka vs RabbitMQ, 어떤 게 나은가요?
A. Kafka는 대용량 이벤트 스트리밍에 유리합니다. RabbitMQ는 작업 큐에 유리합니다. 로그/이벤트는 Kafka, 작업 큐는 RabbitMQ를 권장합니다.
Q. Zookeeper가 뭔가요?
A. Kafka 클러스터를 관리하는 도구입니다. Kafka 3.0+부터는 Zookeeper 없이도 사용 가능합니다 (KRaft 모드).
Q. 메시지 순서가 보장되나요?
A. 같은 Partition 내에서는 순서가 보장됩니다. 다른 Partition 간에는 보장되지 않습니다.
Q. 프로덕션에서 사용해도 되나요?
A. 네, LinkedIn, Netflix, Uber 등 많은 기업에서 사용합니다.