카테고리 없음

Redis 캐시와 객체 직렬화

hyeongjune 2025. 1. 22. 22:47

문제 발생 배경

Spring 애플리케이션에서 Redis를 사용하여 Hashtag 객체를 캐시할 때 발생한 직렬화 문제를 다룹니다.

문제는 Hashtag 객체를 Redis에 저장하고, 저장된 데이터를 가져오는 과정에서 발생했습니다.

Jackson의 직렬화 문제와 Redis에서 데이터를 올바르게 조회하는 과정이 문제가 되었습니다.

문제 분석

  • Redis 데이터 저장 타입 : Hashtag 서비스레이어에서 데이터가 Set 형식으로 반환하여 json 형식으로 직렬화와 역직렬화를 위한 적절한 설정 필요
  • LocalDateTime과 같은 Java 8의 날짜/시간 타입을 직렬화 문제: Hashtag 객체에 포함된 LocalDateTime 필드가 Jackson이 기본적으로 처리할 수 없었습니다. 이를 해결하기 위해 jackson-datatype-jsr310 모듈을 추가해야 했습니다.
  • StackOverflowError: Hashtag 와 Article 클래스가 양방향 관계를 갖고 있기 때문에, 직렬화 시에 서로 참조하게 되어 순환 참조 문제 발생 

해결 방법

build.gradle 에 추가

implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.4'  // Jackson JSR310 모듈 추가

 

Java 8 LocalDateTime 타입을 직렬화하려면 jackson-datatype-jsr310 모듈을 사용해야 합니다. 이를 추가한 후, RedisConfig에서 ObjectMapper를 설정하여 날짜/시간 타입을 처리할 수 있도록 했습니다.

 

redisconfig 설정

@Bean
    public RedisTemplate<String, Object> redisTemplate(ObjectMapper objectMapper) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());

        // Key는 StringRedisSerializer 사용
        redisTemplate.setKeySerializer(new StringRedisSerializer());

        // Value는 GenericJackson2JsonRedisSerializer 사용
        GenericJackson2JsonRedisSerializer genericSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
        redisTemplate.setValueSerializer(genericSerializer);

        return redisTemplate;
 	}

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        // Java 8 날짜/시간 API 처리 위한 모듈 등록
        objectMapper.registerModule(new JavaTimeModule());
        return objectMapper;
    }

 

ObjectMapper에 javaTimeModule 을 등록하여 LocalDateTime 과 같은 java 8 날짜/시간 타입을 직렬화할 수 있게 합니다.

 

직렬화와 역직렬화 시 Set<Hashtag>를 처리할 수 있는 GenericJackson2JsonRedisSerializer를 사용하거나, Jackson2JsonRedisSerializer에 Hashtag 클래스를 매핑하는 방식을 확인해야 합니다.

 

GenericJackson2JsonRedisSerializer는 객체를 JSON 형태로 직렬화 합니다. 내부적으로는 ObjectMapper을 사용하여 위와 같이 ObjectMapper를 적용 할 수 있습니다.

Jackson2JsonRedisSerializer도 기본적으로 객체를 JSON 형태로 직렬화 해주지만 항상 타입을 지정 해 주어야 합니다.

 

2. hashtagservice 에서 hashtag 데이터 저장 및 조회

private final RedisTemplate<String , Object> redisTemplate;
private static final String HASHTAG_CACHE_KEY = "hashtags";

// Hashtag를 캐시하는 메서드
private void cacheHashtags(Set<Hashtag> hashtags) {
    redisTemplate.opsForValue().set(HASHTAG_CACHE_KEY, hashtags);
}

// Redis에서 Hashtag 데이터 조회
public Set<Hashtag> getHashtagsFromCache() {
    Object cachedData = redisTemplate.opsForValue().get("hashtags");
    if (cachedData instanceof Set) {
        return (Set<Hashtag>) cachedData;
    }
    return Set.of();
}

 

 

 

redis에 hashtags 데이터가 들어갔다..!

성공