어떤 문제를 풀었나요?
물류 플랫폼의 주문은 재고 확인, 쿠폰, 결제, 배송, 알림 등 여러 서비스와 연결됩니다. 동기 호출 구조에서는 연계 서비스가 추가될수록 주문 처리의 의존 관계가 복잡해졌고, 알림 실패가 주문 확정에 영향을 주는 문제도 있었습니다.
제가 맡은 일
주문 생성·수정·취소·삭제 API를 담당했습니다. 복잡한 주문 생성 흐름을 Facade로 정리하고, FeignClient 중심의 호출을 Kafka 기반 이벤트 통신으로 전환하는 작업을 맡았습니다.
- 주문 도메인과 서비스 간 이벤트 발행·소비 흐름 설계
- 이벤트 소비 재시도와 DLT 처리
- Transactional Outbox를 통한 이벤트 저장·발행 분리
- 실패 시 재고·결제·쿠폰을 되돌리는 보상 트랜잭션 구현
업무 데이터와 이벤트를 함께 기록하기
DB 변경과 메시지 발행은 서로 다른 시스템에서 이루어집니다. DB에 변경 사항만 남고 이벤트를 발행하지 못하는 상황을 줄이기 위해 업무 데이터와 Outbox 이벤트를 같은 DB 트랜잭션에서 저장하고, 별도의 발행 과정으로 연결했습니다.
발행 재시도에는 지수 백오프와 Jitter를 적용하고, Publishing 상태에 오래 머무른 이벤트를 복구하는 작업을 구성했습니다.
실패를 알리는 이벤트까지 사라졌을 때
다른 서비스에서 재고 선점이 완료된 뒤 주문 생성이 실패하면, 재고를 복원할 실패 이벤트가 필요했습니다. 그런데 예외와 함께 주문 트랜잭션이 롤백되면서 catch 블록에서 저장한 실패 이벤트도 사라졌습니다.
실패 이벤트 저장에 별도의 트랜잭션 경계를 두고 REQUIRES_NEW를 적용했습니다. 이 경험을 통해 정상 흐름뿐 아니라 실패를 전달하는 과정도 독립적으로 설계해야 한다는 점을 배웠습니다.
AI를 활용하되, 구조는 직접 판단하기
AI는 Producer·Consumer와 설정 코드를 작성하는 데 도움이 되었습니다. 하지만 어떤 서비스가 어떤 이벤트를 발행하고 소비해야 하는지는 전체 호출 관계와 데이터 흐름을 직접 추적하며 결정했습니다.
개별 코드의 동작을 넘어, 시스템의 목적과 데이터 정합성에 맞는 선택인지 판단하는 것이 개발자의 역할이라고 생각합니다.