상황
Redis Cache 를 사용해서 List<?>
를 저장하려고 했습니다.
직렬화해서 데이터 저장까지는 잘 되었는데 다시 역직렬화 하려고 하니 에러가 발생하며 실패했습니다.
@Cacheable(cacheNames = "members", key = "'all'")
public List<Member> findAll() {
List<Member> members = store.values().stream().toList();
return members;
}
캐싱한 데이터는 위와 같습니다.
List<Member>
를 응답으로 내려주고 members
라는 캐시의 all
이라는 키값으로 저장됩니다.
Redis 설정으로 Value 는 GenericJackson2JsonRedisSerializer
를 사용하여 직렬화했습니다.
Redis 를 확인해보면 제대로 저장된 것을 확인할 수 있습니다.
에러 로그
com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected VALUE_STRING: need JSON String, Number of Boolean that contains type id (for subtype of java.lang.Object)
at [Source: (byte[])"[{"@class":"com.example.springbootcache.model.Member","id":1,"name":"ChulSoo","age":50}]"; line: 1, column: 2]
원인
List<?> 를 그대로 저장해서 그렇습니다.
사실 정확한 원인은 저도 모릅니다.
그래도 확신은 없지만 나름대로 추측을 해보겠습니다.
GenericJackson2JsonRedisSerializer
는 직렬화 할 때 @class
라는 Key 값에 클래스의 패키지 정보까지 전부 저장됩니다.
그런데 List 를 통째로 저장하면 위 사진과 같이 { "@class": "..." }
이 아니라 [{ "@class": "..."}]
로 저장되어 찾지 못해서 발생하는 이슈 같습니다.
해결
List 를 감싸는 Wrapper 클래스를 만들어 주면 해결됩니다.
Members 클래스 정의
@Getter
@NoArgsConstructor
public class Members {
private List<Member> members = new ArrayList<>();
public Members(List<Member> members) {
this.members = members;
}
}
캐싱 대상 변경
@Cacheable(cacheNames = "members", key = "'all'")
public Members findAll() {
List<Member> members = store.values().stream().toList();
return new Members(members);
}