[REFACTOR]: PaymentService 외부 API 호출 로직 분리#91
Conversation
📝 WalkthroughWalkthroughExtracts Toss API calls into a new TossPaymentService; PaymentService now delegates confirm and cancel operations to that service, preserving existing validation and error handling. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant PaymentService as PaymentService
participant TossService as TossPaymentService
participant RestClient as tossPaymentClient
participant TossAPI as "Toss API"
Client->>PaymentService: confirmPayment(dto) / cancelPayment(...)
PaymentService->>TossService: confirm(dto) / cancel(paymentKey, dto)
TossService->>RestClient: POST /v1/payments... (body)
RestClient->>TossAPI: HTTP request
TossAPI-->>RestClient: HTTP response
RestClient-->>TossService: TossPaymentResponse
TossService-->>PaymentService: TossPaymentResponse
PaymentService-->>Client: validate response -> proceed / throw
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
PaymentService의 외부 Toss 결제 API 호출 로직을 별도 서비스(TossPaymentService)로 분리해 결제 도메인 서비스의 복잡도를 낮추는 리팩토링입니다.
Changes:
- Toss 결제 승인/취소 호출을 담당하는
TossPaymentService신규 추가 PaymentService에서RestClient직접 호출을 제거하고TossPaymentService로 위임
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/main/java/com/eatsfine/eatsfine/domain/payment/service/TossPaymentService.java | Toss 결제 승인/취소 외부 API 호출을 캡슐화하는 서비스 추가 |
| src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java | Toss API 호출 로직을 신규 서비스로 분리하여 의존성/코드 복잡도 감소 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // 토스 API 호출 | ||
| TossPaymentResponse response; | ||
| try { | ||
| response = tossPaymentClient.post() | ||
| .uri("/v1/payments/confirm") | ||
| .body(dto) | ||
| .retrieve() | ||
| .body(TossPaymentResponse.class); | ||
|
|
||
| if (response == null || !"DONE".equals(response.status())) { | ||
| log.error("Toss Payment Confirmation Failed: Status is not DONE"); | ||
| payment.failPayment(); | ||
| throw new GeneralException(ErrorStatus._INTERNAL_SERVER_ERROR); | ||
| } | ||
| } catch (Exception e) { | ||
| log.error("Toss Payment API Error", e); | ||
| TossPaymentResponse response = tossPaymentService.confirm(dto); | ||
|
|
||
| if (response == null || !"DONE".equals(response.status())) { | ||
| log.error("Toss Payment Confirmation Failed: Status is not DONE"); | ||
| payment.failPayment(); | ||
| throw new GeneralException(ErrorStatus._INTERNAL_SERVER_ERROR); | ||
| } |
There was a problem hiding this comment.
confirmPayment에서 tossPaymentService.confirm(dto)가 예외(예: RestClient 4xx/5xx, 네트워크 오류)로 GeneralException을 던지면, 현재 로직은 payment.failPayment()를 호출하지 못한 채 메서드가 종료됩니다. 이 메서드는 @Transactional(noRollbackFor = GeneralException.class)이라서 예외가 발생해도 트랜잭션 롤백이 되지 않기 때문에 결제가 PENDING 상태로 남는 회귀가 발생할 수 있습니다. tossPaymentService.confirm(dto) 호출을 try/catch로 감싸서 예외 시에도 payment.failPayment()를 수행한 뒤 예외를 재던지도록 처리해 주세요.
| public TossPaymentResponse cancel(String paymentKey, PaymentRequestDTO.CancelPaymentDTO dto) { | ||
| try { | ||
| return tossPaymentClient.post() | ||
| .uri("/v1/payments/" + paymentKey + "/cancel") |
There was a problem hiding this comment.
cancel()에서 URI를 문자열 덧셈으로 만들면 paymentKey에 특수문자/슬래시 등이 포함될 때 인코딩이 깨지거나 의도치 않은 경로가 구성될 수 있습니다. RestClient의 URI 템플릿(예: /v1/payments/{paymentKey}/cancel)이나 UriBuilder를 사용해 path variable로 바인딩하도록 변경해 주세요.
| .uri("/v1/payments/" + paymentKey + "/cancel") | |
| .uri("/v1/payments/{paymentKey}/cancel", paymentKey) |
| private final RestClient tossPaymentClient; | ||
|
|
||
| public TossPaymentResponse confirm(PaymentConfirmDTO dto) { |
There was a problem hiding this comment.
현재 애플리케이션에는 RestClient 빈이 최소 2개(TossPaymentConfig.tossPaymentClient, RestClientConfig.businessWebClient) 존재합니다. 이 상태에서 RestClient 타입을 @Qualifier 없이 주입하면, 컴파일러의 파라미터 이름 보존 여부에 따라 런타임에 DI 후보가 모호해질 수 있습니다. tossPaymentClient 주입 지점에 @Qualifier("tossPaymentClient")(또는 @Primary 지정)로 빈을 명시해 주세요.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/payment/service/TossPaymentService.java`:
- Around line 33-41: In TossPaymentService.cancel, avoid string-concatenating
paymentKey into the URI; change the tossPaymentClient.post().uri(...) call to
use a URI template with a placeholder (e.g., "/v1/payments/{paymentKey}/cancel")
and pass paymentKey as the variable so the RestClient will encode reserved
characters properly; keep the subsequent
.body(dto).retrieve().body(TossPaymentResponse.class) behavior unchanged and
ensure you update the uri invocation that currently builds the path with "+"
concatenation.
💡 작업 개요
✅ 작업 내용
🧪 테스트 내용
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.