Redis 캐싱을 적용하던중
캐시를 적용해서 객체를 json으로 컨버팅중
Java 8 date/time type `java.time.LocalDate` not supported by default:
add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
to enable handling
위와 같은 에러가 발생했다.
해석하자면 java 8에서 추가된 localdate는 기본적인 직렬화를 제공하지않아, 추가적으로 모듈을 설치하란 안내이다.
위에 것을 추가했지만, 여전히 에러가 떳고 ,LocalDate 필드에 해결방안을 찾았다.
ObjectMapper를 커스터마이징해서, redistemplate과 cacheConfig에 set 하는것이다.
package code.sns.config.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@Configuration
public class JacksonConfiguration {
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Bean
@Primary
public ObjectMapper serializingObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
public class LocalDateSerializer extends JsonSerializer<LocalDate> {
@Override
public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.format(FORMATTER));
}
}
public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
@Override
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return LocalDate.parse(p.getValueAsString(),FORMATTER);
}
}
}
해당 설정파일을 사용해서, 직렬화와 역직렬화의 값을 세팅해주고 , @Bean에 @Primary를 달아줘서 맨 처음 주입되도록 한다.
package code.sns.config.redis;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
@RequiredArgsConstructor
public class RedisConfig {
@Value("${spring.redis.port}")
public int port;
@Value("${spring.redis.host}")
public String host;
private final ObjectMapper objectMapper;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host,port);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
new Jackson2JsonRedisSerializer<>(Object.class);
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 중요 여기에 objectmapper를 넣어준다
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
해당 설정은 redistemplate을 설정해준다. 여기서 valueSerializer에 GenericJackson@JsonRedisSerailizer(objectMapper)
를 잘 작성하자.
마지막이다. 캐시매니저를 설정하자
package code.sns.config.redis;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Slf4j
@Configuration
@RequiredArgsConstructor
public class CacheConfig {
private final RedisConnectionFactory redisConnectionFactory;
private final ObjectMapper objectMapper;
@Bean
public CacheManager redisCacheManager() {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
//아까와 마찬가지로 설정
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)))
.entryTtl(Duration.ofSeconds(60));
return RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration)
.build();
}
}

Json 형식으로 잘 ~ 캐싱되고 있다.\
해당 내용에 관한 stackoverflow이다.
serialize/deserialize java 8 java.time with Jackson JSON mapper
How do I use Jackson JSON mapper with Java 8 LocalDateTime? org.codehaus.jackson.map.JsonMappingException: Can not instantiate value of type [simple type, class java.time.LocalDateTime] from JSON
stackoverflow.com
궁금한점은 restapi 로 dto들을 반환할때 문제없이 localdate들이 반화이 되었다.
그런데 왜 캐싱을 걸때 왜... 문제가 생겼을까 추후에 찾아서 포스팅 해보도록 하겠다.