Backend/Spring+Boot

@JsonCreator 주의사항

findmypiece 2022. 11. 2. 14:54
728x90

Spring 환경에서는 일반적으로 api body 포맷으로 json 을 사용하고 전달받은 json 문자열의 역직렬화도 자동으로 지원한다. 그런데 역직렬화 함수를 직접 구현해서 유효성 검증에 활용하고 싶은 경우 해당 클래스의 static 팩토리 함수에 @JsonCreator 를 명시해주면 된다.

 

코틀린 환경을 기준으로 아래와 같이 구현할 수 있다.

data class AuthMsgReqInfo private constructor(
    val recieverNumber: String,
    val senderNumber: String,
    var content:String,
    val pocId: String?,
    val origin: Origin?
    ) {

    companion object {
        @JsonCreator
        @JvmStatic
        fun of(
            recieverNumber: String?,
            senderNumber: String?,
            content: String?,
            pocId: String?,
            origin: Origin?): AuthMsgReqInfo =
                when {
                    recieverNumber.isNullOrBlank() ->
                        throw InmsException("'recieverNumber' is required")
                    !content.isNullOrBlank() && !content.contains("\${authKey}") ->
                        throw InmsException("'content' must contain \${authKey}")
                    else ->
                        AuthMsgReqInfo(
                            recieverNumber = recieverNumber!!,
                            senderNumber = senderNumber ?: "15667727",
                            content = if(content.isNullOrBlank()) "인증번호 \${authKey} 을 입력해주세요." else content,
                            pocId = pocId,
                            origin = origin
                        )
                }

    }

}

 

그런데 이때 클래스의 맴버변수는 기본적으로 모두 static 팩토리 함수의 파라미터로 포함되어야 한다.

 

예를 들어 AuthMsgReqInfo의 맴버변수 중 content 값을 무조건 디폴트 문자열로 할당하기 위해 코드를 아래와 같이 바꿨다고 가정해보자.

data class AuthMsgReqInfo private constructor(
    val recieverNumber: String,
    val senderNumber: String,
    var content:String,
    val pocId: String?,
    val origin: Origin?
    ) {

    companion object {
        @JsonCreator
        @JvmStatic
        fun of(
            recieverNumber: String?,
            senderNumber: String?,
//            content: String?,
            pocId: String?,
            origin: Origin?): AuthMsgReqInfo =
                when {
                    recieverNumber.isNullOrBlank() ->
                        throw InmsException("'recieverNumber' is required")
//                    !content.isNullOrBlank() && !content.contains("\${authKey}") ->
//                        throw InmsException("'content' must contain \${authKey}")
                    else ->
                        AuthMsgReqInfo(
                            recieverNumber = recieverNumber!!,
                            senderNumber = senderNumber ?: "15667727",
//                            content = if(content.isNullOrBlank()) "인증번호 \${authKey} 을 입력해주세요." else content,
                            content = "인증번호 \${authKey} 을 입력해주세요.",
                            pocId = pocId,
                            origin = origin
                        )
                }

    }

}

 

request 와 무관하게 디폴트 값을 할당할 것이니 팩토리 함수의 파라미터에서도 제거되고 객체 생성시에만 포함하도록 했다. 표면적으로 딱히 문제는 없어보인다. 

 

실제로 아래와 같은 json 을 requst body 로 보내면 지정한 특정 문자열이 잘 셋팅된다. 

{
  "recieverNumber": "01051641035",
  "senderNumber": "15667727",
  "pocId": null,
  "origin": null
}

 

그런데 문제는 json 이 아래와 같을 경우다.

{
  "recieverNumber": "01051641035",
  "senderNumber": "15667727",
  "content": "test",
  "pocId": null,
  "origin": null
}

 

content 를 "test" 로 할당해서 보냈지만 팩토리 함수에서 이를 활용하지 않고 역직렬화를 수행했으니 디폴트 문자열이 셋팅되었을 거 같지만 실제 역직렬화 되는 값은 "test" 다.

 

예상해보면 request 로 받은 json 값에 대해 1차적으로 @JsonCreator 로 지정된 팩토리 함수로 역직렬화를 수행하고 역직렬화되지 못한 데이터는 spring의 기본 역직렬화 정책에 따라 매핑된 key 값에 할당되는 것 같다.

 

이처럼 @JsonCreator 를 지정한 함수에서 맴버변수를 모두 처리하지 않으면 의도치 않은 데이터가 역직렬화에 포함될 수 있으니 주의가 필요하다.

728x90