코프링으로 결제 시스템 만들기(4) - Controller

미미·2024년 8월 19일

코프링

목록 보기
4/9

Controller

PaymentController.kt

: 결제 API의 컨트롤러 역할을 하며, 클라이언트의 요청을 받아 처리하는 부분


@RequestMapping("/api/v1")
@RestController
class PaymentController(
    private val paymentService: PaymentService // 선언하면서 프로퍼티를 선언하면 스프링이 알아서 의존성을 주입해줌
) {
    @PostMapping("/pay")
    fun pay(
        @Valid @RequestBody
        payRequest: PayRequest
    ): PayResponse {
        return PayResponse("p1", 100, "txId", LocalDateTime.now())
    }
}

data class PayResponse(
    val payUserId: String,
    val amount: Long,
    val transactionId: String,
    val transactedAt: LocalDateTime,
)

data class PayRequest(
    @field:NotBlank 
    val payUserId: String,
    @field:Min(100)
    val amount: Long,
    @field:NotBlank
    val merchantTransactionId: String,
    @field:NotBlank
    val orderTitle: String,
)
  • private val paymentService: PaymentService : kotlin에서는 클래스 선언 시 괄호 안에 생성자를 정의하므로 PaymentService는 생성자 주입 방식으로 spring이 PaymentService의 의존성을 자동으로 주입
  • 같은 파일 내에 Request와 Response DTO를 선언해줌

    💡 Kotlin의 데이터 클래스
    자동으로 equals(), hashCode(), toString(), copy() 등을 제공하여 간결한 코드를 작성할 수 있게 합니다. (DTO나 VO 같은 데이터 전달 객체를 정의할 때 특히 유용!!)

  • @field: 검증 어노테이션에 붙이지않으면 생성자에서만 검증해주므로 필드 레벨까지 검증할수있도록 명시해줌

Controller Test

test code 작성전 test code 작성시 필요한 의존성을 추가해줍니다.
kotlin을 이용한 테스트코드에서는 Mocking을 위해
mock 대신 mockk을 사용합니다.

    testImplementation("io.mockk:mockk:1.13.3")
    testImplementation("com.ninja-squad:springmockk:4.0.0")

‼️ 참고 -> mockk 공식문서

PaymentControllerTest.kt

@WebMvcTest(PaymentController::class)
internal class PaymentControllerTest @Autowired constructor(
    private val mockMvc: MockMvc
) {
    @MockkBean
    private lateinit var paymentService: PaymentService

    private val mapper = ObjectMapper()

    @Test
    fun `결제 요청 - 성공 응답`() {
        //given

        //when

        //then
        mockMvc.post("/api/v1/pay") {
            headers {
                contentType = APPLICATION_JSON
                accept = listOf(APPLICATION_JSON)
            }

            content = mapper.writeValueAsString(
                PayRequest(
                    payUserId = "p1",
                    amount = 100,
                    merchantTransactionId = "m1",
                    orderTitle = "orderTitle"
                )
            )
        }.andExpect {
            status { isOk() }
            content { jsonPath("$.payUserId", equalTo("p1")) }
            content { jsonPath("$.amount", equalTo(100)) }
            content { jsonPath("$.transactionId", equalTo("txId")) }
        }.andDo { print() }
    }

}
  • MockMvc:실제로 애플리케이션을 실행하지 않고도 컨트롤러의 엔드포인트를 테스트할 수 있음.
    @Autowired를 통해 MockMvc 객체가 주입되며, 이를 사용해 컨트롤러에 대한 HTTP 요청을 직접 만들어 테스트함
  • @MockkBean : 테스트 중에 실제 빈 대신 모킹된 빈을 사용하도록 설정하는 어노테이션, PaymentController가 실제 PaymentService 구현체를 호출하지 않고 모킹된 객체를 사용해 테스트를 진행함
  • private val mapper = ObjectMapper() : PayRequest 객체를 JSON 문자열로 변환해 요청의 본문(content)에 포함시키기 위해 사용함

mockMvc.post

  • POST 요청 시뮬레이션: mockMvc.post("/api/v1/pay")는 /api/v1/pay 엔드포인트로 POST 요청을 보내는 테스트 코드
  • 헤더 설정: contentType = APPLICATION_JSON과 accept = listOf(APPLICATION_JSON)로 헤더에 JSON 타입을 설정
  • 본문 내용 설정: mapper.writeValueAsString()을 사용해 PayRequest 객체를 JSON 형식으로 직렬화하여 요청 본문(content)에 포함

응답 검증

  • status { isOk() }: HTTP 상태 코드가 200(OK)임을 검증
  • jsonPath()는 JSON 응답에서 특정 경로의 값을 추출하는 방법
    ex) jsonPath("$.payUserId", equalTo("p1")): 응답 본문에서 payUserId가 "p1"과 같은지 확인

결과 출력

  • .andDo { print() } : 테스트 중 발생한 요청과 응답을 출력

테스트를 실행하여 출력결과를 보면 성공적으로 요청을하고 응답을 받는것을 확인하실 수 있습니다.

0개의 댓글