ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Kotlin + Spring DTO에서 NotNull 변수의 null Validation
    트러블 슈팅 2023. 5. 18. 15:23

    📒 상황

    data class FacilityReq(
        @NotNull
        val category: Category,
    
        @NotBlank
        val name: String
    )

     

    ◼ 시설 등록 요청 DTO를 위와 같이 작성하고 name 값을 넣지 않고 요청을 보내면 에러가 발생한다.

     

    💥 기대했던 에러는 name은 null이 될 수 없다는 에러였는데, 에러 API 응답으로 설정한 값과 다른 값이 응답으로 왔다.

     

    📒 원인

    name을 NotNull 타입인 String으로 설정해서 null을 허용하지 않는 타입으로 정의했다.

    이로 인해, name이 null로 채워지게 된다면 @field:NotBlank validation 로직을 타기 전에 변수에 값을 할당하는 과정에서 예외가 발생하게 되어 다음과 같은 에러가 발생한다.

    💥 HttpMessageNotReadableException

     

    📒 해결 방법

    ◼ Dto에서 NotNull 타입으로 Validation을 진행하고자 하면 다음과 같이 MethodArgumentNotValid 예외를 처리하는 것이 아닌 HttpMessageNotReadableException로 예외를 처리해야 한다.

     

    🤦‍♂️ MissingKotlinParameterException -> Deprecated

    @RestControllerAdvice
    class ExceptionHandler : ResponseEntityExceptionHandler() {
        override fun handleHttpMessageNotReadable(
            ex: HttpMessageNotReadableException,
            headers: HttpHeaders,
            status: HttpStatusCode,
            request: WebRequest
        ): ResponseEntity<Any>? {
            logger.error("[HttpMessageNotReadableException] ${ex.message}")
            val errorMessage = when (val cause = ex.cause) {
                is MissingKotlinParameterException -> "${cause.parameter.name} is null"
    
                else -> "유효하지 않은 요청입니다"
            }
            return ResponseEntity.status(ErrorCode.REQUEST_RESOURCE_NOT_VALID.httpStatus)
                .body(ApiResponse.error(ErrorCode.REQUEST_RESOURCE_NOT_VALID.code, errorMessage))
        }
    }

    ◼ jackson-module-kotlion 2.16 부터 Deprecated 되었다.

     

    ✔ MismatchedInputException

    override fun handleHttpMessageNotReadable(
        ex: HttpMessageNotReadableException,
        headers: HttpHeaders,
        status: HttpStatusCode,
        request: WebRequest
    ): ResponseEntity<Any>? {
        logger.error("[HttpMessageNotReadableException] ${ex.message}")
        val listOf = listOf("a", "b", "c")
        println(listOf.joinToString() { it.toString() })
        val errorMessage = when (val cause = ex.cause) {
            is MismatchedInputException -> "${cause.path.joinToString() { it.fieldName }} is null"
            else -> "유효하지 않은 요청입니다"
        }
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ApiErrorResponse(errorMessage))
    }

    ◼ 위 코드를 작성하고 다시 요청을 보내면 다음과 같이 응답 값이 처리된다.

     

    📒 결론

    ◼ Kotlin에서 NotNull 타입을 사용하면서, Null에 대한 Validation을 체크하려면 Java와 같이 Validation 어노테이션을 통해서 처리하는 것이 아닌 HttpMessageNotReadableException 예외를 잡아서 처리해야 한다.

    댓글

Designed by Tistory.