Booking 서비스 담당자와 API 스펙을 맞추면서 MSA에서 서비스 간 통신이 어떻게 이루어지는지 정리했다.
1. 전체 결제 흐름
사용자 예매 확정
→ Booking 서비스 → POST /internal/v1/payments (FeignClient)
→ Payment 서비스가 orderId 생성 후 반환
→ Booking이 orderId 보관
→ 사용자가 토스 결제창에서 결제
→ Payment 서비스 결제 승인
→ payment.completed 이벤트 발행
→ Booking 서비스가 이벤트 수신 후 예매 확정 처리
2. FeignClient란?
Booking 서비스가 Payment 서비스를 HTTP로 직접 호출하는 클라이언트다.
// Booking 서비스에서 구현
@FeignClient(name = "payment-service")
public interface PaymentClient {
@PostMapping("/internal/v1/payments")
PaymentResponse createPayment(@RequestBody CreatePaymentRequest request);
}
name = "payment-service"는 Eureka에 등록된 서비스 이름이다. 직접 IP를 몰라도 Eureka가 자동으로 주소를 찾아준다.
3. orderId는 누가 만들어?
Payment 서비스가 만든다!
// PaymentCommandService.createPayment()
String orderId = UUID.randomUUID().toString().replace("-", ""); // Payment가 생성
Booking → POST /internal/v1/payments (bookingId, userId, amount 전달)
→ Payment가 orderId 생성
→ orderId 포함한 응답 반환
→ Booking이 orderId 보관
4. Booking 서비스에 공유한 API 스펙
결제 생성
POST http://payment-service/internal/v1/payments
Body:
{
"bookingId": "예매 UUID",
"userId": "로그인한 사용자 UUID",
"finalAmount": 결제금액
}
Response:
{
"paymentId": "결제 UUID",
"orderId": "토스 orderId",
"amount": 50000,
"status": "PENDING"
}
환불 (예매 취소 시)
POST http://payment-service/api/v1/payments/{paymentId}/refund
Headers:
X-User-Id: 사용자 UUID
Body:
{
"cancelReason": "취소 사유"
}
5. payment.completed 이벤트
결제 승인 완료 시 아웃박스 패턴으로 발행된다.
{
"paymentId": "결제 UUID",
"bookingId": "예매 UUID",
"userId": "사용자 UUID",
"amount": 50000,
"status": "SUCCESS"
}
Booking 서비스가 이 이벤트를 수신해서 예매 확정 처리를 한다.
6. 왜 userId를 같이 넘겨줘야 해?
처음에 bookingId만 넘기면 Payment가 Booking에서 userId를 조회하면 되지 않냐는 생각이 들었다.
근데 그러면 순환 참조가 생긴다.
Booking → Payment (결제 생성)
Payment → Booking (userId 조회) ← 순환 참조!
MSA에서 서비스 간 순환 참조는 장애 전파 위험이 있다. Booking이 죽으면 Payment도 userId를 못 가져와서 결제 생성이 실패한다.
그래서 Booking이 userId를 직접 넘겨주는 방식을 선택했다.
7. 예매 취소 = 환불
처음에 PATCH /internal/v1/payments/{paymentId}/status 같은 상태 강제 변경 API가 필요할 것 같았다.
근데 예매 취소는 환불이 같이 일어나는 거라 기존 환불 API로 처리 가능하다.
사용자 예매 취소
→ Booking이 POST /api/v1/payments/{paymentId}/refund 호출
→ 토스 취소 API 호출
→ REFUNDED 상태로 변경
별도 상태 변경 API는 필요 없었다.
마무리
MSA에서 서비스 간 통신 설계 시 고려할 것들을 정리하면 이렇다.
- FeignClient: Eureka 기반 서비스 디스커버리로 IP 없이 통신
- 순환 참조 방지: 필요한 데이터는 요청 시 같이 넘기기
- 이벤트 기반: 결제 완료 후 Booking에 알릴 때는 Kafka 이벤트 사용
- 책임 분리: 결제 생성은 Payment, 예매 확정은 Booking이 각자 처리
'Spring 개발일지' 카테고리의 다른 글
| [팀프로젝트] MSA 기반 티켓팅 프로그램 - 결제 실패 이벤트 발행 (0) | 2026.05.20 |
|---|---|
| [팀프로젝트] MSA 기반 티켓팅 프로그램 - 전체 레이어 테스트 코드 작성 (0) | 2026.05.19 |
| [팀프로젝트] MSA 기반 티켓팅 프로그램 - 결제창 자동 실행 API 구현 (1) | 2026.05.14 |
| [팀프로젝트] MSA 기반 티켓팅 프로그램 - 전체 결제 목록 조회 API (ADMIN) (0) | 2026.05.13 |
| [팀프로젝트] MSA 기반 티켓팅 프로그램 - AuthContext 적용 및 공통 모듈 업데이트 (0) | 2026.05.12 |