본문 바로가기

트러블 슈팅

Kotlin + Spring DTO에서 NotNull 변수의 null Validation

📒 상황

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 예외를 잡아서 처리해야 한다.