3 분 소요

캐시

캐시는 자주 불러와야하는 데이터를 저장해두는 공간을 말한다. 예를 들어 쇼핑몰에서 상품 정보를 가져와야 할 때, DB에 접근해야
하는데 그 정보들을 캐시라는 공간에 저장해둔다면 보다 빠르게 불러와서 활용할 수 있게 된다. 이렇게 불러오는 데이터는 자주 불러오
는 정보이면서 수정되지 않을 수록 캐시에 저장해두고 활용하는게 효율적이므로 주로 변경되지 않는 데이터일 수록 좋다.

Redis를 캐시로 설정하기

먼저 Redis를 설치하고 시작하자!
Redis를 설치하고 실행해도 되고 docker에서 컨테이너로 올려 실행해도 된다.

Redis 설치 (MacOS)

# 설치
brew install redis

# 버전확인
redis-server --version

# 실행 - redis를 Homebrew 서비스로 실행하면 재부팅후에도 자동으로 실행됩니다.
brew services start redis

# 중단
brew services stop redis

# 터미널 접속하는 방법
redis-cli -h localhost -p 6379

# 전체 키 조회
keys *

# 키 전체 삭제
flushall

Redis 설치 (docker)

docker desktop 설치 후 Redis를 컨테이너에 띄워보자.

# docker 설치 확인 (버전 확인)
docker -version

# Redis image 불러오기
docker pull redis

# 불러온 Redis image를 컨테이너로 띄우기
docker run -p 6379:6379 --name redis-container redis

# 컨테이너에 올라간 Redis로 접속 (테스트)
docker exec -it redis /bin/bash

build.gradle

Redis를 스프링에서 캐싱 전략으로 활용하기 위해서는 의존성 설정을 해줘야 한다.

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


application.yml

스프링에서 Redis를 사용하기 위한 값들을 설정해줘야 한다. redis.host127.0.0.1 즉, localhost로 설정해주고
port는 Redis가 기본으로 사용하는 번호인 6379로 설정해주자.

spring:
  cache:
    type: redis
  data:
    redis:
      host: 127.0.0.1
      port: 6379


Configuration 클래스

캐시로 활용할 Redis 설정을 해주는 RedisConfig 클래스를 만들어 @Configuration를 적용해주자.

@Configuration
public class RedisConfig {

    @Value("${spring.data.redis.host}")
    private String host;

    @Value("${spring.data.redis.port}")
    private int port;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(new RedisStandaloneConfiguration(host, port));
    }
}
  • host, port : application.yml에서 설정했던 값 사용

위 코드까지 설정해줘도 스프링 부트에서 캐싱 전략으로 Redis를 알아서 사용하지만, 세세한 설정을 하고 싶을 땐 다음처럼 정의해놓
고 스프링 빈으로 등록해서 사용할 수 있다. 그리고 여러 Redis 캐시에 대해 다른 설정을 해주고 메소드마다 등록해줄 수 있다.

@EnableCaching
@Configuration
public class CacheConfig {

  @Bean
  public RedisCacheConfiguration redisCacheConfiguration() {
      return RedisCacheConfiguration.defaultCacheConfig()
              .entryTtl(Duration.ofSeconds(3600))
              .disableCachingNullValues()
              .serializeKeysWith(
                      RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())
              )
              .serializeValuesWith(
                      RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())
              );
  }

  @Bean
  public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
      return (builder) -> builder
              .withCacheConfiguration("cache1",
                      RedisCacheConfiguration.defaultCacheConfig()
                              .computePrefixWith(cacheName -> "prefix::" + cacheName + "::")
                              .entryTtl(Duration.ofSeconds(120))
                              .disableCachingNullValues()
                              .serializeKeysWith(
                                      RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())
                              )
                              .serializeValuesWith(
                                      RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())
                              ))
              .withCacheConfiguration("cache2",
                      RedisCacheConfiguration.defaultCacheConfig()
                              .entryTtl(Duration.ofHours(2))
                              .disableCachingNullValues());
  }
}
  • entryTtl() : TTL 설정
    • RedisCacheConfiguration이나 RedisCacheManagerBuilderCustomizer에서 TTL을 설정해주지 않으면 무제한으로 설정됨
  • disableCachingNullValues() : null 값 캐싱 안함

활용

캐시가 되길 원하는 메소드에 @Cacheable, @CachePut, @CacheEvict를 붙여서 적용할 수 있다.
아래는 각 애노테이션에 쓰일 수 있는 속성이며 둘 다 같을 경우 같은 장소에 데이터가 저장되므로 구분할 수 있도록 주의해야한다.

  • value : 캐싱 장소
  • key : 캐싱 장소 내 저장할 데이터를 찾을 때 사용하는 값
    • 아래 예시처럼 해당 함수의 파라미터를 적용할 수 있음
    • public void foo(Long userId)key = "#userId로 사용 가능
    • 반드시 String이 아니어도 가능

@Cacheable

메소드 실행 결과를 캐시에 저장하고, 동일한 인자로 재호출 시 저장한 값을 반환한다.
주로 HTTP 메소드 GET에 적용한다.

@Cacheable(value = "mall", key = "#userId")

@CachePut

메소드를 실행 시 그 결과를 캐시에 갱신하는 용도로 사용한다.
주로 HTTP 메소드 POST, PUT 등 DB에 데이터를 반영하는 경우에 적용한다.

@CachePut(value = "mall", key = "#userId")

@CacheEvict

메소드 실행 시 캐시에서 데이터를 제거한다.
주로 HTTP 메소드 DELETE 등 DB에 저장된 데이터를 삭제하는 경우에 적용한다.

@CacheEvict(value = "mall", key = "#userId")

cacheNames

RedisCacheManagerBuilderCustomizer에 설정한 캐시 이름(cache1, cache2 등)은 다음처럼 설정할 수 있다.

//cache1 정책 적용
@Cacheable(cacheNames = "myCache", value = "mall", key = "#userId")
public Object foo(Long userId) {...}

//cache1, cache2 정책 적용
@Cacheable(cacheNames = {"cache1", "cache2"}, value = "mall", key = "#email")
public Object foo2(String email) {...}


테스트 결과

아래는 localhost:8080/api/test로 들어오면 TestEntity 정보를 반환하도록 API를 만든 뒤 테스트한 결과다.
첫 번째는 처음으로 데이터를 받아왔을 때를, 두 번째는 캐싱된 데이터를 받아왔을 때로 274ms에서 79ms로 향상된걸 확인할 수 있다.
redis_cache_test_before.png redis_cache_test_after.png

아래는 redis-cli에서 캐싱된 데이터를 조회한 결과이다.
함수에는 @Cacheable(value = "test", key = "'testValue'")를 적용했었다.

redis_cache_test_terminal.png

References

카테고리:

업데이트:

댓글남기기