정확히는 모르겠습니다만, 원인은 KotlinModule때문인 것 같습니다. (아마 그럴 것 같습니다..ㅜㅠㅜ)
비슷한 글로 다음 블로그도 참고하시면 좋을 것 같습니다.
(https://kapentaz.github.io/kotlin/json/Kotlin-and-Jackson-(ObjectMapper)/)
저는 stackoverflow 뒤지다가 저와 동일한 문제는 아니었지만 ObjectMapper에 KotlinModule을 설정해 주는 코드를 발견했습니다.
혹시나 해서 확인해보니, KotlinModule이 있다면 기본 생성자 없이 deserialize가 가능하다는 사실을 알게되었습니다.
저의 코드는 다음과 같았습니다.
data class CreateMemberRequest(
@field:NotEmpty val email: String,
@field:NotEmpty val password: String,
@field:NotEmpty val nickname: String,
val profileImagePath: String? = null,
) {
fun toServiceDto(): CreateMemberDto {
//profileImagePath가 null이 아닌 ""가 넘어온 경우 null로 변경하여 넘기기
if (profileImagePath != null && profileImagePath.isBlank()) {
return CreateMemberDto(email = email, password = password, nickname = nickname, profileImagePath = null)
}
return CreateMemberDto(email = email, password = password, nickname = nickname, profileImagePath = profileImagePath)
}
}
해당 클래스에 대한 테스트코드를 작성했는데, 다음과 같습니다.
internal class CreateMemberRequestTest {
companion object {
private val objectMapper = ObjectMapper()
private var JSON_STRING_FORMAT = """
{
"email":"%s",
"password":"%s",
"nickname":"%s",
"profileImagePath":"%s"
}
""".replace("\t","").replace("\n","").trim()
}
@Test
fun `Json - CreateMemberRequestTest 변환 테스트`() {
//given
val cmr = createMemberRequest()
val json = JSON_STRING_FORMAT.format(cmr.email, cmr.password, cmr.nickname, cmr.profileImagePath)
//when
val readValue = objectMapper.readValue(json, CreateMemberRequest::class.java)
//then
assertThat(cmr).isEqualTo(readValue)
}
}
그리고 다음과 같이 기본 생성자가 없어 deserialize가 불가능하다는 오류가 발생합니다.
이때 ObejctMapper에 아래와 같이 KotlinModule을 추가해주면 오류가 해결됩니다.
private val objectMapper = ObjectMapper().registerModule(KotlinModule.Builder().build())
정확한 원리는 아직 모르겠습니다... ㅠㅠ
ObjectMapper의 조건을 변경하여 기본 생성자 없이 사용할 수 있으나,
@RequestBody에서 사용되는 ObjectMapper와 테스트코드에서 사용되는 ObjectMapper의 속성(visibility)은 모두 동일했습니다.
원리는 모르겠지만, 아무튼 코틀린에서 @RequestBody를 쓰면 기본 생성자 없이 어떻게 바인딩 되는지 확인해 보도록 하겠습니다.
@RequestBody를 사용하면 AbstractJAckson2HttpConverter(구현체는 MappingJackson2HttpMessageConverter)가 ObjectMappper를 통해 값을 바인딩 해줍니다.
이때 AbstractJAckson2HttpConverter에 등록되는 ObjectMapper는 다음과 같은 모듈들을 가지고 있습니다.
KotlinModule을 가지고 있으므로 기본 생성자가 없는 data class도 값이 역직렬화가 가능해지는 것입니다.
'코틀린 > web' 카테고리의 다른 글
[코틀린][스프링 MVC] - 코틀린에서 @Valid가 작동하지 않을 때 (0) | 2022.07.27 |
---|