본문으로 건너뛰기
Previous
Next
Kafka 완벽 가이드 | 이벤트 스트리밍·Producer

Kafka 완벽 가이드 | 이벤트 스트리밍·Producer

Kafka 완벽 가이드 | 이벤트 스트리밍·Producer

이 글의 핵심

Apache Kafka로 대규모 이벤트 스트리밍을 구축하는 완벽 가이드. Producer, Consumer, Topic, Partition, Replication, Consumer Group까지 실전 예제로 정리. Start now.

이 글의 핵심

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 구현
  • 에러 처리 및 재시도
  • 모니터링 설정
  • 백업 전략

같이 보면 좋은 글


이 글에서 다루는 키워드

Kafka, Event Streaming, Message Queue, Big Data, Microservices, Real-time

내부 동작과 핵심 메커니즘

이 글의 주제는 「Kafka 완벽 가이드 | 이벤트 스트리밍·Producer·Consumer·Partition·Replication」입니다. 앞선 튜토리얼을 구현·런타임 관점에서 다시 압축합니다. 구성 요소 간 책임 분리와 관측 가능한 지점을 기준으로 “입력이 어디서 검증되고, 핵심 연산이 어디서 일어나며, 부작용(I/O·네트워크·디스크)·동시성이 어디서 터지는가”를 한 장면으로 그리면 장애 분석이 빨라집니다.

처리 파이프라인(개념도)

flowchart TD
  A[입력·요청·이벤트] --> B[파싱·검증·디코딩]
  B --> C[핵심 연산·상태 전이]
  C --> D[부작용: I/O·네트워크·동시성]
  D --> E[결과·관측·저장]

경계에서의 지연·실패(시퀀스 관점)

sequenceDiagram
  participant C as 클라이언트/호출자
  participant B as 경계(프로세스·런타임·게이트웨이)
  participant D as 의존성(외부 API·DB·큐)
  C->>B: 요청/이벤트
  B->>D: 조회·쓰기·RPC
  D-->>B: 지연·부분 실패·재시도 가능
  B-->>C: 응답 또는 오류(코드·상관 ID)

알고리즘·프로토콜·리소스 관점 체크포인트

  • 불변 조건(Invariant): 각 단계가 만족해야 하는 조건(버퍼 경계, 프로토콜 상태, 트랜잭션 격리, 파일 디스크립터 상한)을 문장으로 적어 두면 디버깅 비용이 줄어듭니다.
  • 결정성: 동일 입력에 동일 출력이 보장되는 순수 층과, 시간·네트워크·스레드 스케줄에 의해 달라질 수 있는 층을 분리해야 테스트와 장애 분석이 쉬워집니다.
  • 경계 비용: 직렬화/역직렬화, 문자 인코딩, syscall 횟수, 락 경합, GC·할당, 캐시 미스처럼 누적 비용을 의심 목록에 넣습니다.
  • 백프레셔: 생산자가 소비자보다 빠를 때(소켓 버퍼, 큐 깊이, 스트림) 어디서 어떤 신호로 속도를 줄일지 정의합니다.

프로덕션 운영 패턴

실서비스에서는 기능과 함께 관측·배포·보안·비용·규제가 동시에 요구됩니다.

영역운영 관점 질문
관측성요청 단위 상관 ID, 에러율/지연 분위수(p95/p99), 의존성 타임아웃·재시도가 대시보드에 보이는가
안전성입력 검증·권한·비밀·감사 로그가 코드 경로마다 일관적인가
신뢰성재시도는 멱등 연산에만 적용되는가, 서킷 브레이커·백오프·DLQ가 있는가
성능캐시 계층·배치 크기·커넥션 풀·인덱스·백프레셔가 데이터 규모에 맞는가
배포롤백 룬북, 카나리/블루그린, 마이그레이션 호환성·플래그가 문서화되어 있는가
용량피크 트래픽·디스크·파일 디스크립터·스레드 풀 상한을 주기적으로 검증하는가

스테이징은 데이터 양·네트워크 RTT·동시성을 가능한 한 프로덕션에 가깝게 맞추는 것이 재현율을 높입니다.


확장 예시: 엔드투엔드 미니 시나리오

「Kafka 완벽 가이드 | 이벤트 스트리밍·Producer·Consumer·Partition·Replication」을 실제 배포·운영 흐름으로 옮긴 체크리스트형 시나리오입니다. 도메인에 맞게 단계 이름만 바꿔 적용할 수 있습니다.

  1. 입력 계약 고정: 스키마·버전·최대 페이로드·타임아웃·에러 코드 표를 API 또는 이벤트 경계에 둔다.
  2. 핵심 경로 계측: 요청 ID, 단계별 지연, 외부 호출 결과 코드를 한 화면(로그+메트릭+트레이스)에서 추적한다.
  3. 실패 주입: 의존성 타임아웃·5xx·부분 데이터·락 대기를 스테이징에서 재현한다.
  4. 호환·롤백: 설정/마이그레이션/클라이언트 버전을 되돌릴 수 있는지(또는 피처 플래그) 확인한다.
  5. 부하 후 검증: 피크 대비 p95/p99, 에러율, 리소스 상한, 알림 임계값이 기대 범위인지 본다.

의사코드 스케치(프레임워크 무관)

handle(request):
  ctx = newCorrelationId()
  validated = validateSchema(request)        // 경계에서 거절
  authorize(validated, ctx)                  // 권한·테넌트
  result = domainCore(validated)             // 순수에 가까운 규칙
  persistOrEmit(result, idempotentKey)       // I/O: 멱등·재시도 정책
  recordMetrics(ctx, latency, outcome)
  return result

문제 해결(Troubleshooting)

증상가능 원인조치
간헐적 실패레이스, 타임아웃, 외부 의존성 불안정, DNS최소 재현 스크립트, 분산 트레이스·로그 상관관계, 재시도·서킷 설정 점검
성능 저하N+1, 동기 I/O, 락 경합, 과도한 직렬화, 캐시 미스프로파일러·APM으로 핫스팟 확인 후 한 가지씩 제거
메모리 증가캐시 무제한, 구독/리스너 누수, 대용량 버퍼, 커넥션 미반납상한·TTL·힙/FD 스냅샷 비교
빌드·배포만 실패환경 변수, 권한, 플랫폼 차이, lockfileCI 로그와 로컬 diff, 런타임·이미지 버전 핀
설정이 로컬과 다름프로필·시크릿·기본값, 지역 리전단일 소스(예: 스키마 검증된 설정)와 배포 매트릭스 표준화
데이터 불일치비멱등 재시도, 부분 쓰기, 캐시 무효화 누락멱등 키·아웃박스·트랜잭션 경계 재검토

권장 순서: (1) 최소 재현 (2) 최근 변경 범위 축소 (3) 환경·의존성 차이 (4) 관측으로 가설 검증 (5) 수정 후 회귀·부하 테스트.

자주 묻는 질문 (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 등 많은 기업에서 사용합니다.