| 일 | 월 | 화 | 수 | 목 | 금 | 토 | 
|---|---|---|---|---|---|---|
| 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 | 
- 스프링부트
 - 코드업
 - 스프링 부트
 - 쿠버네티스
 - 개발
 - Spring Data JPA
 - Spring Boot
 - Kafka
 - 알고리즘
 - Elasticsearch
 - aws
 - 클라우드 컴퓨팅
 - 오일러프로젝트
 - gcp
 - 백준
 - 머신러닝
 - 카프카
 - Spring
 - 프로그래밍문제
 - DFS
 - Apache Kafka
 - JPA
 - 클라우드
 - Docker
 - 자료구조
 - 로드밸런서
 - VPC
 - 스프링
 - 백트래킹
 - springboot
 
- Today
 
- Total
 
GW LABS
Spring Kafka 완벽 가이드: KafkaTemplate와 @KafkaListener, 재시도 및 Dead Letter Queue 활용법 본문
Spring Kafka 완벽 가이드: KafkaTemplate와 @KafkaListener, 재시도 및 Dead Letter Queue 활용법
GeonWoo Kim 2025. 8. 15. 09:05
Spring Kafka 완벽 가이드: KafkaTemplate와 @KafkaListener, 재시도 및 Dead Letter Queue 활용법
서론
Apache Kafka는 대규모 실시간 데이터 스트리밍과 비동기 메시징을 처리하는 데 최적화된 분산 이벤트 스트리밍 플랫폼입니다.
Spring Kafka는 이러한 Kafka의 기능을 Spring 환경에 자연스럽게 통합하여 개발자가 간결한 코드로 안정적이고 확장 가능한 메시지 기반 애플리케이션을 만들 수 있도록 지원합니다.
이 글에서는 Spring Kafka의 핵심 개념, KafkaTemplate 사용법, @KafkaListener 활용법과 주의사항, 그리고 재시도 로직 및 Dead Letter Queue(DLQ) 적용 방법을 실무 중심으로 정리합니다.
본론
1. Spring Kafka 핵심 개념
Spring Kafka의 주요 컴포넌트는 다음과 같습니다.
- KafkaTemplate
Kafka 프로듀서 역할. 메시지를 전송하고 결과를 비동기/동기로 받을 수 있음. - @KafkaListener
Kafka 토픽을 구독하고 메시지를 처리하는 어노테이션 기반 컨슈머. - Listener Container
메시지 리스너 실행 환경을 관리하며 스레드, 오프셋 커밋, 재시도 정책 등을 제어. - ErrorHandler / SeekToCurrentErrorHandler
메시지 처리 실패 시 재시도 및 DLQ 라우팅 설정 가능. 
기본 의존성 예시
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>
2. KafkaTemplate 사용법
KafkaTemplate은 메시지를 Kafka로 전송하는 핵심 클래스입니다.
프로듀서 설정
@Configuration
public class KafkaProducerConfig {
    @Bean
    public ProducerFactory<String, String> producerFactory() {
        Map<String, Object> config = new HashMap<>();
        config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
        config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        return new DefaultKafkaProducerFactory<>(config);
    }
    @Bean
    public KafkaTemplate<String, String> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }
}메세지 전송
@Service
public class KafkaProducerService {
    private final KafkaTemplate<String, String> kafkaTemplate;
    public KafkaProducerService(KafkaTemplate<String, String> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }
    public void send(String topic, String message) {
        kafkaTemplate.send(topic, message)
            .addCallback(
                success -> System.out.println("전송 성공: " + success.getRecordMetadata()),
                failure -> System.err.println("전송 실패: " + failure.getMessage())
            );
    }
}@KafkaListener 사용법과 주의사항
@KafkaListener는 Kafka 메시지 소비를 단순화합니다.
컨슈머 설정
@Configuration
public class KafkaConsumerConfig {
    @Bean
    public ConsumerFactory<String, String> consumerFactory() {
        Map<String, Object> config = new HashMap<>();
        config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
        config.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group");
        config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        return new DefaultKafkaConsumerFactory<>(config);
    }
    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<String, String> factory =
            new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        return factory;
    }
}메세지 소비
@Service
public class KafkaConsumerService {
    @KafkaListener(topics = "test-topic", groupId = "my-group")
    public void consume(String message) {
        System.out.println("수신 메시지: " + message);
    }
}주의사항
- groupId 필수: 같은 groupId를 사용하는 컨슈머끼리는 메시지를 파티션 단위로 분배 처리.
 - 파티션 수 고려: 병렬 컨슈머 수는 파티션 수 이상이 되어도 추가 컨슈머는 대기 상태.
 - 에러 처리 필수: 예외 발생 시 무한 재시도 방지를 위해 재시도 및 DLQ 설정 필요.
 - 오프셋 관리 전략: auto-commit 또는 수동 커밋 전략을 요구사항에 맞게 선택.
 
4. 재시도 로직과 Dead Letter Queue 적용
메시지 처리 중 오류가 발생하면 무한 재시도를 피하기 위해 재시도 횟수 제한과 DLQ를 설정하는 것이 권장됩니다.
재시도 & DLQ 설정 예시
@Configuration
public class KafkaErrorHandlerConfig {
    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<String, String> factory =
            new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        // SeekToCurrentErrorHandler: 재시도 후 DLQ로 전송
        DeadLetterPublishingRecoverer recoverer =
            new DeadLetterPublishingRecoverer(kafkaTemplate(), (record, ex) -> 
                new TopicPartition(record.topic() + ".DLT", record.partition()));
        factory.setErrorHandler(new SeekToCurrentErrorHandler(recoverer, 3)); // 최대 3회 재시도
        return factory;
    }
    @Bean
    public ProducerFactory<String, String> producerFactory() {
        Map<String, Object> config = new HashMap<>();
        config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
        config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
        return new DefaultKafkaProducerFactory<>(config);
    }
    @Bean
    public KafkaTemplate<String, String> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }
}Dead Letter Queue 운영 팁
- .DLT 접미사를 사용해 DLQ 토픽 이름을 명확하게 구분.
 - DLQ 데이터를 주기적으로 모니터링하여 장애 원인을 분석.
 - 필요 시 DLQ 메시지를 재처리하는 별도 컨슈머 운영.
 
결론
Spring Kafka는 복잡한 Kafka API를 추상화하여 개발자가 간결하게 메시지 기반 애플리케이션을 개발할 수 있도록 돕습니다.
KafkaTemplate과 @KafkaListener를 올바르게 조합하고, 재시도 및 DLQ 설정을 적용하면 안정적인 이벤트 스트리밍 아키텍처를 구축할 수 있습니다.
요약:
Spring Kafka를 이해하고 재시도·DLQ 패턴을 적용하면, 실무에서 안정적이고 예측 가능한 메시징 시스템을 구현할 수 있습니다.
'Programming > Java' 카테고리의 다른 글
| Spring Rest API 캐싱전략 완벽 가이드: @Cacheable과 CacheManager 활용법 (3) | 2025.08.18 | 
|---|---|
| Spring Batch 사용법: 대용량 데이터 처리를 위한 실무 가이드 (3) | 2025.08.16 | 
| Spring의 기본 개념 총정리: DI, IoC, AOP 완벽 가이드 (0) | 2025.08.12 | 
| JVM 완벽 가이드: JVM 구조, JVM 역할, 동작 원리 총정리 (2) | 2025.08.09 | 
| Spring Boot Multi-threaded JDBCTemplate (0) | 2021.12.15 |