들어가면서
스프링에서 캐시를 기능을 추상화하여 사용자가 쉽게 캐시를 이용할 수 있도록 @Cacheable
과 같은 어노테이션을 제공하는데 캐시를 저장하는 저장소와 상호작용하는 인터페이스는 CacheManager
라는 인터페이스가 담당한다.
레디스를 캐시로 사용하는 경우에는 CacheManager
를 상속받은 레디스 전용 캐시매니저이다. 레디스에 어떻게 키를 저장하는지 궁금해서 간단한 테스트코드와 디버거를 이용해서 공부해보았다.
Spring boot starter redis 의존성을 추가하고 로컬에서 Redis를 가동하고 연결 설정까지 하고 실습을 진행했다. 실습한 스프링 부트의 버전은 3.2.1이다.
데이터 캐시(저장)
스프링이 제공하는 추상화를 이용하려면 우선 Cache
객체를 매니저를 통해서 조회해야한다.
@SpringBootTest
public class RedisCashManagerPracticeTest {
@Autowired
CacheManager manager;
@DisplayName("execute redis put")
@Test
void cache1() {
// given
Cache cache = manager.getCache("cache-type");
// when
cache.put("key", "test value");
// then
// redis 에서 데이터 확인하기!
}
}
스프링 부트 테스트를 사용하여 의존성 설정을 부트에게 위임하고 만들어진 CacheManager
를 주입받는다. 이때의 매니저는 RedisCacheManager
이다.
테스트를 진행하고 레디스에 저장된 데이터를 확인한 결과는 다음과 같다.
캐시를 가져올 때의 값 `cache-type`과 데이터를 저장할 때, 사용한 키값의 조합으로 생성된 `cache-type::key`를 키로 가지고 값은 알 수 없는 쓰레기 값(아마 직렬화를 위한 클래스 정보라고 추정한다)과 실제 값이 저장된 것을 확인할 수 있었다.
데이터 조회
데이터 조회 역시 Cache
객체가 필요하다. 저장에서 처럼 캐시를 매니저를 통해서 조회하고 get..() 메서드를 통해서 조회할 수 있다. 기초적인 get()
을 통해서 테스트를 해보았다.
방금 생성한 데이터를 조회해보자!
@Order(2)
@DisplayName("Redis get value")
@Test
void redisGetValue() {
// given
Cache cache = manager.getCache("cache-type");
// when
ValueWrapper value = cache.get("key");
String stringValue = cache.get("key", String.class);
String keyName = cache.getName();
// then
System.out.println("value = " + value.get());
System.out.println("stringValue = " + stringValue);
System.out.println("keyName = " + keyName);
}
저장과정에서 저장한 데이터가 잘 조회됨을 테스트로 검증할 수 있다!!
하지만 조회과정에서 레디스와 연결이 불안정하여 실패한다면 어떻게 처리가 될까? 그래서 디버깅을 해본 결과, Cache
객체를 생성하는 manger.getCache()
에서는 레디스가 다운되어도 정상적으로 실행되는 반면 캐시 객체에서 어떤 연산을 실행하는 코드에서는 에러가 발생한 것을 확인할 수 있었다.
존재하지 않은 키로 데이터를 조회하는 경우에는 어떻게 될까?
@DisplayName("When get key that is not existed, Redis get value")
@Test
void redisGetKeyThatIsNotExisted() {
// given
Cache cache = manager.getCache("cache-type");
// when
ValueWrapper value = cache.get("invalid");
String stringValue = cache.get("invalid", String.class);
String keyName = cache.getName();
// then
System.out.println("stringValue = " + stringValue);
System.out.println("keyName = " + keyName);
then(value).isNull();
then(stringValue).isNull();
}
에러가 발생하지는 않고 null을 반환한다는 것을 알 수 있었다.
데이터 삭제
마지막으로 캐시된 데이터를 매니저가 삭제하는 방법을 알아보자!
@Order(3)
@DisplayName("delete cache")
@Test
void deleteCache() {
// given
Cache cache = manager.getCache("cache-type");
// when
cache.evict("key");
ValueWrapper value = cache.get("key");
String stringValue = cache.get("key", String.class);
String keyName = cache.getName();
// then
System.out.println("stringValue = " + stringValue);
System.out.println("keyName = " + keyName);
then(value).isNull();
then(stringValue).isNull();
}
cache.evict()
를 이용해서 삭제할 수 있다.
마치며
@Cacheable
이 작동할 때, 사용하는 캐시 매니저를 통해서 어떻게 캐시를 저장, 조회, 삭제하는지 그 구현 방식을 알 수 있었다. 특히 RedisCacheManager가 어떻게 캐시를 관리하는지 알 수 있었다.
TTL은 Redis 매지저를 생성할 때, 설정할 수 있기 때문에 이런 부가적인 설정은 다음 포스팅에서 한번 알아볼까? 한다.
그리고 CacheResolver를 통해서 여러개의 캐시 매니저를 통한 전략을 구현할 수 있다고 들었는데 그 방법을 알아봐야겠다.
'spring' 카테고리의 다른 글
reseilence4j를 이용한 장애 대처 (0) | 2024.01.27 |
---|---|
[WireMock] WireMock을 이용하여 Mock API 서버 사용하기 (0) | 2024.01.21 |
스프링 AOP를 적용하는 방법 (1) | 2024.01.10 |
테스트 코드에서 @Transactional 을 통한 롤백 사용시 주의점 (2) | 2023.12.04 |
Spring 메일 전송기능 구현하기 (3) | 2023.12.01 |