Handling Null in Request DTO

About

Spring의 컨트롤러에서 @RequestBody 어노테이션을 이용하면 HTTP body에 있는 JSON 데이터를 객체로 변환해 받아올 수 있다. JSON에 있는 모든 데이터가 꼭 필요하지 않아서 오지 않으면 무시하는 경우도 있지만, 전부 필요할 수도 있다.

Java

Java에서는 null checking을 언어 차원에서 지원하고 있지는 않다. 하지만 Validation의 어노테이션을 통해 검증할 수 있다.

ProfileUpdateRequest.java
@Getter
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ProfileUpdateRequest {
    
    @NotNull
    private Int age;
    
    private String imageUrl;
}

@NotNull 어노테이션으로 agenull이면 오류를 반환한다.

Kotlin

코틀린에서는 nullable 타입과 non-nullable 타입을 구분하여 null safe하게 개발할 수 있다. 그런데 약간의 주의가 필요하다.

일단 위의 코드를 코틀린으로 바로 바꾸면 이렇게 될 것이다.

ProfileUpdateRequest.kt
data class ProfileUpdateRequest(
    @field:NotNull
    val age: Int

    val imageUrl: String
)

하지만 여기서 여러 가지를 수정해야 한다.

  • 코틀린은 nullable과 non-nullable 타입을 타입 뒤에 ?를 붙이는 것으로 구분한다. 따라서 age는 애초에 null이 될 수 없다.

  • 이렇게 작성했을 때 age가 null로 들어오게 된다면, @field:NotNull 어노테이션을 통해 validation 로직이 실행되기 전에 변수에 값을 할당하는 과정부터 예외가 발생한다. 따라서 여기서 이 어노테이션은 의미가 없다.

  • 원래 @NotNull이 붙어있지 않았던 imageUrl은 nullable 타입이 되어야겠다.

수정사항을 반영할 때 이렇게 해결하는 방법이 있다.

ProfileUpdateRequest.kt
data class ProfileUpdateRequest(
    @field:NotNull    
    val age: Int?,

    val imageUrl: String?
)

다만 해당 DTO를 컨트롤러에서 받아서 사용하게 될텐데, age는 실제로 null이 아님에도 불구하고 age뒤에 ?를 붙이는 등 null checking을 진행해야 한다. 불편하기도 하고, 깔끔하지도 않다.

Solution

내 생각엔 이 방법이 가장 깔끔할 것 같다. 다음과 같이 진행하자.

  • null로 받아도 되는 필드는 단순히 ?를 붙인다. 아니면 붙이지 말.

    ProfileUpdateRequest.kt
    data class ProfileUpdateRequest(
        val age: Int,
        
        val imageUrl: String?
    )
  • HttpMessageNotReadableException을 exception handler에서 잡고, 필요하다면 추가 작업을 통해 MissingKotlinParameterException을 내부적으로 타입을 체크해 메시지를 반환한다.

참고로, non-nullable 타입에 null이 담기려고 하면 HttpMessageNotReadableException이 발생하지만, 예외가 발생한 진짜 원인인 MissingKotlinParameterException과는 직접적인 클래스 관계가 없다.

REF

Last updated