학습일지/Kafka
[Kafka] RetryableTopic, DLT
Merge Log
2025. 9. 23. 14:12
테스트를 위한 간단한 시나리오
- 이메일 전송을 위한 서비스
- 이메일 발송 토픽 :
email.send - 이메일 정보 Producer
- 이메일 발송 Consumer

외부 API 혹은 서버 장애 등 다양한 상황에서 작업이 실패할 수 있다
이 경우 실패에 대해서 재시도 처리를 해야하는 것이 일반적이다
참고로 토픽은 key-value 형태로 생성된다
이메일 전송 같은 경우 재시도 처리를 위해 아래와 같이 코드를 작성하였다
@Service
public class EmailSendConsumer {
@KafkaListener(
topics = "email.send",
groupId = "email-send-group"
)
@RetryableTopic(
attempts = "5",
backoff = @Backoff(delay = 1000, multiplier = 2),
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(3000);
} catch (InterruptedException e) {
throw new RuntimeException("이메일 발송 실패");
}
System.out.println("이메일 발송");
}
}
그리고 실패 상황을 재현하기 위해 실패 조건을 추가하였다
재시도 정책은 실제 외부 API 의 지연시간 등을 고려하여 정책을 세워야 한다
현재는 테스트를 위해 5번의 재시도와 Exponential (지수적) 재시도 정책을 통해 재시도 로직을 처리하였다
Spring Kafka 에서 제공하는 재시도 처리
import org.springframework.kafka.annotation.RetryableTopic- Consumer 서버에서 재시도에 대해 아무런 코드를 작성하지 않더라도 자동으로 재시도를 처리한다 → 기본값으로 재시도(retry) 전략이 설정되어 있다
interval: 재시도를 하는 시간 간격 (ms)interval=0일 경우 실패하자마자 즉시 재시도 처리
maxAttempts: 최대 재시도 횟수maxAttempts=9재시도를 9번까지 시도
currentAttemps: 지금까지 시도한 횟수 (최소 시도 횟수 + 재시도 횟수)currentAttemps=10최소 시도를 1번 하고 재시도를 9번 시도
재시도를 여러번 했음에도 불구하고 처리에 실패하는 메시지는 어떻게 해야할까?
DLT (Dead Letter Topic)
- 오류로 인해 처리할 수 없는 메시지를 임시로 저장하는 토픽
- Kafka 에서는 재시도까지 실패한 메시지를 다른 토픽에 따로 저장해서 유실을 방지하고 후속 조치를 가능하게 만든다
DLT 를 사용함으로 얻는 이점
- 실패한 메시지를 DLT 에 따로 저장하므로 실패한 메시지를 유실하지 않는다
- 사후에 실패 원인 분석에 사용할 수 있다
- 처리되지 못한 메시지 또한 수동으로 처리해줄 수 있다
사실 Spring Kafka 는 @RetryableTopic 을 사용하면 자동으로 DLT 토픽을 생성하고 메시지를 전송해준다.
- 기본적으로 만드는 DLT 토픽 이름은
{기존 토픽명}-dlt형태로 지어진다 - 일관된 토픽이름을 위해 dlt 토픽 이름을 변경해줄 수 있다
@RetryableTopic(
attempts = "5",
backoff = @Backoff(delay = 1000, multiplier = 2),
dltTopicSuffix = ".dlt"
)

DLT 에 저장된 메시지 사후 처리 방식
- DLT 에 저장된 메시지를 로그 시스템에 전송하여 장애 원인을 추적할 수 있도록 한다
- DLT 에 메시지가 인식될 수 있도록 알림 발송을 한다
- 관리자는 알림을 확인 후 로그 시스템의 로그를 확인하여 사후 처리를 한다
수동 처리 방식 예시들
- 메시지를 원래 토픽으로 직접 보내기
- 장애가 일시적이고 지금은 해결된 경우 (서버 장애)
- 메시지 폐기
- 영구적으로 처리할 수 없는 메시지 (탈퇴한 사용자, 형식 오류)
- 단, 영구적으로 폐기하지 않고 혹시 모를 상황에 대비해 로그로 남겨둔다
- 잘못된 메시지 내용이 Kafka 에 들어가지 않게 Product 의 검증 로직 보완
- 원인 분석 후 검증 로직 보완
사후 처리를 위한 dlt 토픽을 소비하는 서비스를 작성한다
@Service
public class EmailSendDltConsumer {
@KafkaListener(
topics = "email.send.dlt",
groupId = "email-send-dlt-group"
)
public void consume(String message) {
// 1. 로그 시스템에 전송
System.out.println("로그 시스템에 전송 : " + message);
// 2. 알림 발송
System.out.println("Slack 알림 발송 : " + message);
}
}
email.send.dlt라는 토픽을 소비- 첫번째로 로그 시스템에 전송
- 두번째로 슬랙 알림 전송


테스트 코드