본문 바로가기
스프링 부트/A-ger

스프링 레디스 redis cache 프로젝트에 적용하기

by illlilillil 2022. 2. 4.

실행 환경

mac air m1 몬터레이

spring boot: 2.6.2

redis: 6.2.6

Docker Desktop: 4.4.2 

 

 

스프링에서 레디스를 사용하기 위해서 도커에 redis 컨테이너가 실행 중인 상태로 두신 상태를 전제로 진행하겠습니다.

 

 

 

 

 

 

1. build.gradle에 implementation 추가하기

data-redis와 cache manager 사용을 위해 두 가지 모두 추가해줍니다.

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

2. @EnableCaching 추가

프로젝트에서 캐싱을 사용할 수 있게 어노테이션을 추가합니다.

@SpringBootApplication
@EnableJpaAuditing
@EnableCaching
public class AgerProjectApplication {

    public static void main(String[] args) {
        SpringApplication.run(AgerProjectApplication.class, args);
    }
}

 

3. application.properties에 redis 설정 정보 추가

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.timeout=6

 

4.  redisConfig 작성

configuration으로 빈 설정 정보를 등록하기 위한 어노테이션을 설정해줍니다. extends로 CachingConfigurerSupport를 추가해줬는데 ttl 설정을 위해 추가해주었습니다. spring boot 2.6.1부터는 setTimeout이 deprecated되어 캐시매니저를 상속 받았습니다.

@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.timeout}")
    private Long timeout;
    @Bean
    public LettuceConnectionFactory lettuceConnectionFactory() {
        LettuceClientConfiguration lettuceClientConfiguration = LettuceClientConfiguration.builder()
                .commandTimeout(Duration.ofMinutes(1))
                .shutdownTimeout(Duration.ZERO)
                .build();
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(host, port);
        return new LettuceConnectionFactory(redisStandaloneConfiguration, lettuceClientConfiguration);
    }

    @Bean
    public RedisTemplate<?, ?> redisTemplate() {
        RedisTemplate<byte[], byte[]> template = new RedisTemplate<>();
        template.setConnectionFactory(lettuceConnectionFactory());
        return template;
    }

    @Bean
    @Override
    public CacheManager cacheManager() {
        RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
                .RedisCacheManagerBuilder
                .fromConnectionFactory(lettuceConnectionFactory());
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(timeout));
        builder.cacheDefaults(configuration);
        return builder.build();
    }
}

 

5. 캐싱하길 원하는 엔티티에 Serializable 추가

redis의 저장 방식은  byte array 타입이기 때문에 직렬화를 하여 캐싱을 할때 넘겨주어야 합니다.

이 때 두 가지 방법이 있는데 redisConfig에서 직렬화 설정을 하거나 직접 엔티티에 직렬화 설정을 하는 방법이 있습니다.

우리 프로젝트에는 직접 엔티티에 설정하는 방식을 채택하였습니다. 또한 상속받는 baseEntity 직렬화 설정을 일괄적으로 해주셔야 오류가 나지 않습니다.

public class Product extends BaseEntity implements Serializable {
    @Id
    @GeneratedValue
    private Long productId;
    ...
    ...
    ...
}

 

이제 캐싱을 적용하기 위한 모든 준비가 끝났습니다. 캐싱을 적용하기 위해서 적용하고 싶은 메소드에 @Cacheable만 추가해주시면 됩니다. 우리 프로젝트에는 단일 상품 조회 부분에 적용을 해보았습니다.(service 단에 적용) value를 사용하시면 redis에 캐시가 저장될때

"product::(productId)" -> product::2 같이 저장됩니다.

@Cacheable(value = "product")
public Product findProductById(Long productId) {
     Product product = productRepository.findById(productId).orElseThrow(NotFoundException::new);
     ...
     return product;
}

 

간단하게 포스트맨으로 캐시 적용 전과 후의 처리 시간을 비교해보겠습니다.

 

첫 조회 - 캐시 적용 전 처음 조회가 된 후 캐시에 올라가게 됩니다. 104ms의 시간이 걸렸습니다. 

 

조회한 후엔 redis에 캐시가 들어가게 됩니다.

 

첫 조회 이후의 조회 - 캐싱 적용 후 :: 28ms정도의 비교적 빠른 시간으로 처리가 되었습니다.

 

현재는 단순 상품 조회 기능만 캐싱 처리를 했지만 값이 잘 변하지 않는 엔티티들을 캐싱 적용해 보도록 하겠습니다.

 

다음 글에서는 Jmeter를 이용해 대량의 get 요청을 보내고 캐싱 전 후로 성능 차이를 알아보도록 하겠습니다.

댓글