refactor: PaymentClient 및 테스트에 새로 추가된 커스텀 예외 반영

This commit is contained in:
이상진 2025-07-23 18:37:41 +09:00
parent 498340f173
commit e9fcc31dea
2 changed files with 56 additions and 58 deletions

View File

@ -4,17 +4,15 @@ import com.fasterxml.jackson.databind.ObjectMapper
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.http.HttpRequest
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatusCode
import org.springframework.http.MediaType
import org.springframework.http.client.ClientHttpResponse
import org.springframework.stereotype.Component
import org.springframework.web.client.RestClient
import roomescape.common.exception.ErrorType
import roomescape.common.exception.RoomescapeException
import roomescape.payment.exception.PaymentErrorCode
import roomescape.payment.exception.PaymentException
import roomescape.payment.web.PaymentCancelRequest
import roomescape.payment.web.PaymentCancelResponse
import java.io.IOException
import java.util.Map
@Component
@ -43,7 +41,7 @@ class TossPaymentClient(
{ req: HttpRequest, res: ClientHttpResponse -> handlePaymentError(res) }
)
.body(PaymentApproveResponse::class.java)
?: throw RoomescapeException(ErrorType.PAYMENT_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR)
?: throw PaymentException(PaymentErrorCode.PAYMENT_PROVIDER_ERROR)
}
fun cancel(cancelRequest: PaymentCancelRequest): PaymentCancelResponse {
@ -60,7 +58,7 @@ class TossPaymentClient(
{ req: HttpRequest, res: ClientHttpResponse -> handlePaymentError(res) }
)
.body(PaymentCancelResponse::class.java)
?: throw RoomescapeException(ErrorType.PAYMENT_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR)
?: throw PaymentException(PaymentErrorCode.PAYMENT_PROVIDER_ERROR)
}
private fun logPaymentInfo(paymentRequest: PaymentApproveRequest) {
@ -80,32 +78,25 @@ class TossPaymentClient(
private fun handlePaymentError(
res: ClientHttpResponse
): Nothing {
val statusCode = res.statusCode
val errorType = getErrorTypeByStatusCode(statusCode)
val errorResponse = getErrorResponse(res)
throw RoomescapeException(
errorType,
"[ErrorCode = ${errorResponse.code}, ErrorMessage = ${errorResponse.message}]",
statusCode
)
getErrorCodeByHttpStatus(res.statusCode).also {
logTossPaymentError(res)
throw PaymentException(it)
}
}
private fun getErrorResponse(
res: ClientHttpResponse
): TossPaymentErrorResponse {
private fun logTossPaymentError(res: ClientHttpResponse): TossPaymentErrorResponse {
val body = res.body
val errorResponse = objectMapper.readValue(body, TossPaymentErrorResponse::class.java)
body.close()
log.error { "결제 실패. response: $errorResponse" }
return errorResponse
}
private fun getErrorTypeByStatusCode(
statusCode: HttpStatusCode
): ErrorType {
private fun getErrorCodeByHttpStatus(statusCode: HttpStatusCode): PaymentErrorCode {
if (statusCode.is4xxClientError) {
return ErrorType.PAYMENT_ERROR
return PaymentErrorCode.PAYMENT_CLIENT_ERROR
}
return ErrorType.PAYMENT_SERVER_ERROR
return PaymentErrorCode.PAYMENT_PROVIDER_ERROR
}
}

View File

@ -14,8 +14,8 @@ import org.springframework.test.web.client.ResponseActions
import org.springframework.test.web.client.match.MockRestRequestMatchers.*
import org.springframework.test.web.client.response.MockRestResponseCreators.withStatus
import org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess
import roomescape.common.exception.ErrorType
import roomescape.common.exception.RoomescapeException
import roomescape.payment.exception.PaymentErrorCode
import roomescape.payment.exception.PaymentException
import roomescape.payment.web.PaymentCancelRequest
import roomescape.payment.web.PaymentCancelResponse
@ -56,28 +56,32 @@ class TossPaymentClientTest(
}
}
test("400 에러 발생") {
commonAction().andRespond {
withStatus(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_JSON)
.body(SampleTossPaymentConst.tossPaymentErrorJson)
.createResponse(it)
context("실패 응답") {
fun runTest(httpStatus: HttpStatus, expectedError: PaymentErrorCode) {
commonAction().andRespond {
withStatus(httpStatus)
.contentType(MediaType.APPLICATION_JSON)
.body(SampleTossPaymentConst.tossPaymentErrorJson)
.createResponse(it)
}
// when
val paymentRequest = SampleTossPaymentConst.paymentRequest
// then
val exception = shouldThrow<PaymentException> {
client.confirm(paymentRequest)
}
exception.errorCode shouldBe expectedError
}
// when
val paymentRequest = SampleTossPaymentConst.paymentRequest
// then
val exception = shouldThrow<RoomescapeException> {
client.confirm(paymentRequest)
test("결제 서버에서 4XX 응답 시") {
runTest(HttpStatus.BAD_REQUEST, PaymentErrorCode.PAYMENT_CLIENT_ERROR)
}
assertSoftly(exception) {
this.errorType shouldBe ErrorType.PAYMENT_ERROR
this.invalidValue shouldBe "[ErrorCode = ERROR_CODE, ErrorMessage = Error message]"
this.httpStatus shouldBe HttpStatus.BAD_REQUEST
test("결제 서버에서 5XX 응답 시") {
runTest(HttpStatus.INTERNAL_SERVER_ERROR, PaymentErrorCode.PAYMENT_PROVIDER_ERROR)
}
}
}
@ -111,26 +115,29 @@ class TossPaymentClientTest(
}
}
test("500 에러 발생") {
commonAction().andRespond {
withStatus(HttpStatus.INTERNAL_SERVER_ERROR)
.contentType(MediaType.APPLICATION_JSON)
.body(SampleTossPaymentConst.tossPaymentErrorJson)
.createResponse(it)
context("실패 응답") {
fun runTest(httpStatus: HttpStatus, expectedError: PaymentErrorCode) {
commonAction().andRespond {
withStatus(httpStatus)
.contentType(MediaType.APPLICATION_JSON)
.body(SampleTossPaymentConst.tossPaymentErrorJson)
.createResponse(it)
}
val cancelRequest: PaymentCancelRequest = SampleTossPaymentConst.cancelRequest
val exception = shouldThrow<PaymentException> {
client.cancel(cancelRequest)
}
exception.errorCode shouldBe expectedError
}
// when
val cancelRequest: PaymentCancelRequest = SampleTossPaymentConst.cancelRequest
// then
val exception = shouldThrow<RoomescapeException> {
client.cancel(cancelRequest)
test("결제 서버에서 4XX 응답 시") {
runTest(HttpStatus.BAD_REQUEST, PaymentErrorCode.PAYMENT_CLIENT_ERROR)
}
assertSoftly(exception) {
this.errorType shouldBe ErrorType.PAYMENT_SERVER_ERROR
this.invalidValue shouldBe "[ErrorCode = ERROR_CODE, ErrorMessage = Error message]"
this.httpStatus shouldBe HttpStatus.INTERNAL_SERVER_ERROR
test("결제 서버에서 5XX 응답 시") {
runTest(HttpStatus.INTERNAL_SERVER_ERROR, PaymentErrorCode.PAYMENT_PROVIDER_ERROR)
}
}
}