티스토리 뷰

728x90

 

 

아래 글은 Kafka Consumer가 애플리케이션에서 담당하는 역할부터 컨슈머 그룹으로 확장되는 병렬 처리 모델, 그리고 오프셋 관리 전략(자동·수동 커밋)을 체계적으로 설명한 전문가용 가이드입니다. Java SDK와 kafka-python을 이용해 바로 실행 가능한 최소-예제 코드를 제공하며, 각 설정이 실전에 미치는 영향과 주의점을 최신 문서와 커뮤니티 사례를 근거로 정리했습니다. 이 글 한 편이면 “왜 이렇게 설계됐고, 어떻게 쓰면 안전·고성능인가?” 를 모두 이해하실 수 있습니다.

1 | 컨슈머의 핵심 역할

Kafka Consumer는 브로커에 저장된 파티션 로그를 읽어 비즈니스 로직으로 전달하는 계산 계층입니다. poll() 루프 안에서 레코드를 가져오고, 오프셋 위치 를 중심으로 재시작·장애 복구·재처리를 제어합니다. “스토리지는 브로커, 컴퓨트는 컨슈머” 라는 Kafka 분리 원칙의 출발점이 바로 이 컴포넌트입니다.

2 | 컨슈머 그룹: 왜 중요한가?

2-1. 스케일-아웃과 고가용성

동일 group.id 의 컨슈머를 여러 대 띄우면 파티션이 자동 분배(Assign)되어 병렬 처리량이 선형 확장됩니다. 새 컨슈머가 합류·이탈할 때 리밸런스 프로토콜이 동작해 부담을 고르게 재조정합니다. 

2-2. Exactly-Once 의 단서

컨슈머 그룹은 “파티션당 단일 컨슈머” 원칙 덕분에 메시지 순서를 유지합니다. 따라서 멱등 프로듀서와 결합하면 파티션 단위에서 정확-한-번 처리가 가능합니다. 

3 | 오프셋 관리 전략

전략 특징 설정 키

자동 커밋 주기(auto.commit.interval.ms)마다 브로커의 __consumer_offsets 토픽에 오프셋 저장 → 구현 단순, at-least-once enable.auto.commit=true (기본) 
수동(동기) 커밋 처리 완료 후 commitSync() 호출 → 실패 시 재시도, 데이터 유실 최소화 enable.auto.commit=false 
수동(비동기) 커밋 commitAsync() → 지연·TPS 향상, 실패 재시도는 직접 처리 동일 

힌트 – auto.offset.reset
초기 오프셋이 없을 때 소비 시작 위치를 결정합니다. earliest = 0부터, latest = 끝에서. 실시간 서비스는 latest, 배치 리플레이는 earliest를 주로 사용합니다. 

내부적으로 커밋된 오프셋은 __consumer_offsets 토픽에 칸별 키-값으로 저장되며, 브로커 장애 시에도 동일 클러스터 안에서 안전하게 복구됩니다.

4 | Java 컨슈머 최소 예제

Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "blog-demo");
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");   // 수동 커밋
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,
          "org.apache.kafka.common.serialization.StringDeserializer");
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
          "org.apache.kafka.common.serialization.StringDeserializer");
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); // 처음이면 0부터

try (KafkaConsumer<String,String> c = new KafkaConsumer<>(props)) {
    c.subscribe(List.of("quickstart"));
    while (true) {
        ConsumerRecords<String,String> recs = c.poll(Duration.ofMillis(500));
        for (ConsumerRecord<String,String> r : recs) {
            // TODO: 비즈니스 처리
        }
        c.commitSync();                 // 동기 커밋으로 정확성 확보
    }
}

commitSync() 는 네트워크 오류가 나면 재시도하며, 리밸런스 직전에도 자동 호출하여 중복 소비를 줄여 줍니다. 

5 | Python (kafka-python) 컨슈머 예제

from kafka import KafkaConsumer
import json, logging

consumer = KafkaConsumer(
    'quickstart',
    bootstrap_servers='localhost:9092',
    group_id='blog-demo',
    enable_auto_commit=False,
    auto_offset_reset='earliest',
    key_deserializer=lambda k: k.decode() if k else None,
    value_deserializer=lambda v: json.loads(v.decode())
)

for msg in consumer:
    process(msg.value)     # 비즈니스 로직
    consumer.commit()      # 기본은 동기; 별도 쓰레드에서 비동기 커밋도 가능

kafka-python 은 commit_async() 도 지원하니 고TPS 환경에서 고려하세요. 

6 | 모범 사례 & 트러블슈팅

  1. 리밸런스 최적화 – 메시지 처리 시간이 길다면 max.poll.interval.ms 를 충분히 늘리고, cooperative-sticky 파티셔너로 재조정 비용을 줄입니다.
  2. 오프셋-업데이트 패턴 – 데이터베이스 트랜잭션과 함께 커밋하려면 Outbox 패턴 또는 브로커 트랜잭션 API를 활용해 exactly-once 를 완성합니다. 
  3. dead-letter 대응 – 역직렬화 예외 등 비즈니스 에러는 재처리 내역 을 별도 토픽으로 보내고, 원본 토픽 오프셋은 정상 커밋해 파이프라인 정체를 막습니다.

7 | 맺음말

컨슈머는 Kafka 파이프라인에서 “읽기·계산·커밋” 3단계를 구현하며, 컨슈머 그룹을 통해 무중단 스케일-아웃·장애 복구 능력을 갖춥니다. 자동 커밋은 학습·프로토타이핑에 빠르고, 실전 서비스는 수동 커밋 + 예외 관리 로 정확성을 확보하는 것이 정석입니다. 본문의 Java·Python 예제를 토대로 자신만의 컨슈머 모듈을 템플릿화해 두면, 어떤 스트리밍 프로젝트에서도 안정적인 데이터 소비를 시작할 수 있습니다. 

728x90
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/07   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함