프로젝트 진행 목적 요약하기 💡
개인 과제로 진행했던 결제 시스템 기반의 프로젝트를 고도화하고 구독 기반의 복합 결제 시스템으로 발전 시키는 과정을 팀 프로젝트로 진행하게 되었다.
고도화에 필요한 ERD 설계부터 비즈니스 로직 플로우차트를 그리고, 백엔드에서 이뤄져야할 과정들의 흐름을 이해하고 넘어가고자 했다.
☠️ 트러블 슈팅
1. 비즈니스 로직 설계 단계에서의 애로사항
- 포트원 기반의 API 연동과정에 대한 이해도 부족
2. 설계되어있는 프로젝트의 리팩토링 접근
3. Mono는 어떻게 사용해야 하는가?
💡 접근 및 해결방안
1. 엔티티간의 연관관계와 필요 요소들에 대해서 정리가 필요했다.
- 검증에 대한 분기점 설정은 언제가 적합한가?
- 외부 API(포트원) 기반의 실 결제 내역 및 추출할 수 있는 요소는 어떤 부분이 있는가?
- 내부 DB에 저장되는 시점은 언제가 적합한가?
우선 이번 프로젝트에서는 포인트 기반의 결제 시스템 구현이 주 목적이기에 기본적으로 상품이 생성되어있는 상태에서의 환경을 기반으로 프로젝트 작업을 진행하였다.
짜여져있는 html 화면이 로그인 / 회원가입 / 포인트 결제 시스템 (결제 조회 / 주문 조회 / 결제하기(포트원 연동) 인 점을 고려하여 필요 과정 외에는 Postman을 활용하여 특정 진행과정을 처리하도록 하였다.
또한 PortOne 공식문서를 참고하여 포트원에서 실 결제처리된 결제내역에서 담고 있는 정보에 대해서도 이해하는 과정을 거쳤다 .

2. 우선 구현 가능한 범위 내에서의 작업을 우선적으로 하고자했다.
- 코드에 대한 이해가 되지 않아 컨트롤러 및 서비스를 새롭게 생성하여 구현하였고, 기존 구현되어있던 내용에서 활용 가능한 정도만 끌어서 붙였다.
- PortOneClient 기반의 호출이기 때문에 큰 틀을 벗어날 수 없었던 점, 사용하는 데 정해진 양식과 규칙이 있다는 점을 고려해서 작업을 진행했다.
- 실제 프론트엔드에서 사용자가 결제를 진행하고 해당 결제건에 대한 검증과정을 하나의 진입점으로 모아 처리하도록 구현했다.
// 1) PortOne 호출 + 결제 정보 조회
public Mono<Boolean> verifyPayment(String impUid, Long orderId, BigDecimal expectedAmount) {
return portOneClient.getAccessToken()
.flatMap(token -> portOneClient.getPaymentDetails(impUid, token))
.map(paymentDetails -> {
// 2) 금액 검증
BigDecimal paidAmount = extractAmount(paymentDetails);
if (paidAmount == null || paidAmount.compareTo(expectedAmount) != 0) {
return false;
}
// 3) 상태 검증
String status = extractStatus(paymentDetails); // "paid", "failed" 등
return "paid".equalsIgnoreCase(status);
});
}
단순히 PG사 결제 성공이 비즈니스 로직 성공을 의미하는 것이 아닌, 내부적으로 정의한 검증조건이 만족하는 경우 후처리를 할 수 있도록 구성했다.
해당 검증과정 이후 후처리 로직을 트랜잭션으로 감싸서 트랜잭션 기반의 후처리 과정을 하나의 서비스 메서드로 담을 수 있도록 처리했다.
@Transactional
public void updateAfterPayment(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow(...);
Payment payment = paymentRepository.findByOrderId(orderId).orElseThrow(...);
User user = userRepository.findById(order.getUserId()).orElseThrow(...);
// 1) 재고 차감
order.getOrderItems().forEach(item -> {
item.getProduct().decreaseStock(item.getQuantity());
});
// 2) 누적 결제 금액 갱신 + 등급 업데이트
user.updateTotalPaidAmount(order.getTotalAmount());
user.updateRankByTotalPaid();
// 3) 포인트 적립
pointService.earnPoints(user, order);
// 4) 주문 상태 최종 완료
order.complete(); // order.setStatus(OrderStatus.COMPLETE) 같은 의미 메서드
}
3. Mono의 핵심 특징
- 모든 내용을 정리한다기 보다는 PortOne 기반에서 사용된 내용으로 해당 핵심 특징을 정리하고자 한다.
- 결제 시스템에서 Mono를 사용했던 이유
- 비동기 API 하나의 결과 처리에 맞는 구조
- 스레드 점유를 하지않는 논블로킹 방식 → 고성능
- 연속된 비동기 작업을 자연스럽게 이어갈 수 있음
- 단계별 오류 처리에 매우 강함
- 필요할 때만 실행되는 Lazy 특성
- 스레드 관리 자동화로 안정적인 구조
'Spring > Spring 실습' 카테고리의 다른 글
| [프로젝트 회고] 실시간 채팅 프로젝트 (0) | 2025.12.31 |
|---|---|
| [프로젝트] 실시간 데이터 처리 회고 (0) | 2025.12.08 |
| [Spring 실습] 이커머스 백오피스 프로젝트 (0) | 2025.11.03 |
| [Spring 실습] 점심 추천 & 투표 서비스 만들기 V2 (0) | 2025.10.29 |
| [Spring 실습] 점심 추천 & 투표 서비스 만들기 v1 (0) | 2025.10.27 |