수강 신청 요청, 수강 신청 수락, 수강 신청 취소, 수강 신청 거절, 수강 변경 등의 로직이 완료된 후
해당하는 이벤트를 전송하는 것이 100% 성공한다고 보장할 수 없습니다.
도메인 로직과 이벤트 발행의 관점에서 볼 때
| 강의 로직 수행 | 이벤트 발행 | |
| 1 | 성공 | 성공 |
| 2 | 실패 | 성공 |
| 3 | 성공 | 실패 |
| 4 | 실패 | 실패 |
이렇게 경우의 수를 추려 볼 수 있습니다.
- 강의 로직 수행 성공, 이벤트 발행 성공
1번의 경우 강의 로직이 성공적으로 실행되어 트랜잭션 commit이 발생하고, 이후 이벤트가 발행됩니다.
TransactionalEventListener에서 해당 이벤트를 읽고 카프카로 메시지를 발행합니다. - 강의 로직 수행 실패, 이벤트 발행 성공
강의 로직은 실패했으나 이와 연관된 이벤트는 발행되는 상황인데, 이는 발생 가능성이 없습니다. 왜냐하면 강의 로직의 트랜잭션 commit이 실패하면 Spring의 TransactionalEventListener에서도 해당 이벤트를 리스닝하지 않기 때문입니다. - 강의 로직 수행 성공, 이벤트 발행 실패
강의 로직이 성공적으로 실행되고 트랜잭션 commit 도 이루어졌으나, 이후 TransactionalEventListener에서 카프카에 메시지를 발행하는 과정이 실패한 경우입니다. 강의 로직이 성공적으로 실행된 상황에서 카프카로의 메시지 발행이 실패하면 전체 서비스 간의 데이터 정합성이 불일치하게 됩니다. - 강의 로직 수행 실패, 이벤트 발행 실패
강의 로직이 실패하면 이와 연관된 이벤트 발행도 이루어지지 않는 경우입니다. 비록 로직은 실패했지만 전체적인 정합성은 깨지지 않았기 때문에 의도한 상황으로 봐도 무방합니다.
3번의 경우는 DB와 카프카 간 데이터 정합성 문제를 해결하기 위해 트랜잭션 아웃박스 패턴을 사용하여 해결하고자 했습니다.

예시로 강의 수정에 따른 알림 메시지를 각 학생들에게 발송하는 도메인 로직을 들겠습니다.
- 강의 수정 로직 수행
- ApplicationEventPublisher를 사용하여 강의 수정 이벤트 발행
- 도메인 로직과 동일한 트랜잭션 내에서, 발행할 이벤트를 outbox 테이블에 저장
- 발행된 이벤트를 outbox 테이블에 저장(성공도 실패도 아닌, 최초 초기화 상태)합니다.
- 트랜잭션 커밋 이후(AFTER_COMMIT, 여전히 동일한 트랜잭션), 이벤트 발행기가 강의 수정 이벤트를 카프카에 전송
- 트랜잭션 커밋 이후, 별도의 발행기가 outbox 테이블의 이벤트를 읽어 Kafka에 전송합니다.
- 전송 성공 시 해당 이벤트의 상태를 '발행 완료'로 업데이트하고, 실패 시에는 '발행 실패'로 기록합니다.
- 카프카 컨슈머는 해당 이벤트가 발행되었음을 확인 후 해당 강의를 수강하고 있는 학생들에게 알림 메시지를 전송합니다.
- 발행에 실패한 이벤트는 일정 주기마다 이벤트 재처리기에서 조회하여 재전송을 시도
outbox 테이블에 발행할 이벤트 정보를 기록해 놨기 때문에 일정 주기마다 동작하는 배치 등을 통해서 카프카로 메시지를 발행하는 로직을 다시 실행할 수도 있습니다.
트랜잭션 커밋 후 이벤트를 발행하는 방법
트랜잭션 커밋 이후, 이벤트를 발행할 때 다음과 같은 방법을 사용할 수 있습니다.
결론적으로는
- 메인 로직의 트랜잭션과 외부 시스템(Kafka)으로의 이벤트 발행을 분리하기 위해 @Async를 사용한 비동기 방식을 채택했습니다.
- 비동기 쓰레드 내에서는 Kafka Producer의 동기(Blocking) API를 호출하여 메시지 전송의 성공 여부를 명확히 확인합니다.
- 그 결과를 outbox 테이블에 반영(성공 또는 실패)합니다.
'SSA > Back' 카테고리의 다른 글
| [SSA] Kafka Consumer가 멱등성을 가질 수 있도록 설계 (0) | 2025.09.09 |
|---|---|
| [SSA] 수강 기능, 알림 기능을 이벤트 기반으로 분리 (0) | 2025.09.09 |
| [SSA] 비동기 이벤트 발행기 테스트 (1) | 2025.09.08 |
| [SSA] 알림 모듈 구조 개선 및 간단한 동시성 제어 .. (2) | 2025.08.30 |
| [SSA] 새로운 트랜잭션을 만들면 데드락이 발생하는 것을 테스트해 보자 (0) | 2025.08.29 |
