Back-end/Spring

Spring Boot와 Redis 연동(2) : Redis Serializer

songu1 2025. 5. 4. 23:59

 

지난 포스트에서의 Spring Boot와 Redis 연동 과정에서 언급했던 RedisSerializer 선택 기준에 대해 이 포스트에서 다뤄보고자 한다.

 

Spring은 총 3가지 직렬화/역직렬화 클래스를 제공한다.

JdkSerializationRedisSerializer, GenericJackson2JsonRedisSerializer, Jackson2JsonRedisSerializer 3가지를 지원한다.

각 Serializer에 대해 알아보자.

 

[1] JdkSerializationRedisSerializer

JdkSerializationRedisSerializer는 Java의 기본 직렬화 기법인 JDK 직렬화를 사용하여 객체를 처리한다. RedisTemplate의 기본 직렬화 방식으로, 별도로 등록된 Serializer가 없다면 JdkSerializationRedisSerializer를 사용한다. 스프링에서 제공하는 @Cacheable과 @CacheEvict 등과 같은 캐시 추상화 설정을 위한 RedisCacheConfiguration 내부에서 사용되는 기본 직렬화기도 이에 해당한다. JDK 직렬화는 직렬화한 값에 객체 필드의 패키지 위치와 타입이 저장되며, 역직렬화 시 사용된다.

 

JdkSerializationRedisSerializer는 장점과 문제점을 가지고 있다.

장점

장점은 객체마다 Class Type 등 메타 정보를 같이 저장하기 때문에, 객체별로 직렬화/역직렬화에 대한 설정을 해주지 않아도 자동으로 해준다는 점이다.

문제점

문제점은 자바 기본 직렬화 방식을 사용하므로 자바 기본 직렬화가 가지는 문제를 그대로 가진다.

  • 반드시 Serializable 인터페이스를 구현해야한다. 구현하지 않으면 런타임에 직렬화할 때 NotSerializableException이 발생한다. 이 예외가 발생할 것이라는 것을 컴파일 타임에는 알 수 없다.
  • 직렬화할 때의 멤버 변수의 메타 정보와 역직렬화할 때 메타 정보가 다르면 예외가 발생하고 역직렬화에 실패할 수 있다.
  • serialVersionUID(클래스 해시값)을 설정하지 않았을 경우 문제가 생길 수 있다. serialVersionUID를 설정하지 않으면 기본 해시값을 serialVersionUID으로 사용하여 역직렬화 시 직렬화된 객체와 비교한다. 이 때 직렬화 시 serialVersionUID와 역직렬화 시 serialVersionUID가 다르면 InvalidClassException이 발생한다.
  • 클래스 메타 정보(패키지, 타입)가 포함되어 용량을 많이 차지한다. 비교적 큰 데이터를 캐시한다면 각 멤버 변수별로 메타 정보를 가지므로 용량이 확 커질 수 있다.
  • 자바 시스템에서만 직렬화/역직렬화가 가능하다.

따라서 자주 변경되는 객체를 대상으로 사용하는 것은 지양해야한다.

 

 

[2] GenericJackson2JsonRedisSerializer

Jackson라이브러리이며, 객체를 JSON형태로 직렬화한다. 내부적으로 ObjectMapper를 사용하는데 , 파라미터로 전달되는 ObjectMapper가 있으면 이를 활용하고 없으면 직접 ObjectMapper를 생성하여 사용한다. ObjectMapper 설정이 추가되면 직렬화 시에 클래스 정보가 포함된다. JSON 문자열 자체를 저장하므로 저장 용량을 많이 차지한다.

장점

장점은 별도의 Class타입을 설정해주지 않아도 자동으로 직렬화/역직렬화를 해준다는 것이다.

문제점

  • 직렬화할 때의 멤버 변수의 메타 정보와 역직렬화할 때 메타 정보가 다르면 예외가 발생하고 역직렬화에 실패할 수 있다.
    • 클래스 정보가 포함되어 패키지 이동 및 클래스명 변경 등의 경우, 해당 객체를 찾을 수 없음. InvalidClassException 이 발생하여 역직렬화에 실패할 수 있다.
    • 특정 애플리케이션 객체 패키지 정보에 의존적인 구조가 되어 변경에 취약하며, MSA와 같이 여러 애플리케이션이 상호작용하며 같은 데이터를 사용하는 프로젝트의 경우 모두 같은 패키지 구조를 가져야한다.
  • @class로 직렬화하는 객체의 메타 정보를 저장하므로 대용량 시스템에서는 당연히 용량 문제가 발생 가능하다.

따라서 수정에 취약하고 MSA 환경에서는 사용하기 힘들다. 용량 문제가 큰 영향을 끼치지 않으면 직렬화/역직렬화에 대한 설정을 해줄 필요가 없는 장점을 가지고 있다.

 

[3] Jackson2JsonRedisSerializer

Jackson라이브러리로, 객체를 JSON형태로 직렬화한다. 클래스 정보가 포함되지 않으므로 항상 타입을 지정해줘야 한다. 일반적으로 캐시는 여러 객체를 대상으로 범용적으로 사용되므로, 모든 캐시 대상 객체별로 Jackson2JsonRedisSerializer 객체를 생성해줘야한다. 단일 Serializer를 가지고도록 설계되었다고 이해하면 된다. (RedisConnectionFactory 1 : N RedisTemplate)

JSON 객체를 그대로 바이트 배열로 변환하여 저장하는 방식이다.

 

장점

직렬화 객체의 메타 정보를 저장하지 않아 versioning 이슈가 해결되며, 메타 정보로 인한 용량 이슈가 해결된다.

 

문제점

Class타입 문제를 가지고 있다.

RedisTemplate 설정에 매번 ClassType을 지정해줘야하며, ClassType별로 RedisTemplate을 지정해줘야한다.

  • RedisTemplate은 지정한 Class Type만을 직렬화/역직렬화한다.
  • Class Type 종류가 많아지면 단점이 될 수 있다
  • (ClassType문제) ClassType이 없으므로 의도치 않은 ClassCastException 문제 발생 가능하다
    • Value를 Object로 설정하고, Jackson2JsonRedisSerializer를 사용하면 역직렬화시 ObjectMapper는 변환할 구체적인 타입 정보를 알 수 없다
    • ObjectMapper는 임의로 LinkedHashMap과 같은 기본 컬렉션 타입으로 역직렬화를 시도하게 되어 개발자가 의도한 특정 클래스로 캐스팅하려고 할 때 ClassCastException이 발생한다

 

 

 

비교

Serializer  메타정보 버전 이슈 serialVersionUID 버전 이슈 ClassType 용량 이슈 ClassType별 지정 이슈 호환성 이슈
JdkSerializationRedisSerializer ✖️
GenericJackson2JsonRedisSerializer ✖️ ✖️
Jackson2JsonRedisSerializer ✖️ ✖️ ✖️ ✖️

 

 

 


참고 자료

https://mangkyu.tistory.com/402

https://github.com/binghe819/TIL/blob/master/Spring/Redis/redis serializer/serializer.md