SpringBoot] Redis 로 Cache 구현해보기

JUNHYUK CHANG·2024년 2월 23일
0

TIL

목록 보기
21/33

Cache 란?

데이터나 정보를 빠르게 접근할 수 있도록 메모리에 저장하는 임시저장소
네트워크를 통해 DB 에 접속하여 값을 가져오는 것보다 메모리에 미리 저장해둔 값을 반환하면 응답 시간을 줄이고 성능을 향상시킬 수 있다! ( 별도의 쿼리문도 생성하지 않는다! )

SpringBoot 에서도 자체적으로 지원하는 Spring Boot Starter cache 도 있지만, 이번엔 Redis 를 통해 구현을 해보려고 한다.

그럼 Redis란?

Remote Dictionary Server의 약자로, 말그대로 원격에서 딕셔너리 형태(키-값 쌍데이터)를 저장하는 데이터 저장소(서버) 이다. 주로 Inmemory 데이터 구조 서버로 이용되어 직접 DB 에 접근하여 데이터를 가져오는 것보다 훨씬 빠르고 효율적으로 동작한다.

클라이언트가 데이터를 요청하면 DB 에 찾아가기 전, Redis 서버 컴퓨터의 메모리(RAM)에 해당 데이터가 캐시되어 있는지 확인하여 바로 가져오기 때문이다.

하지만 이는 서버가 실행되는 동안에만 유지되며, 재시작하면 데이터가 초기화 되기 때문에 주로 캐싱이나 세션 관리와 같이 임시적인 데이터를 다루는 데 사용된다.

Redis 사용 방법

사용방법 또한 간단하다. SpringBoot 에는 @Cacheable 이라는 어노테이션이 있어 캐시에 저장하고, 불러오는 과정을 대신 해주기 때문에 우리는 의존성 추가와 yml 설정, RedisConfig 설정만 해주면 된다.

  • @Cacheable : SpringFramework 에서 제공하는 캐싱을 적용하기 위한 메타데이터를 나타내는 어노테이션. 메서드에 적용하면 해당 메서드의 반환값이 캐시되어 딕셔너리 형태로 저장이 되고, 이후 같은 키를 호출할 때 캐시에서 가져오도록 한다.
  • 즉, 저장된 값 자체를 찾는 것이 아니라 로 구분하기 때문에 캐시의 이름과 키 값을 어떻게 지정할 것인가도 전략적으로 접근해야 한다.

0. Redis 설치

공식 홈페이지에선 우분투를 통해 설치하는 방법을 소개하고 있는데 windows 환경에서도 간단하게 설치할 수 있다.

아래 링크에 접속하여 msi 확장자의 Redis 설치 프로그램을 다운로드한다.
https://github.com/microsoftarchive/redis/releases
출처: https://ittrue.tistory.com/318 [IT is True:티스토리]

1. @EnableCaching 어노테이션 설정, 의존성 추가

캐싱 기능을 활성화하기 위해 메인 어플리케이션 클래스에 @EnableCaching 어노테이션을 추가하고, Gradle에 의존성을 추가해야한다.

    // Redis 
    implementation("org.springframework.boot:spring-boot-starter-data-redis")
    // 캐싱 기능 
    implementation("org.springframework.boot:spring-boot-starter-cache")

2. yml 설정

yml 에선 Redis 의 포트와 호스트를 설정해주고, 캐시 타입을 지정해준다.
(spring.redist.port 경로 사이에 .data 가 추가되었다. )

spring:  
  data:
    redis:
      port: 6379  # 레디스 포트( 기본 6379 ) , 호스트 설정
      host: localhost 
  cache:
    type: redis # 캐싱 타입

3. RedisCacheConfig 설정

Redis 를 이용하여 캐시를 사용하려면 스프링부트 어플리케이션에서 캐싱을 활성화하고, 캐시 매니저를 구성해야 하는데 아래과 같은 설정들이 필요하다.

@Configuration
@EnableCaching  // 캐싱 활성화
class RedisCacheConfig {

    // Redis 캐시 메니저를 생성하는 메서드. 레디스 연결 팩토리 를 인자로 받아 캐시 매니저를 생성함
    @Bean
    fun cacheManager(cf: RedisConnectionFactory): CacheManager {
        
        // Redis 캐싱 구성을 생성하는 부분
        val redisCacheConfiguration =
            RedisCacheConfiguration.defaultCacheConfig() // 기본 캐시 구성 사용.
            .serializeKeysWith(  // 키를 직렬화 - StringRedisSerializer 사용
                RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer()))
            .serializeValuesWith( // 값을 직렬화 - GenericJackson2JsonRedisSerializer 사용
                RedisSerializationContext.SerializationPair.fromSerializer(
                    GenericJackson2JsonRedisSerializer()
            ))
            .entryTtl(Duration.ofMinutes(3L)) // 캐시 만료시간  3분으로 설정

        return RedisCacheManager // RedisCacheManager를 생성하기 위한 빌더 패턴을 사용
            .builder(cf) // RedisConnectionFactory 를 받아 CacheManager를 빌드
            .cacheDefaults(redisCacheConfiguration) // 캐시 매니저의 기본 캐시 구성 설정. 위에서 만든 RedisCache 구성 사용
            .build() // 최종적으로 빌드하여 반환. 캐싱을 관리하고 사용할 수 있도록 함.
    }
}

4. 캐싱을 적용할 메서드에 @Cacheable 어노테이션 설정

@Cacheable 어노테이션에서 캐시에 저장될 이름과 키를 지정해줄 수 있다.
캐시에 저장된 값이 호출될 때 를 기준으로 호출되니, 어떤 방식으로 지정해줄 지 고민해야한다.

    @Cacheable("storeCache", key = "{#id}") 
    override fun getStoreBy(id: Long?, company: String?, shopName: String?, tel: String?): StoreResponse {
        if(id == null && company == null && shopName == null && tel == null)
            throw NotFoundException()

        return storeRepository.getStoreBy(id, company, shopName, tel).toResponse()
    }

위 코드를 실행하고 redis-cli 를 실행하여 key * 를 입력하면 아래와 같이 "캐시이름::id" 값이 값으로 저장된다.
get 키 를 입력하여 내용을 확인할 수도 있지만, 한글은 깨져서 보이게 된다..


위의 과정을 따라 캐싱 적용한 뒤 SQL Query 로그를 확인해보면 더 명확한 차이를 알 수 있다.
캐시에 저장되지 않은 데이터를 조회할 땐 기존대로 쿼리문을 전송하지만, 이미 저장되어 있다면 쿼리문을 전송하지 않고 빠르게 값을 반환한다.

이후 logging 이나 AOP 를 활용하여 실제 시간값을 확인해보면 더 정확한 차이를 알 수 있을 것 같다.

캐싱은 최신데이터 n 개를 미리 저장해놓는다거나, 사람들이 검색한 결과를 저장해놓는 등 다양하게 활용해볼 수 있으니 잘 익혀두어야겠다.

0개의 댓글