Using Spring Cache in Spring Boot

Spring Boot에서 Cache를 사용하는 방법은 본 포스팅외 많은 포스팅에서 다루고 있으니, 해당 부분은 다루지 않는다.

본 포스팅에서는 어노테이션을 사용해 캐시를 저장, 수정, 삭제 (Cacheable, CachePut, CacheEvict) 하는 방법외에 CacheManager를 통해 개별적인 캐시 삭제 방법에 대해 다뤄본다.

Case1. 사용자가 페이지에 접근하면, 사용자가 접근 가능한 메뉴에 대한 네비게이션을 조회하며, Cache를 사용한다.

Case2. 사용자의 Left Navigation 정보를 저장하는 Cache의 이름은 “leftNavigation”이고, userId와 upperMenuNo를 key로 사용한다.

Case3. 만약, 사용자의 권한 변경 및 삭제등에 따라 leftNavigation에서 해당 사용자의 Cache만을 삭제하고 싶다면?

@Cacheable(cacheNames = "leftNavigationStorage", key = "#userId+#upperMenuNo")
public List<MenuNavigationOne> getLeftNavigation(String userId, Integer upperMenuNo) {
return getList(userId, upperMenuNo);
}

이때, Cacheable의 key 값에 해당 Cache의 key값을 입력하게 되는데, 이것은 SpringEL 표현식이 된다. userId는 gildong, upperMenuNo은 5라고 가정해보자.

key = “#userId+#upperMenuNo”

위와 같이 지정하면 key는 gildong5라는 String으로 저장되게 된다.

이것을 복합키로 지정하려면, SpringEL의 표현을 아래와 같이 바꿔야한다.

key = “{#userId, #upperMenuNo}”

위와 같이 지정하면, key값은 ArrayList<Object>로 생성되게 되는데, UserId로 특정 사용자의 Cache를 가져오려면 해당 key의 첫번째 엘리먼트를 추출하여 비교해야 한다. (findFirst)

뭔가 마음에 들지 않는다. Map으로 key를 지정하려면 어떻게 해야할까?

생각보다 간단하다. 아래 예제를 보자.

key = "{userId: #userId, upperMenuNo: #upperMenuNo}"

위와 같이 지정을 하게 되면, key값은 LinkedHashMap<Object, Object>로 저장되게 된다.

그럼 이제, 사용자를 삭제하는 Method에서 해당 사용자의 Cache 값만을 확인하여 삭제하는 로직을 작성해보자.

private final ConcurrentMapCacheManager cacheManager;...ConcurrentMapCache cache = (ConcurrentMapCache) cacheManager.getCache("leftNavigationStorage");

if (cache != null) {
ConcurrentMap<Object, Object> nativeCache = cache.getNativeCache();

for (Object objKey : nativeCache.keySet()) {
if (objKey instanceof String) { // Key값이 문자열일때 (UserId로 키값을 지정했을때)
String key = objKey.toString();

if (key.startsWith(userId)) {
nativeCache.remove(key);
}
} else if (objKey instanceof ArrayList) { // Key값을 ArrayList로 저장했을때
List<?> arrKey = ListUtils.toList(objKey);

Optional<?> first = arrKey.stream().findFirst();

if (first.isPresent() && first.get().equals(userId)) {
nativeCache.remove(userId);
}
} else if (objKey instanceof LinkedHashMap) { // Key값을 LinkedHashMap으로 저장했을때
LinkedHashMap<?, ?> key = (LinkedHashMap<?, ?>) objKey;

if (key.get("userId").equals(userId)) {
nativeCache.remove(userId);
}
}
}
}

Spring Boot에서 별도의 설정을 하지 않으면, CacheManager는 ConcurrentMapCacheManager를 사용하게 된다.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store