data class로 레디스에 객체를 역직렬화하는 과정에서 다음과 같은 에러가 발생했다.
에러 내용은 ttl이라는 필드가 non-nullable인데, 실제로는 null이 들어가서객체 생성에 실패했다는 것이다.
Could not read JSON: Instantiation of [simple type, class ...] value failed for JSON property ttl due to missing (therefore NULL) value for creator parameter ttl which is a non-nullable type
class는 대략 다음과 같은 형태였다.
분명 @JsonIgnore 애너테이션으로 직렬화/역직렬화에서 해당 프로퍼티를 제외했는데, 왜 직렬화/역직렬화에 포함된 건지 궁금했다.
data class RedisObject(
val no: Long,
val name: String,
...
@JsonIgnore
val ttl: Duration,
)
답은 스택오버플로우에서 찾을 수 있었다.
우선 @JsonIgnore는 대응되는 JSON 필드를 무시한다. Java에서 Jackson은 역직렬화 시JSON 필드가 존재하지 않는다면 대응되는 객체의 필드에 null을 세팅한다. 이는 Java에서 기본적으로 참조 타입의 변수엔 null이 담길 수 있기 때문이다. 그러나 Kotlin은 Java와 달리 non-nullable 타입이 존재한다. 여기에 null이 담기려고 해서 에러가 발생한 것이다.
정리하자면 @JsonIgnore를 프로퍼티에 붙여서 역직렬화 시 해당 프로퍼티 값으로 null이 세팅되려고 했으나, 프로퍼티의 타입이 non-nullable인 관계로 객체 생성이 실패한 것이다.
해결 방법은 크게 3가지가 있다.
1) default value 셋팅
data class RedisObject(
val no: Long,
val name: String,
...
@JsonIgnore
val ttl: Duration = Duration.ofMinutes(1),
)
2) nullable한 타입 사용
data class RedisObject(
val no: Long,
val name: String,
...
@JsonIgnore
val ttl: Duration?,
)
3) 지연 초기화 사용
data class RedisObject(
val no: Long,
val name: String,
...
) {
@JsonIgnore
lateinit var ttl: Duration
}
나는 default value를 넣어주거나, 타입을 바꿔주는 것은 싫어서 조금 번거롭더라도 지연 초기화(3번)를 사용해 문제를 해결했다.
상황에 따라 적절하게 선택하면 될 것 같다.
참고로 lateinit 키워드를 사용하면 최초에 프로퍼티가 null로 초기화된다. 그래서 non-nullable한 타입으로 선언하더라도 null이 들어갈 수 있는 것이다. 실제 사용하는 시점에는 초기화가 되어야 한다.
'프로그래밍 언어 > Java + Kotlin' 카테고리의 다른 글
[Kotlin] 쿠폰적용가 성능 개선기 (1) | 2023.11.18 |
---|---|
[Java] 제네릭 타입 소거(Generic Type Erasure)에 대해 알아보자 (0) | 2023.08.02 |
[Java] Collection과 Collections의 차이 (0) | 2023.03.16 |
[Java] 생성자 총정리 (0) | 2023.01.06 |
[Kotlin] 코틀린에서 애너테이션 사용하기 (vs 자바) (0) | 2022.12.19 |