JSCODE Logo
프로그래밍 과외블로그후기멘토진
회사명 : JSCODE대표 : 박재성사업자 등록번호 : 244-22-01557통신판매업 : 제 2023-인천미추홀-0381 호
학원 명칭 : 제이에스코드(JSCODE)원격학원학원설립ㆍ운영 등록번호 : 제6063호

서울특별시 구로구 경인로 20가길 11(오류동, 아델리아)

Copyright ⓒ 2025 JSCODE - 최상위 현업 개발자들의 프로그래밍 교육 All rights reserved.

이용약관개인정보처리방침
← 블로그 목록으로 돌아가기

[실습] Spring Boot로 Kafka에서 재시도조차 실패한 메시지를 따로 보관하기 (DLT, Dead Letter Topic)

JSCODE 박재성
JSCODE 박재성
2025-12-06
author
JSCODE 박재성
category
Kafka
createdAt
Dec 6, 2025
series
실전에서 바로 써먹는 Kafka 입문
slug
practice-dead-letter-topic-dlt
type
post
updatedAt
Dec 6, 2025 05:39 AM
👨🏻‍🏫
이전 강의에서는 Kafka에서 비동기적으로 메시지를 처리하고, 재시도(Retry) 정책을 통해 메시지 처리에 실패했을 때의 대응법을 배웠다. 그러나 재시도를 여러 번 했음에도 불구하고 처리에 실패하는 메시지는 어떻게 해야 할까?
이런 메시지를 아무런 처리 없이 그대로 버리면, 사용자 입장에서는 “성공했다고 생각했지만 실제론 실패한 작업”으로 남게 된다. 이럴 때는 Dead Letter Topic(DLT)라는 별도 토픽을 활용하여, 재시도까지 실패한 메시지를 안전하게 보관하고 나중에 관리자가 확인해서 수동으로라도 처리할 수 있도록 구성할 수 있도록 해야 한다.
 

✅ Dead Letter Topic이란?

DLT(Dead Letter Topic)는 오류로 인해 처리할 수 없는 메시지를 임시로 저장하는 토픽이다. Kafka에서는 재시도까지 실패한 메시지를 다른 토픽에 따로 저장해서 유실을 방지하고 후속 조치를 가능하게 만든다.
 
그럼 DLT는 왜 사용하는 걸까?
 
  1. 실패한 메시지를 DLT 토픽에 저장해놓기 때문에, 실패한 메시지가 유실되는 걸 방지할 수 있다.
  1. DLT 토픽에 실패한 메시지가 저장되어 있기 때문에, 사후에 실패 원인을 분석할 수 있다.
  1. DLT 토픽에 실패한 메시지가 저장되어 있기 때문에, 처리되지 못한 메시지를 수동으로 처리할 수 있다.
 
 

✅ DLT를 활용해 재시도에 실패한 메시지 따로 보관하기

사실 Spring Kafka는 @RetryableTopic을 사용하면 자동으로 DLT 토픽을 생성하고 메시지를 전송해준다. 기본적으로 만드는 DLT 토픽 이름은 {기존 토픽명}-dlt 형태로 지어진다. 일관적인 DLT 토픽 이름을 위해 직접 DLT 토픽명을 별도로 다시 설정해주자.
 
  1. Consumer 코드 수정하기
    1. EmailSendConsumer
      @Service public class EmailSendConsumer { @KafkaListener( topics = "email.send", groupId = "email-send-group" // 컨슈머 그룹 이름 ) @RetryableTopic( // 총 시도 횟수 (최초 시도 1회 + 재시도 4회) attempts = "5", // 재시도 간격 (1000ms -> 2000ms -> 4000ms -> 8000ms 순으로 재시도 시간이 증가한다.) backoff = @Backoff(delay = 1000, multiplier = 2), // DLT 토픽 이름에 붙일 접미사 dltTopicSuffix = ".dlt" ) public void consume(String message) { System.out.println("Kafka로부터 받아온 메시지: " + message); EmailSendMessage emailSendMessage = EmailSendMessage.fromJson(message); // 잘못된 이메일 주소일 경우 실패 가정 if (emailSendMessage.getTo().equals("fail@naver.com")) { System.out.println("잘못된 이메일 주소로 인해 발송 실패"); throw new RuntimeException("잘못된 이메일 주소로 인해 발송 실패"); } // ... 실제 이메일 발송 로직은 생략 ... try { Thread.sleep(10000); // 이메일 발송을 하는 데 10초가 걸린다고 가정 } catch (InterruptedException e) { throw new RuntimeException("이메일 발송 실패"); } System.out.println("이메일 발송 완료"); } }
       
  1. 서버 실행 후 API 요청 보내기
    1. Consumer 서버를 재실행한 이후에 API 요청을 보내보자. 그러고 5번 시도 동안 전부 실패할 때까지 기다리자.
      notion image
      notion image
      마지막 부분의 로그를 보면 email.send.dlt 토픽으로 메시지가 잘 전달된 걸 알 수 있다.
       
  1. CLI로 정말 DLT 토픽이 잘 생성됐는 지 확인해보기
    1. # 토픽 전체 조회 $ bin/kafka-topics.sh \ --bootstrap-server localhost:9092 \ --list
      notion image
      생성하고자 하는 DLT 토픽(email.send.dlt)이 잘 생성된 걸 확인할 수 있다. email.send-retry-1000, email.send-retry-2000 와 같은 토픽은 재시도를 하면서 생긴 토픽들이고, email.send-dlt라는 토픽은 DLT의 기본 설정값({기존 토픽명}-dlt)으로 인해 생겼던 토픽이다.
       
      # 토픽 내부 메시지 전체 조회 $ bin/kafka-console-consumer.sh \ --bootstrap-server localhost:9092 \ --topic email.send.dlt \ --from-beginning
      notion image
      재시도에 실패한 메시지가 email.send.dlt 토픽에 저장되어 있는 걸 확인할 수 있다.
 
 

✅ 그림으로 이해하기

notion image
Conusmer 서버가 메시지를 처리하다가 실패하면 정해진 횟수까지 재시도를 한다. 여기서 끝까지 재시도 처리에 실패한 메시지는 DLT 토픽으로 옮겨서 보관하게 된다.
 
 
👨🏻‍🏫
이렇게 재시도(Retry)조차 실패한 메시지를 DLT 토픽에 따로 보관하는 것까지 완료했다. 그럼 이 실패한 메시지에 대해서 조치를 취해야 하는데, 그 방법에 대해서는 다음 강의에서 알아보자.
 
author
JSCODE 박재성
category
Kafka
createdAt
Dec 6, 2025
series
실전에서 바로 써먹는 Kafka 입문
slug
type
series-footer
updatedAt
Dec 6, 2025 05:39 AM
📎
이 글은 실전에서 바로 써먹는 Elasticsearch 입문 (검색 최적화편) 강의의 수업 자료 중 일부입니다.