Redis로 캐싱 구현하기

su_under·2024년 6월 18일
0
post-custom-banner

이번 포스팅에서는 Redis로 캐싱을 구현하는 방법에 대해서 알아보겠다.
현재 진행하고 있는 OMD 프로젝트에서 매번 데스크 셋업 전체를 조회하는 것은 비효율적이라는 생각이 들어 캐싱을 적용하기로 했다.

1. 의존성 추가

 	implementation 'org.springframework.boot:spring-boot-starter-data-redis'

먼저 의존성을 추가하고 application.yml에 다음과 같이 작성한다.

spring:
  data:
    redis:
      host: redis
      port: 6379
      ssl:
        enabled: true

2. RedisConfig 작성

RedisConfig 파일을 작성하여 Redis를 사용하기 위한 설정을 정의해준다. Redis 서버와의 연결, 데이터 직렬화, 캐시 관리를 설정해주었는데 각자 상황에 맞게 작성하면 된다.

@Configuration
public class RedisConfig {

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port));
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        return redisTemplate;
    }

    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory){
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();

        return RedisCacheManager.RedisCacheManagerBuilder
                .fromConnectionFactory(connectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .withInitialCacheConfigurations(redisCacheConfigurationMap)
                .build();
    }
}

3. 캐싱 적용

마지막으로 캐싱을 적용해보자. 캐싱을 적용하고 싶은 메소드에 @Cacheable 어노테이션을 적용한다. 나는 page, limit, criteria 파라미터를 조합하여 캐시 키를 생성했다. 이로써 동일한 파라미터로 메서드가 호출되면 캐시된 결과를 반환하게 된다.

새로운 게시글이 생성된 후 게시글 목록 조회를 했을 경우에 최신 데이터를 반영해야 하기 때문에 @CacheEvict 적용하여 게시글이 생성될 때마다 게시글 목록 조회와 관련된 모든 캐시 항목을 제거하여 이후의 조회 요청이 최신 데이터를 반영하도록 하였다.

  	// 게시글 생성
    @Transactional
    @CacheEvict(value = "posts", allEntries = true)
    public Post savePost(PostRequest request) {
        // 게시글 생성 로직
        return post;
    }
    
    // 게시글 목록 조회
    @Transactional(readOnly = true)
    @Cacheable(value = "posts", key = "#page + '-' + #limit + '-' + #criteria")
    public List<PostPreviewResponse> list(Integer page, Integer limit, Integer criteria) {
        // 게시글 목록 조회 로직
    }

4. 결과

캐싱 전에는 10번의 호출을 했을 때 평균적으로 30초가 걸렸다면 캐싱 후에는 평균적으로 14초가 걸렸다. 따라서 지연 시간이 대략 50%정도 감소했다.


❗️ 캐싱 구현 과정에서 있었던 오류

Could not write JSON: Java 8 date/time type java.time.LocalDateTime not supported by default: add Module

게시글 요청 응답 DTO에서 LocalDateTime 을 사용하고 있었는데, 해당 오류는 Java 8의 LocalDateTime 타입이 기본적으로 JSON 직렬화/역직렬화를 지원하지 않기 때문에 발생하는 문제였다. 이 문제를 해결하기 위해 LocalDateTime을 지원하는 모듈을 추가해야 했다. 따라서 다음과 같은 의존성을 추가하였다. jackson-datatype-jsr310 모듈은 Java 8 날짜 및 시간 API 타입을 직렬화 및 역직렬화할 수 있도록 지원한다.

implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'

하지만 위의 의존성을 추가해도 문제가 해결되지 않았다. 찾아보니 직렬화, 역직렬화 어노테이션을 추가해 주면 문제가 해결된다는 사례를 보고 나도 동일하게 적용해 보았다.


public class PostResponse {
    ...

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul")
    private final LocalDateTime createdAt;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul")
    private final LocalDateTime updatedAt;

}

위와 같이 @JsonSerialize(using = LocalDateTimeSerializer.class) , @JsonDeserialize(using = LocalDateTimeDeserializer.class) 을 추가해주었더니 문제가 해결되었다.


직렬화 및 역직렬화 과정을 명시적으로 정의함으로써, Jackson 라이브러리가 LocalDateTime 타입의 데이터를 처리할 때 발생할 수 있는 오류를 방지하고, 원하는 형식과 타임존에 맞게 데이터를 정확하게 변환할 수 있게 되었기 때문에 오류가 해결 된 것 같다.

profile
솨의 개발일기
post-custom-banner

2개의 댓글

comment-user-thumbnail
2024년 7월 5일

너무 대충 올리신거 같은데여..

1개의 답글