앞선 글에서 Redis 설치 및 실행하는 과정에 대하여 알아보았습니다.
이번 글에서는 SpringBoot에서 Redis에 연결하고 캐싱하는 방법에 대해 알아보겠습니다.
pom.xml - dependency 추가
redis 사용을 위해 스프링부트의 starter 시리즈 중 spring-boot-starter-data-redis에 대한 의존성을 추가합니다. 저는 이후 RestController 사용을 위해 web까지 받아놓겠습니다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
EnableCaching
캐시기능을 사용하기 위해 프로젝트 Application에 @EnableCaching 애노테이션을 추가합니다. 스프링은 EnableCaching 애노테이션은 Cache 관련 애노테이션이 붙은 public 메서드의 모든 스프링 빈을 스캔합니다. 이에 부합하는 대상을 찾는다면 해당 메서드를 intercept 하여 캐싱 기능이 추가된 프록시를 생성합니다.
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
properties
application.yml에 redis 캐시 설정에 필요한 값들을 세팅합니다. spring.redis.host와 spring.redis.port는 지정하지 않을 시 default로 127.0.0.1(로컬 호스트) 과 6379 (redis default port)로 설정됩니다. 추가로 적용하고 싶은 설정값은 RedisProperties를 참고하시기 바랍니다.
spring:
cache:
type: redis
redis:
host: {host}
port: {port}
@Cacheable, @CacheEvict
@Cacheable과 @CacheEvict는 spring에서 지원하는 캐싱을 위한 애노테이션입니다. 스프링은 애노테이션을 통해 쉽게 캐싱을 적용할 수 있도록 아래와 같은 애노테이션들을 지원합니다.
- @Cacheable: 캐시 생성 수행.
- @CacheEvict: 캐시 삭제 수행.
- @CachePut: 캐시 업데이트 수행(메서드 실행에는 영향을 끼치지 않으며).
- @Caching: 메서드에 적용할 캐시 작업 그룹 정의.
- @CacheConfig: 클래스 레벨에서 캐시 관련 설정을 공유.
우선적으로 프로젝트에서 간단히 사용하기 위해 @Cacheable과 @CacheEvict만을 사용하겠습니다.
@RestController
@RequestMapping("/redis")
public class RedisController {
private static final Logger logger = LoggerFactory.getLogger(RedisController.class);
@GetMapping()
@Cacheable(value = "user")
public String get(@RequestParam(value = "id") String id) {
logger.info("get user - userId:{}", id);
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return id;
}
@DeleteMapping()
@CacheEvict(value = "user")
public void delete(@RequestParam(value = "id") String id) {
logger.info("delete user - userId:{}", id);
}
}
- get() 메서드는 요청을 받아 Threed.sleep() 메서드를 통해 1.5초의 지연 후 id값을 그대로 반환합니다. 이때 sleep은 캐싱 전과 캐싱 후 속도 차이를 위해 의도적으로 지연을 발생시킨 코드입니다. '/redis'로 첫 GET 요청이 온다면 1.5초의 지연 후 id를 응답으로 내려주고 이를 캐시에 저장합니다. 해당 id값이 캐싱되었다면 같은 id 요청에 대하여 지연 없이 캐시를 이용하여 응답할 것입니다.
- delete() 메서드는 요청을 받아 아무 동작을 하지않지만, 전달받은 파라미터와 동일한 값으로 저장된 캐시를 삭제합니다.
동작 확인
동작 확인을 위해 먼저 redis를 서버를 동작시키고 이어서 spring 프로젝트를 동작시킵니다.
redis와 spring프로젝트를 모두 로컬에서 실행하는것이 아니라면 redis.conf 파일에서 protect-mode가 no로 지정되어 있는지, bind ip가 올바르게 설정되어 있는지 확인하시기 바랍니다. 그리고 지정한 설정대로 실행시켜주기 위해 실행시 argument 로 설정파일의 경로를 전달해주어야 합니다. (src/redis-server redis.conf)
먼저, localhost:8080/redis?id=id1 로 get 요청을 보내고 응답 시간을 확인합니다. 첫 번째 시도 시 캐싱된 값이 존재하지 않기에 Thread.sleep()으로 걸어준 1.5초가 지난 후 결과가 응답됨을 확인할 수 있습니다. 하지만 두 번째 시도 시 응답 시간은 17ms 밖에 되지 않는 것을 확인할 수 있습니다. 이는 @Cacheable을 통해 id1이라는 input값에 대한 결과가 캐싱되었기 때문인데요 실제로 redis-cli에서 이를 확인해 볼 수 있습니다.
127.0.0.1:6379> keys *
1) "user::id1"
캐시 삭제를 위해 localhost:8080/redis?id=id1 로 delete 요청을 보내겠습니다. @CacheEvict을 통해 id1이라는 input값에 대해 캐싱된 결과가 제거될 것입니다. 마찬가지로 redis-cli에서 해당 키가 삭제되었는지 확인해 볼 수 있습니다.
127.0.0.1:6379> keys *
(empty list or set)
이렇게 SpringBoot 프로젝트와 Redis를 연결하고 간단한 캐시를 생성, 제거하는 것을 진행해보았습니다. 실제 서비스에 캐싱을 사용할 때는 @EnableCaching을 별도의 Cache 설정 클래스를 만들어서 그곳에 선언해주는 것이 더 좋습니다. 또한, @Cacheable과 @CacheEvict과 애노테이션은 Controller가 보다 실제 비즈니스 로직을 처리하는 Bo에서 사용하는 것이 더 올바른 위치에서의 사용입니다. 위의 예시는 빠른 예시를 위해 생성한 프로젝트로 봐주시면 감사하겠습니다.