항해 백엔드8주차 회고(WIL): 도메인 이벤트 기반 트랜잭션 분리

JUNYOUNG·2025년 5월 23일

항해 플러스 백엔드

목록 보기
13/14
post-thumbnail

이번 주 목표

이번 주의 핵심 목표는 핵심 도메인의 상태 전이를 기준으로 트랜잭션 경계를 재설계하고, 이벤트 기반으로 도메인 간 협업을 비동기화하여 실시간성과 복원력을 동시에 확보하는 구조를 구현 및 검증하는 것이었다.

이를 위해 다음과 같은 도메인 이벤트 체계와 상태 기반 설계를 적용하고, 이벤트 실패 보상 흐름까지 포함한 구조적 실험을 진행했다:

  • 결제 성공 → 주문 CONFIRMED → 상품 랭킹 등록 → 외부 전송 까지 이벤트 체이닝 구현
  • 잔액 충전 → 잔액 이력 저장 로직을 도메인 이벤트로 분리
  • 주문 확정 실패 시 OrderConfirmationFailedEvent 로 보상 트리거 설계
  • @TransactionalEventListener(AFTER_COMMIT) + @Async 기반 비동기 리스닝 구조 적용
  • Awaitility 기반의 E2E 통합 테스트로 전체 이벤트 체이닝 검증

문제 정의

1. 결제/주문 트랜잭션이 하나의 흐름에 결합되어 실패 전파가 광범위함

  • 결제 성공 후 주문 상태 변경, 상품 랭킹 기록, 외부 전송까지 모두 한 흐름에 존재
  • 중간 하나라도 실패하면 전체 트랜잭션이 롤백되어 UX와 데이터 무결성 모두에 악영향

2. 부가 로직이 핵심 로직 내부에서 처리되어 트랜잭션이 길어짐

  • 판매량 기록이나 외부 연동 요청 같은 부가 로직이 핵심 결제/주문 트랜잭션에 포함됨
  • I/O 작업 포함 시 타임아웃, 장애 전파, 트랜잭션 병목 리스크 증가

해결 전략 및 설계 방식


🧾 STEP15 – 도메인 이벤트 기반 관심사 분리

설계 목표

  • 결제, 주문, 잔액, 통계, 외부 전송 등 도메인을 상태 전이 기반으로 명확히 분리
  • 후속 처리는 모두 이벤트 리스너에서 처리하여 핵심 트랜잭션은 작게, 처리는 분산되도록 설계

이벤트 체이닝 구조

1. 결제 성공 시 → OrderConfirmedEvent 발행
2. 주문 CONFIRMED → ProductSalesRankRecordedEvent, OrderExportRequestedEvent 발행
3. 각 이벤트는 AFTER_COMMIT + Async 리스너에서 소비
4. 실패 시 OrderConfirmationFailedEvent 로 보상 이벤트 분리

테스트 전략

통합 테스트 구조

  • Awaitility를 활용한 비동기 리스너 대기
  • 실제 이벤트 흐름이 순차적으로 실행되는지, 비동기 리스너가 정상 동작하는지 확인
테스트명검증 포인트
charge_shouldRecordBalanceHistory잔액 충전 → 이력 저장 도메인 이벤트 분리 검증
should_process_payment_successfully_and_confirm_order결제 성공 → 주문 상태 변경 → 통계/외부 전송 이벤트 체이닝 정상 동작

📌 통합 테스트 실행 로그에서 도메인 이벤트 등록, 리스너 실행 로그까지 추적


인사이트

  • 핵심 도메인은 상태 전이만 책임지고, 후속 로직은 이벤트로 연결할 때 구조가 명확해진다.
  • @TransactionalEventListener(AFTER_COMMIT) + @Async 구조는 핵심 트랜잭션과 후속 I/O 작업을 분리하면서도 흐름을 연결시켜준다.
  • 이벤트가 늘어나면 복잡도도 함께 증가한다. 따라서 어떤 이벤트는 발행하지 말고 도메인 내부로 처리해야 할지 선별 기준이 필요하다.
  • 실패 상황에 대비해 XXXFailedEvent를 별도 정의하고 로깅하는 방식은 Kafka가 없는 상황에서도 복원력을 확보하는 현실적인 대안이 될 수 있다.
  • 이벤트 기반 구조는 단순히 메시지를 넘기는 수준이 아니라, 도메인의 책임을 상태 전이 단위로 구조화할 수 있는 수단이기도 하다.

요약 정리

흐름핵심 설계기술
결제 성공 → 주문 확정상태 기반 이벤트 트리거OrderConfirmedEvent
주문 확정 → 통계/전송이벤트 체이닝ProductSalesRankRecordedEvent, OrderExportRequestedEvent
실패 보상 처리실패 이벤트 발행 및 로깅OrderConfirmationFailedEvent
트랜잭션 경계 최소화핵심만 @Transactional, 나머지는 이벤트 리스너AFTER_COMMIT + @Async
통합 테스트Awaitility 기반 이벤트 흐름 검증E2E 시나리오

지난 목표 회고

지난 주 목표는 도메인 이벤트 기반 설계 적용 및 트랜잭션 경계 분리였으며, 아래 항목을 통해 실질적으로 달성했다.

  • 모든 상태 전이 기반 도메인 이벤트 등록 완료
  • 통합 테스트로 트랜잭션 내/외 경계 분리 흐름 검증 완료
  • 이벤트 실패 대비 설계 (OrderConfirmationFailedEvent) 도입

다만 Kafka나 Outbox 기반의 실제 MSA 구조에서의 전파 설계는 아직 적용하지 못했다. 실패 이벤트를 DB 테이블에 저장하거나 Retry Queue를 활용한 자동 복구 설계는 향후 과제로 남는다.

profile
Onward, Always Upward - 기록은 성장의 증거

2개의 댓글

comment-user-thumbnail
2025년 5월 24일

안녕하세요 준영님 5팀 최은강입니다. 성지순례하다가 wil까지 찾아보게됐는데요. 와 과제도 벅찬데.. WIL까지 너무 꼼꼼하게 하신거같아서 wil을 등한시한 제자신을 반성하게되네요 ㅠㅠ.. 블로그글도 PR도 참 너무 잘쓰시고 고민이 깊은걸 느껴졌습니다. 경력이 1년6개월이라고하셨지만.. 정말 1년6개월 경력이 의심할정도로 너무 실력자셔서 놀랐습니다.
경력만으로 개발능력과 문제해결능력을 평가의 잣대로 할수없단걸, 준영님의 글과 PR, 코드를 통해 깨닫게됩니다.

준영님은 과제를 제출을떠나서 뭔가 해당주차의 본질을 파악하시는거같단 생각이들어서 감탄하면서 봤습니다.
과제통과에 BP에 집착한 제자신을 부끄러울정도네요. 과제 통과와 BP, 뱃지때문에 나를 갉아가면서 지쳐가던중에 나는 무엇을 위해서 뭔가 중요한게 빠진거같았고, 난 무엇을 위해 항해를 했지? 라는 생각이 절로들더군요.
wil를 보고서처럼 꾸준히 작성하신 성실함과 빠르게 수용하려는 집중력은 정말 부럽고 저도 닮아지고싶단 생각이 듭니다.

매번 해당주차에 대한 과정을 글로 기록하는모습이 인상깊어서 코멘트를 남겻네요.
저도 늦었지만 그래도 준영님글과 마인드를 참고하여 글을 작성해나가고싶어집니다.ㅎㅎ

1개의 답글