본문 바로가기

Spring/Redis

1) Spring Redis Cache적용하기 - 1부 Springboot 설정

반응형

cache 종류는 여러가지 있겠지만 주로 많이 쓰는 cache중 하나가 바로 Redis 이다. 

여기서는 Redis서버의 설치와 설정은 다루지 않고 Springboot 설정을 어떻게 하고 어떻게 사용을 하는지에 대해 

공부해 본다.

 

먼저 로컬에 Redis서버를 설치해야 되나? 아니다. Springboot 에 로컬에서 사용하기 위해 임베디드 Redis 를 지원하는 

라이브러리가 있다. 

이것을 이용해서 간단히 로컬서버를 띄울때  Redis 서버를 띄우고 연결하는 방법을 알아 본다.

 

1)  build.gradle 에 redis dependency추가

2) redis server에 필요한 설정 application.yml 에 추가

3) redis configuration 추가

4) 서버 실행 및  테스트 코드 작성후 테스트

 

위 단계로 테스를 진행한다. 

 

1) build.gradle 에 redis dependency추가

plugins {
    id 'org.springframework.boot' version '2.4.5'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.devracoon.redis'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'

    //embedded-redis
    implementation group: 'it.ozimov', name: 'embedded-redis', version: '0.7.1'

    compileOnly 'org.projectlombok:lombok'
    compileOnly 'com.h2database:h2'

    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
    annotationProcessor 'org.projectlombok:lombok'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
    useJUnitPlatform()
}

위 코드에서 embedded-redis 부분을 참조.

 

2) redis server에 필요한 설정 application.yml 에 추가

spring:
  redis:
    host: 127.0.0.1
    port: 6379

 

3) redis configuration 추가

- embedded redis 는 로컬서버에서만 뜨게 하기 위해 아래와 같이 spring profile 이 local 일때만 작동하고 configuration 한다.

package com.devracoon.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import redis.embedded.RedisServer;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;

@Slf4j
@Profile("local")
@Configuration
public class LocalRedisConfig {
    @Value("${spring.redis.port}")
    private int redisPort;

    private RedisServer redisServer;

    @PostConstruct
    public void redisServer() throws IOException {
        redisServer = new RedisServer(redisPort);
        redisServer.start();
    }

    @PreDestroy
    public void stopRedis() {
        if (redisServer != null) {
            redisServer.stop();
        }
    }

}

그다움 redis client 에 대한 설정을 한다.

package com.devracoon.redis;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;

@Configuration
@EnableRedisRepositories
public class RedisRepositoryConfig {
    @Value("${spring.redis.host}")
    private String redisHost;

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

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisHost, redisPort);
    }

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

4) 서버 실행 및  테스트 코드 작성후 테스트

간단한 테스트 코드를 아래와 같이 작성.

package com.devracoon.redis.controller;

import com.devracoon.redis.service.SimpleRedisTestServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@Slf4j
@RestController
@RequiredArgsConstructor
public class SimpleRedisTestController {

    private final SimpleRedisTestServiceImpl simpleRedisTestService;

    @GetMapping("/simple/getSimpleData")
    public List<String> getSimpleData(HttpServletRequest request , HttpServletResponse response) throws Exception{
        List<String> result = simpleRedisTestService.getSimpleData("param1");
        return result;
    }

    @GetMapping("/simple/removeSimpleData")
    public void removeSimpleData(HttpServletRequest request , HttpServletResponse response) throws Exception{
        simpleRedisTestService.simpleDataEvict();
    }

}
package com.devracoon.redis.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class SimpleRedisTestServiceImpl {

    @Cacheable(value="simpleData")
    public List<String> getSimpleData(String param){
        log.info("start getSimpleData");
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");

        return list;
    }

    @CacheEvict(value="simpleData" , allEntries = true)
    public void simpleDataEvict(){
        log.info("cache evict");
    }

}

아래는 http://localhost:8080/simple/getSimpleData 를 실행하고 나서 redis에  캐시data 가 저장된 모습이다.

 

아래는 http://localhost:8080/simple/removeSimpleData 를  실행하고 위와 같은 redis 상태를 조회한 결과이다.

 

저장되어 있던 cache key가 삭제 된것을 확인할수 있다.

 

간단하게 설명을 하자면 

http://localhost:8080/simple/getSimpleData 호출을 하게되면 

@Cacheable(value="simpleData")
    public List<String> getSimpleData(String param){
        log.info("start getSimpleData");
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");

        return list;
    }

가 실행되는데 @Cacheable Annotation 이 붙어 있으므로  cache key 가 simpleData + parameter 로 cache key가 만들어진다. 

이말은 무슨뜻이냐면 parameter가 달라지면 cache 된 데이터가 없으니 또다시 다른 key 로 데이터가 cache 된다는 뜻이다.

 

 

- http://localhost:8080/simple/removeSimpleData 를 실행시키면 

@CacheEvict(value="simpleData" , allEntries = true)
public void simpleDataEvict(){
log.info("cache evict");
}

@CacheEvict 는 cache를 삭제하는 Annotation이다. 어떤 기분으로 삭제를 할지에 대한 내용이 들어가 있는데 .

여기서는  simpleData 라고 되어 있는 AllEntires (모든키) 를 삭제한다 라고 이해하면 된다.

 

여기서 문제는 보통 운영상에서 Redis 설정을 할때 allEntires 같은 키워드를 쓰게되면 Redis 설정에서 해당 Command 를 실행하게 되는데 실제 운영을 할떄는 allEntiries에 해당하는 Command는 막아 놓고 있다. 

 

이유는 Redis에 저장된 무수히 많은 Key 와 Data 가 있는데 이중에서 내가 원하는 (simpleData) 를 찾기 위해 전체 key를 full scan 하면서 성능에 문제를 일으킨다고 한다.

 

그래서 다음장에서는 실제 운영상에서 사용되야할 cachable 과 cacheEvict 방법에 대해 알아본다.