spring-data redis serializer 사용하기

redis의 데이터 저장 형식은 byte array형태이며, 

사용하는 쪽에서 포맷을 정하여 저장하고 불러와야 한다.


spring-data redis는 자주 쓸만한 포맷을 편하게 쓸 수 있도록 시리얼라이저 클래스로 내장되어 있다.

커스텀 클래스를 저장할 때, 시리얼라이저를 이용하면 유용하게 쓸 수 있다.


테스트로 사용할 커스텀 클래스 코드


대표적인 시리얼라이저만 보자.

JdkSerializationRedisSerializer

디폴트 시리얼라이저이며, 다른 시리얼라이저를 지정하지 않는다면 기본적으로 사용된다.

User user = new User(); user.setId(1); user.setName("warwick"); user.setLevel(Level.SILVER); redisTemplate.opsForValue().set("key", user);

redis-cli에서 저장된 것을 확인해 보자.

127.0.0.1:6379> get key (nil)

왜 안나올까?

이유는 자바 클래스와 필드정보가 부가적으로 저장되어 있기 때문이다.

그렇다면 키값이 어떻게 저장되었는지 보자.

127.0.0.1:6379> keys * 1) "\xac\xed\x00\x05t\x00\x03key"

저장된 키값을 이용해 값을 확인해 보면 다음과 같다.

127.0.0.1:6379> get "\xac\xed\x00\x05t\x00\x03key" "\xac\xed\x00\x05sr\x00\x18com.yakolla.mvctest.User!\xeb#\xb3\x17e1\x19\x02\x00\x03I\x00\x02idL\x00\x05levelt\x00\x1bLcom/yakolla/mvctest/Level;L\x00\x04namet\x00\x12Ljava/lang/String;xp\x00\x00\x00\x01~r\x00\x19com.yakolla.mvctest.Level\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00xr\x00\x0ejava.lang.Enum\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00xpt\x00\x06SILVERt\x00\awarwick"


StringRedisSerializer

jdk~처럼 지저분한(?) 값을 붙이지 않고 저장한다.

redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); User user = new User(); user.setId(1); user.setName("warwick"); user.setLevel(Level.SILVER); redisTemplate.opsForValue().set("key", user);

저장을 시도하면 다음과 같은 오류가 발생한다.

HTTP Status 500 - Request processing failed; nested exception is java.lang.ClassCastException: com.yakolla.mvctest.User cannot be cast to java.lang.String

말그대로 User class를 String으로 캐스팅할 수 없다는 오류다.

커스텀 클래스를 String으로 캐스팅 되도록 구현하거나 다른 시리얼라이저를 이용하는게 속편하다.


JacksonJsonRedisSerializer

json 형태로 저장한다.

이번에 key는 String으로 저장하되, value는 json으로 저장해보자.

redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new JacksonJsonRedisSerializer(User.class)); User user = new User(); user.setId(1); user.setName("warwick"); user.setLevel(Level.SILVER); redisTemplate.opsForValue().set("key", user);

저장을 시도하면 다음과 같은 오류가 발생한다.

HTTP Status 500 - Handler processing failed; nested exception is java.lang.NoSuchMethodError: org.codehaus.jackson.map.type.TypeFactory.defaultInstance()Lorg/codehaus/jackson/map/type/TypeFactory;

json mapper class의 버전이 다르거나 라이브러리가 없을 때 발생한다.

pom.xml에 추가하자

다시 실행하면, 정상적으로 저장된다.

어떻게 저장되었는지 확인해 보자.

127.0.0.1:6379> keys * 1) "key" 127.0.0.1:6379> get key "{\"name\":\"warwick\",\"id\":1,\"level\":\"SILVER\"}"

저장된 값 얻어오기

기본 문법은 다음과 같다.

User user = (User)redisTemplate.opsForValue().get("key");

주의사항

커스텀 클래스 형태로 저장된 값을 얻어올 때는 주의가 필요하다.

저장된 이후에 커스텀 클래스가 변경되면, 변경된 정도에 따라 사용한 시리얼라이저 마다 오류가 다르다.


1)json 시리얼라이저라면

저장할 때 사용했던 커스텀 클래스의 프로퍼티 함수들이 변경되면, 오류 발생

커스텀 클래스의 새로운 프로퍼티 함수를 추가하면, 오류 발생하지 않음.

이름으로 프로퍼티를 매핑하니까 괸찮은 거다.


2)jdk 시리얼라이저라면

저장할 때 사용했던 커스텀 클래스의 프로퍼티 함수들이 변경되면, 오류 발생

커스텀 클래스의 새로운 프로퍼티 함수를 추가하면, 오류 발생.

커스텀 클래스의 버전이 저장되 있어, 수정하면 버전 불일치로 오류가 발생한다.


버전 불일치시, 다음과 같은 오류가 발생한다.

HTTP Status 500 - Request processing failed; nested exception is org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.InvalidClassException: com.yakolla.mvctest.User; local class incompatible: stream classdesc serialVersionUID = 2444086474877251865, local class serialVersionUID = 5789808414104702375