• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

Spring Validation 의 @field:NotBlank 등으로 Request DTO 필드 검증 시, 필드타입 선언방식( non-null or nullable )

23.10.03 16:14 작성 23.10.03 18:36 수정 조회수 1.46k

3

안녕하세요 강사님. 참 좋은 강의 감사드립니다.

예제 고도화 과정에 Request DTO nullable 필드 처리에 어떤 방식이 적절할 지 알수 없어 남기게 되었습니다.


Kotlin 객체 필드타입을 크게 두 방식으로 선언 할 수 있습니다.

  1. nullable

  2. non-null

코프링 기반에서 Request DTO 의 경우,
객체필드를non-null 타입으로 선언하면 WebMvc 와 spring-validation 을 함께 사용하면, 발생 가능한 가설을 세워보았습니다.

문제:
서버 요청에 '비 유효값'`[공백문자["", ' '], null 필드값, 공백 JSON( {} )]`이 넘어올 때, Kotlin 언어의 non-null 타입 속성은, sprint-validation 절차를 거치지 못함.

 

원인:
1. 속성 타입을 non-null 타입 선언 후 컴파일 시 Intrinsics.checkNotNullParameter(field, "field"); 함수가 non-null 검증코드로 DTO 생성자 함수 본체에 추가.

2. 앞선 checkNotNullParameter 함수는 DTO 생성을 원천 차단.

3. 때문에 Kotlin non-null 검증이 spring-validation 검증 이전에 수행됨을 뜻함.

 

즉, 검증 대상이 생성이 되어야 spring-validation 절차를 거칠텐데, 검증 대상이 없는 상황이 된다.

(이 부분은 저의 뇌피셜이 포함되었으므로 검증되지 못한 분석결과 입니다)

 

이 문제에 대해 다음의 해결 방법을 생각할 수 있습니다.

  • field 타입을 nullable 하게 선언, 기본값 null 설정

data class UserCreateRequest(
    @param:JsonProperty("email")
    @field:NotBlank
    @field:Email
    val email: String? = null, ⬅️`?`및 기본값 설정
) {

    fun toCommand() = UserCreate(
        /*  String ❌ String? 미스매치 */
        email = email!!, ✅단언 해결
    )
}

이 방법은 단언 !! 을 사용하여 전적으로 spring-validation 의 검증에 전적으로 의지하는 코드입니다.

의문점은 개인적으로 우아하지 못한 단언 !! 을 꼭 사용해야 할까? 인데요,

 

다른 방법으로는,

  • Backing 필드 생성자 선언 ➡️ 커스텀 getter 로 Backing 필드값 반환

이 떠오르는데요, 이것 또한 보일러-플레이트 코드량이 늘어나서 우아하지 않아 보입니다^^;

강사님의 경우에는 어떠한 해결방법을 사용하지는지 궁금해서 질문 남겨보았습니다.

읽어 주셔서 감사합니다.

답변 1

답변을 작성해보세요.

2

안녕하세요! Truestar님!! 정말 좋은 질문 감사드립니다! 😊

 

결론부터 말씀드려보면 저는 DTO validation 과정에서 spring-validator를 사용하지 않는 편입니다!!!

저는 spring-validator를 사용하기 보다는 직접 validation 해주는 것을 선호해요! 😊 (매우 개인적인 의견입니다!)

예를 들어, 말씀해주신 것과 같이 특정 필드가 email이어야 한다면, 아래와 같이 코드를 작성할 것 같습니다!

data class UserCreateRequest(
    val email: String, // (1)
) {

    fun toCommand(): UserCreate {
      require(email.isEmail()) // (2)
      return UserCreate(email = email)
    }
}

하나씩 설명드려볼게요!

 

[1. val email: String]

우선 email을 받을 때 굳이 Blank인지 확인하지 않습니다! 또한, null이 들어오는 경우는 DTO 변환 과정에서 실패해도 괜찮다고 생각해서 처음부터 String 으로 받습니다.

 

[2. require() + isEmail]

다음으로는 코틀린에서 제공해주는 함수인 require를 사용해 주어진 email 필드가 이메일 포맷인지 확인할 것 같아요! 이때 isEmail() 은 미리 만들어둔 확장 함수로 생각할 수 있습니다! 😊

 

이런 방법을 사용하면, 고민하시던 String? -> String 을 굳이 사용하지 않아도 괜찮습니다.

 

또한, 개인적으로 이 방법을 선호하는 이유는, 예외 처리를 조금 더 custom 하게 할 수 있기 때문입니다.

예를 들어 원래는 email 포맷이 아니었던 경우 아무 에러나 던져줘도 괜찮았는데, 이제부터 이메일 포맷이 잘못되었음을 사용자에게 알려주어야 한다면?! 혹은 email 검증 과정에서 조금 특별한 규칙이 추가되어야 한다면?! (정해진 도메인의 이메일만 허용한다거나...)

 

이렇게 요구사항이 바뀌는 경우 spring-validator를 사용하는 것보다 조금 더 유연하게 대처할 수 있다 보니 개인적으로는 이런 검증 방식을 쓰고 있는 것 같습니다.

 

다만, "우아하지 못한 단언 !!" 이라 표현해주신 것처럼 외부와의 경계점에서는 불가피하게 !! 를 사용해야 하는경우가 꽤 있었습니다. API 쪽일 수도 있고, DB 쪽일 수도 있는데요! 이런 경우 정말 어쩔 수 없이 !! 를 사용해야 한다면 말씀해주신 backing property로 nullable type -> non-nullable type 변환 작업을 한 번쯤 해주는 편입니다 ㅎㅎㅎㅎ..

 

답변이 도움이 되었다면 좋겠습니다. 감사합니다!! 🙇🙇

Truestar님의 프로필

Truestar

질문자

2023.10.03

빠른 답변 감사합니다. 참고가 많이 되었어요! 한가지 궁금한 것이 spring-validation 없이 하신다면 이전이 구현해놓은 커스텀 검증모듈을 사용하시는건가요? 검증 해야할 부분이 은근 많을텐데, 이런것 역시 모두 커스텀으로 구현해서 사용하신다는 걸까요? 아니면 kotlin 용 검증 라이브러리가 또 있을까요?

이전에 구현해 놓은 커스텀 검증 모듈을 사용한다고 말씀드리는게 맞을 것 같습니다! 별도의 검증 라이브러리를 쓰지는 않고 있어요! 다만 검증해야 할 부분이 은근히 많긴(?) 하지만 또 그때 그때 필요한 걸 만들다보면 반복되는 경우가 많더라고요!! (생각해보면 spring-validation에서도 주로 사용하는 어노테이션이 그렇게 많지는 않았던 것 같습니다!) 👍

Truestar님의 프로필

Truestar

질문자

2023.10.03

소중한 답변 고맙습니다🙇