Redis 직렬화/역직렬화시 생기는 Issue
Redis를 사용하던 와중 특정 오류가 잡혀 골치 아픈 적이 있었다.
public class CacheConfig {
private final RedisConnectionFactory redisConnectionFactory;
@Bean
public CacheManager cacheManager() {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.
SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(Duration.ofSeconds(30));
return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration).build();
}
}
기존 코드는 이렇다. 이후 잘 동작하나 확인하기 위해 Postman을 사용해봤더니 문제가 발생하였다....
org.springframework.data.redis.serializer.SerializationException: Could not write JSON: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: me.golf.blog.domain.board.dto.BoardDTO["lastModifiedAt"]); nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: me.golf.blog.domain.board.dto.BoardDTO["lastModifiedAt"])
at org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer.serialize(GenericJackson2JsonRedisSerializer.java:124) ~[spring-data-redis-2.6.3.jar:2.6.3]
at org.springframework.data.redis.serializer.DefaultRedisElementWriter.write(DefaultRedisElementWriter.java:44) ~[spring-data-redis-2.6.3.jar:2.6.3]
Jackson에서 Java 8에서 추가된 LocalDateTime 타입을 직렬화/역직렬화 하지 못해서 문제가 발생한 것이다. (본 프로젝트에서는 Redis를 사용해서 데이터 캐싱을 하고 있는 데 이 때 객체를 직렬화 하여 저장한다.)
직렬화 할 때 전략을 바꿔줄 필요성이 있다. 그래서 아까 해당 필드에 다음과 같이 추가적으로 @Bean을 등록해주었다.
public class CacheConfig {
private final RedisConnectionFactory redisConnectionFactory;
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper()
.findAndRegisterModules()
.enable(SerializationFeature.INDENT_OUTPUT)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.registerModule(new JavaTimeModule());
}
@Bean
public CacheManager cacheManager() {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.
SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper())))
.entryTtl(Duration.ofSeconds(30));
return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration).build();
}
}
위 코드를 설명하자면
- .enable(SerializationFeature.INDENT_OUTPUT)
- Jackson에서 Json을 좀 더 이쁘게 표현해준다.
- .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
- Timestamp 형태로 저장하지 않게 설정한다.
- .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
- Timestamp 형태로 저장하지 않게 되면 LocalDateTime은 지원하지 않는 형식이 되어버린다. 그럼 에러가 발생하는데 이러한 현상을 막아줄 수 있게 false로 바꾸어 무시하게 한다.
- .registerModule(new JavaTimeModule())
- 역 직렬화 시 LocalDateTime 형식으로 저장할 수 있게 JavaTimeModule을 이용하여 Timestamp로 객체에 저장하게 도와준다.
자 모두 마쳤다. 이제 다시 postman으로 다시 테스트를 해보자
정상적으로 조회 되고 LocalDateTime 타입인 lastModifiedAt도 정상적으로 역직렬화가 되는 것을 확인할 수 있다.