RedisTemplate 을 이용해서 Multi Pojo get/set 할 때 이슈사항
RedisTemplate 을 이용해서 Multi Pojo get/set 할 때 이슈사항
Spring Boot에서 제공하는 RedisTemplate 을 이용하면 쉽게 Redis 데이터를 Get/Set 할 수 있다.
이 때, Data를 Serialize 하는 방법을 설정할 수 있는데 org.springframework.data.redis.serializer 패키지에서 확인할 수 있다.
다양한 모델의 데이터를 Redis에서 조회해올 때 겪었던 이슈사항과 최종적으로 택한 방법을 정리하고자 한다.
아래 내용은 나와 정말 똑같은 고민을 가진 사람이 쓴 글이다 ㅋㅋ
https://cnpnote.tistory.com/entry/SPRING-Spring-RedisTemplate-%EC%97%AC%EB%9F%AC-%EB%AA%A8%EB%8D%B8-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A5%BC-JSON%EC%9C%BC%EB%A1%9C-serialize%ED%95%A9%EB%8B%88%EB%8B%A4-%EC%97%AC%EB%9F%AC-RedisTemplates%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%A9%EB%8B%88%EA%B9%8C
1. GenericJackson2JsonRedisSerializer
첫번째로 시도했던 Serializer이다. 이 Serializer은 별도로 Class Type을 지정해줄 필요없이 자동으로 Object를 Json으로 직렬화 해주는게 장점이다.
@Bean public RedisTemplate redisTemplate() { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(jedisConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; }
그러나 치명적인 단점이 있었다.
바로, Object의 Class Type을 함께 레디스에 넣는다는 것이다.
"{\"@class\":\"com.prnv.model.WhitePaper\",\"title\":\"Hey\",\"author\":{\"@class\":\"com.prnv.model.Author\",\"name\":\"Hello\"},\"description\":\"Description\"}"
위와 같이 @class 로, 이 Dto의 패키지까지 모두 포함되어 저장이 된다.
이게 왜 치명적이냐 함은, 이 데이터를 꺼내올때 (GET) @class로 지정된 동일한 패키지에 있는 Dto로 밖에 가져올 수 없다는 것이다.
이런식으로 데이터가 저장된다면, 다른 Application에서는 무조건 저 Class를 생성하고, 무조건 저 루트로 같은 이름으로 Object를 생성해 놔야 한다.
MSA 프로젝트란 무엇인가. 여러 Application API 들이 서로 상호작용하는게 기본이다. 이렇게 데이터가 저장된다면 MSA API 들은 Data Class Type 에 묶여버리게 된다.
2. Jackson2JsonRedisSerializer
위의 Generic Serializer를 버리고 Jackson2JsonRedisSerializer 이 Serializer를 택했다.
이 Serializer은 Class Type 형을 명시한다. 위와 같이 @class 를 포함해서 저장하지 않는다.
단점은, 항상 Class Type을 지정해줘야 한다는 것이다.
하지만 이부분은 메소드를 공통화 시키면 쉽게 풀리는 일이었다.
public boolean setHashData(String key, String hashKey, Object value) throws Exception { redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer(value.getClass())); redisTemplate.opsForHash().put(key, hashKey, value); return true; }
위와 같이 저장하고자하는 PoJo Type을 파라미터로 받아서, Jackson2JsonRedisSerializer를 생성한다.
문제가 해결된듯 보였다. 하지만 또 다른 문제가 발생한다.
바로, 이렇게 되면 각 Class Type은 따로따로 RedisTemplate을 생성해야 한다는 것이다.
Serializer는 redisTemplate에 설정하는 것이다. 그리고 이 redisTemplate은 (의도한것은) 모든 서비스에서 불러다 쓰는 공통 Service이다.
각 서비스들이 이 Resource 에 접근해서 Seriazlier를 바꿔치기 해버리면 어떻게 될까?
동기 호출이면 모를까, 여러 API들이 호출될텐데, 각 스레드들이 redisTemplate에 접근해서 데이터를 Get 하다가 Serializer가 바꿔치기 되버려서 결국 Serialize 에러가 나버린다.
눈물..
대안은 두가지였다.
1. 각 Dto를 가지는 API 들이 Redis 에서 Get/Set할때 각자 고유한 RedisTemplate을 생성해서 가지고 있는다.
2. Generic으로 돌아와서, 모든 @class 타입을 맞춰버린다.
두 가지 다 너무 구렸다. 그래서 생각을 해낸게 JacksonSerializer를 쓰지 말고 StringSerializer를 쓰고 Json으로 파싱하는 것은 우리가 직접 하자!!
3. StringRedisSerializer
@Bean public RedisTemplate redisTemplateTest() { RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new StringRedisSerializer()); return redisTemplate; }
/** * Redis 데이터 조회 * @param * @param key * @param classType * @return * @throws Exception */ public T getData(String key, Class classType) throws Exception { String jsonResult = (String) redisTemplate.opsForValue().get(key); if (StringUtils.isBlank(jsonResult)) { return null; } else { ObjectMapper mapper = new ObjectMapper(); T obj = mapper.readValue(jsonResult, classType); return obj; } }
깔-끔
Spring 에서 제공하는 RedisTemplate 메소드가 Develop 되면 모를까.. 영문 자료밖에 없고 힘들었지만, 나름 뿌듯하네
참고로, RedisTemplate 하나만으로 여러 스레드들이 작동할 수 있는것은 Lettuce가 멀티 스레드를 지원하기 때문이다.
자세한 내용은 아래 공식문서를 보자
https://docs.spring.io/spring-data/redis/docs/current/reference/html/#redis:serializer
from http://mongsil-jeong.tistory.com/25 by ccl(A) rewrite - 2020-03-11 19:20:27
댓글
댓글 쓰기