티스토리 뷰
반응형
Kafka Outbox Pattern with Spring Event - 주문 예제
Kafka Outbox 패턴을 Spring Boot와 Event 기반으로 구현하며,
주문(Order) 도메인을 중심으로 트랜잭션 안정성 보장과 Kafka 전송 재시도 가능성을 확보하는 방법을 설명합니다.
개요
Kafka Outbox 패턴은 DB 트랜잭션과 Kafka 전송 간의 일관성을 유지하기 위한 대표적인 방법입니다.
Spring의 @TransactionalEventListener
를 활용하여 도메인 이벤트를 트랜잭션 내부에서 발행하고,
트랜잭션 커밋 이후 Kafka로 안전하게 발송되도록 구성했습니다.
Kafka Outbox 패턴
Kafka Outbox 패턴은 DB 트랜잭션과 Kafka 메시지 발행 사이의 일관성을 보장하는 구조입니다.
직접 Kafka를 발행하는 대신, 먼저 Outbox 테이블에 메시지를 저장하고
트랜잭션 커밋 이후 별도 프로세스(리스너 또는 스케줄러)에서 Kafka로 전송합니다.
장애 발생 시 메시지를 유실하지 않고 재처리할 수 있는 신뢰성 패턴입니다.
CDC(Change Data Capture)
CDC는 DB의 변경 로그를 추적하여 Kafka로 보내는 비침투형 이벤트 수집 방식입니다.
반면, Outbox 패턴은 애플리케이션 내부에서 이벤트를 직접 저장하고 처리합니다.
애플리케이션 코드에서 제어 가능하며, 보다 명시적이고 확장성 있는 흐름을 구성할 수 있다는 장점이 있습니다.
핵심 흐름 요약
- 사용자가 주문을 요청한다
- OrderService에서 주문을 저장하고 이벤트를 발행한다
- 이벤트 리스너가 Outbox 테이블에 이벤트를 저장한다 (BEFORE_COMMIT)
- 트랜잭션 커밋 후 Kafka로 메시지를 발송한다 (AFTER_COMMIT)
- Kafka 전송 성공 시 Outbox 상태를 업데이트한다 (sent = true)
- OutboxScheduler가 주기적으로 발행되지 못한 메시지를 재전송한다
주요 클래스
1. OrderService
@Service
@RequiredArgsConstructor
public class OrderService {
...
@Transactional
public Long createOrder(Long productId, int quantity) {
...
publisher.publishEvent(event); // Spring 이벤트 발행
return orderSave.getId();
}
}
2. OutboxBeforeCommitListener
@Component
@RequiredArgsConstructor
public class OutboxBeforeCommitListener {
...
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
public void saveOutbox(OrderCreatedEvent event) {
...
outboxRepository.save(outbox);
}
}
3. OutboxAfterCommitListener + OutBoxPublishService
@Component
@RequiredArgsConstructor
public class OutboxAfterCommitListener {
...
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void publishToKafka(OrderCreatedEvent event) {
outBoxPublishService.publish(event);
}
}
@Slf4j
@RequiredArgsConstructor
@Service
public class OutBoxPublishService {
...
public void publish(OrderCreatedEvent event) {
...
kafkaTemplate.send("order.created", outbox.getPayload())
.whenComplete((result, ex) -> {
if (ex == null) {
outbox.completeSent();
eventOutboxRepository.save(outbox);
}
});
}
}
4. OutboxScheduler
@Slf4j
@Component
@RequiredArgsConstructor
public class OutboxScheduler {
...
@Scheduled(fixedDelay = 5000)
@Transactional
public void publishPendingOutboxEvents() {
...
kafkaTemplate.send("order.created", outbox.getPayload());
outbox.completeSent();
}
}
트랜잭션 흐름 정리
- OrderService는 트랜잭션 내에서 주문 저장과 이벤트 발행을 동시에 처리
- BEFORE_COMMIT 리스너는 트랜잭션 커밋 전에 Outbox 테이블에 기록
- AFTER_COMMIT 리스너는 커밋 후 Kafka로 메시지를 발행
- 발행 성공 시 sent = true로 상태를 갱신
Kafka에서 메시지 순서 보장 방법
Kafka는 파티션 내부에서만 순서를 보장합니다. 다음 조건을 만족해야 순서가 유지됩니다:
- KafkaTemplate.send(topic, key, value) 형태로 동일한 키 사용
- 동일 키는 항상 동일 파티션에 할당
- Kafka는 해당 파티션의 메시지를 순서대로 소비하게 보장
마무리
시스템 규모가 크고, 메시지 유실 방지 및 재처리가 중요한 경우
Kafka + Outbox 패턴은 안정성과 신뢰성을 높이는 효과적인 방식입니다.
전체 소스 코드: https://github.com/devHjlee/kafka-outbox-pattern.git
반응형
'Spring' 카테고리의 다른 글
Spring Batch + Quartz 통합 배치 스케줄러: QuartzBatchControl 프로젝트 소개 (1) | 2025.05.31 |
---|---|
Cursor AI로 게시판 풀스택 개발 실험 후기 (0) | 2025.05.11 |
Kafka와 Redis로 만든 선착순 이벤트 시스템 구조 분석 (1) | 2025.05.05 |
Spring Boot 모니터링 구축 (0) | 2024.04.09 |
SpringBoot Gradle (0) | 2024.03.26 |
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- cursor ai
- 배치 모니터링
- oubox pattern
- 스케줄링 시스템
- cursor ai crud
- kafka srping event
- tomcat gzip
- Spring boot Actuator
- spring boot jpa crud
- SpringBatch 5.1.1
- spring boot jpa
- actuator prometheus grafana
- 잡 스케줄링
- custom Item writer
- quartz spring batch
- no `meta.properties` found in
- Enum equals
- cursor ai 프롬프트
- Enum Equals ==
- quartz 실무 적용
- cursor ai 프로젝트
- 배치 관리 ui
- kafka redis
- kafka oubox
- cursorAI
- CompositeItemWriter
- Spring Actuator
- Enum ==
- spring security
- JdbcBatchItem
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
글 보관함