반응형
문제 상황
Redis Cache를 사용해서 List<Entity>를 저장하려고 했습니다.
엔티티의 모든 데이터를 저장하는 것은 비효율적이고 참조된 엔티티와의 순환 참조 때문에 List<DTO> 형태로 저장하기로 했습니다.
기존 캐싱 적용 코드
@Override
@Cacheable(value = "nowRecruit", key = "'sorted'")
public List<Recruit> getNowRecruitOrderByClass(LocalDateTime now) {
return recruitRepository.findNowRecruitOrderByClass(now);
}
캐싱한 데이터는 위 코드와 같습니다.
List<Recruit>를 응답으로 반환하고 **nowRecruit**라는 캐시의 **sorted**라는 키 값으로 저장됩니다.
Redis 설정으로 Value는 GenericJackson2JsonRedisSerializer 를 사용해 직렬화합니다.
에러 발생
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)
GenericJackson2JsonRedisSerializer는 직렬화할 때 @Class라는 Key 값에 클래스의 패키지 정보까지 전부 저장됩니다.
그런데 List를 통째로 저장하면 { "@class": "..." } 이 아니라 [{ "@class": "..."}] 로 저장되어 있어 해당 패키지를 찾지 못하는 이슈가 발생합니다.
문제 해결
List를 감싸는 Wrapper 클래스를 만들어 해결합니다.
NowRecruits 래퍼 클래스 정의
public static class NowRecruits {
private List<NowRecruit> nowRecruits = new ArrayList<>();
public NowRecruits(List<NowRecruit> recruits) {
this.nowRecruits = recruits;
}
}
- NowRecruits는 List<NowRecruit> 정보를 가지고 있는 클래스입니다.
변경 캐싱 적용 코드
@Override
@Cacheable(value = "nowRecruit", key = "'sorted'")
public NowRecruits getNowRecruitOrderByClass(LocalDateTime now) {
List<NowRecruit> recruits = recruitRepository.findNowRecruitOrderByClass(now);
return new NowRecruits(recruits);
}
레디스 캐싱 결과
배운점
스프링에서 공통적으로 조회하는 데이터를 레디스에 캐싱해서 성능을 최적화하려고 했습니다.
저장하는 값이 List 형이어서 직렬화 / 역직렬화 하는 과정에서 이슈가 있었지만, 래퍼 클래스를 만들어 사용하면서 List<DTO>를 캐싱하는 방법과 래퍼 클래스란 무엇인지, 왜 사용하는지 알게 되는 시간이었습니다.
반응형