로직은 같지만 타입에 따라 캐싱 처리를 다르게 해야 할 때, 여러분은 어떻게 처리하시나요? 저의 경우, 메서드를 두 개 만들어 타입에 따라 명칭을 달리하고 각각 @Cacheable 어노테이션을 붙여서 사용했습니다. 이게 얼마나 레거시 중에 레거시인지 모르겠네요😂. 이번에 해당 영역을 다시 작업할 일이 생겨 이참에 두 메서드를 하나로 통합해 보려고 하다가, @Caching 어노테이션에 대해 알게 되었습니다. 제 상황에 딱 맞는 적절한 어노테이션이라 생각해서 바로 사용해 보았습니다.🙂
캐시 관리의 중요성이 커지면서, Spring 프레임워크의 @Caching 어노테이션은 복잡한 비즈니스 로직을 효과적으로 처리할 수 있는 강력한 도구입니다. 이번 블로그에서는 @Caching을 통해 다양한 조건에 따라 캐시를 유연하게 관리하는 방법을 살펴보겠습니다.
먼저, 부끄럽지만 제가 기존에 사용했던 방식은 아래와 같았습니다.
@Cacheable(value = "cachingTypeA", cacheManager = "comdoliManager")
public List<String> getTypeA(String type) {
// A 타입의 데이터를 가져오는 로직
return fetchDataByType("A");
}
@Cacheable(value = "cachingTypeB", cacheManager = "comdoliManager")
public List<String> getTypeB(String type) {
// B 타입의 데이터를 가져오는 로직
return fetchDataByType("B");
}
타입에 따라 조회되는 결과물이 다르기 때문에 타입에 따라 분리된 메서드를 사용해야 했습니다. 하지만 이번에 위 코드를 아래와 같이 캐시 설정을 변경해 보았습니다.
@Caching(
cacheable = {
@Cacheable(value = "cachingTypeA", cacheManager = "comdoliManager", condition = "#type.equals('A')"),
@Cacheable(value = "cachingTypeB", cacheManager = "comdoliManager", condition = "#type.equals('B')")
}
)
public List<String> getData(String type) {
// 주어진 타입에 따라 데이터를 가져오는 로직
return fetchDataByType(type);
}
이 코드는 두 개의 서로 다른 캐시를 설정하는 예시입니다. cachingTypeA 캐시는 type이 'A'인 경우에만 활성화되며, cachingTypeB 캐시는 'B'인 경우에 사용됩니다. 이렇게 조건부 캐시를 사용하면 특정 상황에 맞게 데이터를 캐싱하고, 불필요한 계산을 줄일 수 있습니다.
또한 cacheManager 속성으로 사용할 캐시 매니저를 명시하여, 복수의 캐시 매니저를 운영하는 환경에서도 유연하게 대응할 수 있습니다. 이러한 유연성 덕분에 서비스의 성능과 사용자 경험을 크게 개선할 수 있습니다. 예를 들어, 사용자가 자주 조회하는 데이터를 빠르게 제공함으로써 리소스 낭비를 줄이고, 서버의 부하를 최소화할 수 있습니다.
추가로, @Cashing을 @Transactional(readOnly = true)와 함께 사용하면 데이터베이스의 읽기 전용 트랜잭션이 활성화되므로, 동시에 캐시된 데이터를 더욱 효율적으로 관리할 수 있어요.
@Transactional(readOnly = true)
@Caching(
cacheable = {
@Cacheable(value = "cachingTypeA", cacheManager = "comdoliManager", condition = "#type.equals('A')"),
@Cacheable(value = "cachingTypeB", cacheManager = "comdoliManager", condition = "#type.equals('B')")
}
)
public List<String> getData(String type) {
return fetchDataByType(type);
}
이렇게 설정하면 데이터베이스에 대한 불필요한 쓰기 작업을 줄이면서 읽기 작업의 성능을 최적화할 수 있습니다.