IT

Spring + Kotlin 요청 파라메터 snake case -> camel case 로 받기

Dante2k™ 2025. 3. 7. 11:54
반응형

spring + java 로 구현된 레거시 프로젝트를 spring + kotlin 으로 리팩토링하면서 customer_code 같은 형태로 요청 파라메터를 받았던 부분을 request 객체를 통해서 customerCode 같은 property 로 바인딩 하고 싶어졌습니다.

한방에 모든 API 의 파라메터를 camel case 로 추가하는 방법은 다른 블로그에서 소개하므로, 여기서는 하나하나 리팩토링하는 과정에서 kotlin 의 생성자 파라메터 명칭을 이용하는 방법이 가장 간단히 구현할 수 있는 방법으로 보여 이를 간단히 설명해 보겠습니다.

snake case -> camel case 한방에 : https://m.blog.naver.com/simpolor/221874168491

 

일단 요청 파라메터는 @JsonProperty 를 사용할 수 없습니다. 이는 요청 파라메터는 ObjectMapper 를 통해 바인딩되는 것이 아니기 때문입니다.

 

일단 콘트롤러 부분을 먼저 봅니다. 이번에 리팩토링하면서 여러개의 요청 파라메터를 객체로 받도록 구현을 변경했습니다.

@RestController
class SalesLedgerQueryV2Controller(
    private val salesLedgerService: SalesLedgerService,
) {

    @GetMapping(
        value = [ApiVersion.VERSION_2 + "/ledgers"],
        produces = [MediaType.APPLICATION_JSON_VALUE],
    )
    @ResponseStatus(HttpStatus.OK)
    fun getList(
        @Valid @ModelAttribute request: SalesLedgerGetListRequest,
        tokenData: TokenData,
    ): SalesLedgerResponse {
        return salesLedgerService
            .getList(request.toQuery(tokenData))
    }
}

 

이제 파라메터를 바인딩하는 request 객체를 보겠습니다.

@Suppress("LocalVariableName", "kotlin:S117")
class SalesLedgerGetListRequest constructor(
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    start_day: LocalDate?,
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
    end_day: LocalDate?,
    customer_code: String?,
) {
    @field:NotNull(message = "조회 시작 일자를 입력하세요.")
    val startDay: LocalDate? = start_day

    @field:NotNull(message = "조회 마지막 일자를 입력하세요.")
    val endDay: LocalDate? = end_day

    @field:NotBlank(message = "거래처 코드를 입력해주세요.")
    val customerCode: String? = customer_code
}

 

주요한 부분은 constructor 부분에 전달되는 parameter 는 snake case 이고, class 내부에 정의된 property 는 camel case 입니다.

 

바인딩 시 데이터 변환에 대한 정보는 parameter 에 정의하고, 데이터 검증(validation) 에 대한 정보는 property 에 정의되어 있음에 주의합니다. 이렇게 정의하면 여러 property 에 대한 validation 예외가 한번에 BindException 으로 전달되지만, parameter 에 validation annotation 을 정의하면 가장 첫번째 예외만 IllegalArgumentException 등으로 전달되어 예외 처리가 복잡해집니다.

 

가장 상단의 @Suppress 는 intellij 같은 IDE, sonarqube 등에서 경고를 하는 이유로 추가되었습니다.

 

이렇게 하나씩 리팩토링하다보면 인터페이스 변경없이 원하는대로 내부 구현을 깔끔하게 정리해 나갈 수 있습니다.

반응형