티스토리 뷰
위 글에서 이어지는 글이다
많은 우여곡절 끝에 HATEOAS를 적용한 PagedModel 형태에 캐시 추상화 @Cacheable을 접목했다
정확하게 말해 Page를 캐시하고 Controller 단에서 Page 형태의 캐시 데이터를 PagedModel로 변환하여 반환한다
지난 글에서의 문제점은 _links 가 깨진다는 점이었다
사실 위 문제를 해결하고도 PagedModel에 캐시를 적용하는 과정에서 수많은 시간을 쓰고도 실패했다
class java.util.LinkedHashMap cannot be cast to class org.springframework.data.domain.Page
에러 로그를 만나면서 가슴 아픈 시간을 보냈다
java.lang.ClassCastException: class java.util.LinkedHashMap
cannot be cast to class org.springframework.data.domain.Page
(java.util.LinkedHashMap is in module java.base of loader 'bootstrap';
org.springframework.data.domain.Page is in unnamed module of loader 'app')
at com.woomoolmarket.service.member.MemberService$$EnhancerBySpringCGLIB$$7b31efb5.findAllMembers(<generated>)
LinkedHashMap은 애플리케이션이 띄워지면서 bootstrap loader에 의해 앱에 올라가는 놈이고
Page는 app loader에 의해 올라가는 놈이라 변환할 수 없다는 것이다
문제 파악하는 데에도 시간이 꽤 걸렸고 문제 해결은 덮어놓고 나중에 생각해야지 하다가 한 달 정도 걸린 것 같다
class java.util.LinkedHashMap cannot be cast to [Custom Object]는 생각보다 자주 만날 수 있는 에러인데
Redis에 저장된 데이터를 꺼내올 때 타입이 지정되지 않으면 LinkedHashMap으로 읽어오면서 발생하는 문제다
즉 레디스에서 반환될 때의 데이터 타입을 추정할 수 없어 터지는 것이다
간단한 해결 방법으로 objectMapper.readValue([Custom Object], new TypeReference<어쩌구>)으로 풀면 되지만
조금 깊게 보면 캐시란 것은 핵심 비지니스와 관련이 없다 엄격한 잣대로 로직에 캐시 관련 로직이 침투해서는 안 된다
자바 코드로 해결하지 않고 캐시 추상화를 사용하기 위해 아등바등했다
그럼 이 문제를 풀 때 그다음으로 쉽게 푸는 방법이 무엇이 있을까?
난 이전에 CacheManager에 ValueSerializer에 GenericJackson2JsonRedisSerializer를 지정해줬다
얘가 레디스에서 데이터 꺼내올 때 타입을 자꾸 못 찾았으니 다른 Serializer를 사용하면 된다
기본 Serializer인 JdkSerializationRedisSerializer를 넣어주면 byte 형태로 저장된다
내가 설정하고 최근까지 사용 중인 CacheManger는 아래와 같다
@Primary
@Bean(name = "jdkCacheManager")
public RedisCacheManager jdkCacheManager() {
RedisCacheConfiguration configuration = RedisCacheConfiguration
.defaultCacheConfig()
.disableCachingNullValues()
.entryTtl(Duration.ofDays(1))
.serializeKeysWith(SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(SerializationPair.fromSerializer(new JdkSerializationRedisSerializer()));
return RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(configuration)
.build();
}
위 CacheManager를 이용해 데이터를 반환받아보면 아래와 같이 이쁘게 나온다
이걸로 해결된 걸까?
JdkSerializationRedisSerializer로는 반만 해결됐다
어쨌거나 저장, 반환에는 문제없지만 Redis에 저장된 데이터를 볼 때가 문제다
난 Redis GUI 툴로 Medis를 사용 중인데 JdkSerializationRedisSerializer를 사용해 저장된 데이터는
Hex 형태로 보이고 Viewer type을 JSON이나 plain으로 바꾸면 데이터가 보이지 않는다
레디스에 저장된 데이터 관리가 필요하다면 사용할 수 없는 방법이겠다
다른 방법으로도 풀어보려고 여러 시도를 했다
ObjectMapper에 타입을 알려줘야 하므로 HATEOAS와 관련된 HalModule 등을 넣어주고 이놈을 주입받아서
GenericJackson2JsonRedisSerializer()의 인자로 넣어서 해보기도 했는데 ClassCastException의 벽을 넘을 수 없었다
일단은 HATEOAS + @Cacheable 적용은 해결했으니 한 타임 쉬어가기로 하고 추후 더 개선해봐야겠다
자료를 조금 더 찾아본 결과로는
1. PagedModel을 직접 Serialize, Deserialize 하는 모듈을 만들어서 ObjectMapper에 넣어주는 방법이 있고
2. PagedModel을 상속받고 상속받은 놈에 implements Serializable을 하는 방법이 있을 것 같은데
둘 다 만만한 작업이 될 것 같진 않아 후일을 기약해야겠다
만약 이에 대해 좋은 방법을 알고 계시는 분이 있다면 알려주시면 참 감사하겠습니다 😭
개선본
'Spring > Spring Data' 카테고리의 다른 글
[JPA] 간접 참조 연관 관계 EventListener로 삭제하기 - 1 (0) | 2022.10.02 |
---|---|
[Redis] HATEOAS와 @Cacheable - 3 (0) | 2021.12.25 |
[Redis] LocalDateTime Serialization / Deserialization 삽질기 - 2 (4) | 2021.12.22 |
[Redis] HATEOAS와 @Cacheable 같이 쓰기 (0) | 2021.10.02 |
[Redis] HATEOAS와 @Cacheable (0) | 2021.09.20 |