2022. 4. 3. 00:40ㆍSpring
Spring Cache 사용법, Annotation 등을 알아보고 설정 방식을 알아보는 것이 해당 포스팅의 목표입니다.
📌 Spring Cache Series
해당 포스팅에서는 Spring Cache에 대해 다룹니다.
Cache를 사용하기 위해서는 CacheManager가 필요한데요.
연결되는 다음 포스팅에서는 로컬 캐시 중 성능에 유리한 Caffeine Cache를 다룹니다.
----------------- INDEX -----------------
----------------------------------------------
Cache ?
자주 사용하는 데이터를 저장해서 재활용하는 기술
일반적으로 캐시는 일시적인 특징이 있는 데이터 집합을 고속 데이터 스토리지 저장합니다.
따라서 이후에 해당 데이터에 대한 요청이 있을 경우,
데이터의 원본 스토리지 위치로 액세스할 때보다 더 빠르게 요청을 처리할 수 있습니다.
이렇게 캐싱을 사용하면 이전에 검색했거나 계산한 데이터를 효율적으로 재사용할 수 있습니다.
부분적으로 설명하자면,
일반적으로 동일한 리소스 대해 빈번한 SELECT로 발생되는 DBMS 과부하를 줄이고자 사용합니다.
---
쉬운 비유를 해볼게요.
도서관에서 책을 빌리려고 "이달의 추천 책 리스트"를 확인하고 싶은데,
이 책 리스트를 확인하기 위해서는 도서관으로 가야합니다.
하지만, 도서관까지 가기엔 너무나도 번거롭죠.
그래서 생각해낸 것이 책 리스트를 복사해서 집에서도 확인할 수 있게합니다.
이때, 도서관에 배치된 "이달의 추천 책 리스트"이 바로 원본 데이터고
집으로 가져갈 복사본이 캐시, 또 집이라는 근접한 공간이 바로 고속 데이터 스토리지에 해당합니다.
캐시를 사용할 때는 몇 가지 주의사항이 있지만,
본 포스팅에서 다루기엔 무겁기 때문에 생각할 만한 요소들을 던져보겠습니다.
- 복사를 위해서는 복사집에 가서 500원이라는 요금을 내야합니다. 즉, 캐시를 저장할 때의 비용이 필요합니다.
- 이달의 추천 책 리스트는 변경되는 데이터입니다. 변경 전 복사본을 가지고 있으면 문제가 생깁니다.
- 종이로는 그럴일이 없겠지만, 복사된 양이 너무나도 많아 집에서 다 가지고 있기 버거울 수 있습니다.
이런 내용들을 계속해서 고려하며 캐시를 다뤄야 합니다.
📌 Cache Abstraction
스프링에서는 빈의 메소드에 캐시를 적용할 수 있는 기능을 제공합니다.
트랜잭션과 마찬가지로, AOP를 이용해 메소드 실행 과정에 투명하게 적용됩니다.
스프링의 캐시 추상화는 캐시 특정 기술에 종속되지 않으며 AOP를 통해 적용되어
애플리케이션 코드를 수정하지 않고 캐시 부가기능을 추가할 수 있습니다.
즉, 캐시 API를 코드에 추가하지 않아도 손쉽게 캐시 기능을 부여할 수 있습니다.
또한 캐시 서비스 구현 기술에 종속되지 않도록 추상화 서비스를 제공하기 때문에 환경이 바뀌거나 적용할 기술을 변경해서 캐시 서비스의 종류가 달라지더라도 애플리케이션 코드에 영향을 주지 않습니다.
더 자세히 말하면 캐싱이 필요한 비즈니스 로직에서 EhCache, redis 등 캐싱 종류에 의존하지 않고 추상화된 인터페이스로 캐싱을 적용 할 수 있습니다.
📌 Cache Manager
캐시 추상화에서는 캐시 기술을 지원하는 캐시 매니저를 빈으로 등록해야 합니다.
✔️ ConcurrentMapCacheManager
ConcurrentHashMap을 이용해 캐시 기능을 구현하는 간단한 캐시 매니저입니다.
캐시 정보를 Map 타입으로 메모리에 저장해두기 때문에 빠르고 별다른 설정이 필요없다는 장점이 있지만, 실제 서비스에서 사용하기에는 기능이 빈약합니다.
✔️ SimpleCacheManager
기본적으로 제공하는 캐시가 없습니다.
사용할 캐시를 직접 등록하여 사용하기 위한 캐시 매니저입니다.
✔️ EhCacheCacheManager
자바에서 유명한 캐시 프레임워크 중 하나인 EhCache를 지원하는 캐시 매니저입니다.
✔️ CaffeineCacheManager
Java 8로 Guava 캐시를 재작성한 Caffeine 캐시를 사용하는 캐시 매니저입니다.
EhCache와 함께 인기 있는 캐시 매니저인데, EhCache보다 좋은 성능을 갖는다고 해서 최근 많이 사용합니다.
✔️ CompositeCacheManager
한 개 이상의 캐시 매니저를 사용하도록 지원해주는 혼합 캐시 매니저입니다.
✔️ JCacheCacheManager
JSR-107 기반의 캐시를 사용하는 캐시 매니저입니다.
📌 의존성 추가
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
</dependencies>
@EnableCaching
Spring에서 @Cacheable과 같은 어노테이션 기반의 캐시 기능을 사용하기 위해서는 먼저 별도의 선언이 필요합니다.
때문에, @EnableCaching 어노테이션을 설정 클래스에 추가해주어야 합니다.
✔ Example
@EnableCaching
@Configuration
public class EhCacheConfiguration { /* ... */}
@Cacheable
적용된 메소드의 리턴값을 기준으로 캐시에 값을 저장합니다.
적용할 메소드에 @Cacheable 어노테이션을 붙여주면
캐시에 데이터가 없을 경우에는 기존의 로직을 실행한 후에 캐시에 데이터를 추가하고,
캐시에 데이터가 있으면 캐시의 데이터를 반환합니다.
cacheName(value) 값을 기준으로 캐시를 저장하는데,
만약 더 디테일한 값으로 저장해야한다면 key와 함께 저장합니다.
사용 예시를 확인해보면 이해가 쉬울거에요.
✔ Optional Element
Element | Description | Type |
cacheName | 캐시 이름 (설정 메서드 리턴값이 저장되는) | String[] |
value | cacheName의 alias | String[] |
key | 동적인 키 값을 사용하는 SpEL 표현식 동일한 cache name을 사용하지만 구분될 필요가 있을 경우 사용되는 값 |
String |
condition | SpEL 표현식이 참일 경우에만 캐싱 적용 - or, and 등 조건식, 논리연산 가능 |
String |
unless | 캐싱을 막기 위해 사용되는 SpEL 표현식 condition과 반대로 참일 경우에만 캐싱이 적용되지 않음 |
String |
cacheManager | 사용 할 CacheManager 지정 (EHCacheCacheManager, RedisCacheManager 등) |
String |
sync | 여러 스레드가 동일한 키에 대한 값을 로드하려고 할 경우, 기본 메서드의 호출을 동기화 즉, 캐시 구현체가 Thread safe 하지 않는 경우, 캐시에 동기화를 걸 수 있는 속성 |
boolean |
✔ Example
@Cacheable(cacheNames = "platformTeamBooks", key = "#root.target + #root.methodName + '_'+ #p0")
public Book getPlatformTeamBook(int bookId) { /* ... */}
@Cacheable(cacheNames = "users", condition = "#user.type == 'ADMIN'")
public Book selectUser(User user) { /* ... */}
@Cacheable(value="addresses", unless="#result.length()<64")
public String getAddress(Customer customer) {...}
@CacheEvict
메서드가 호출 될 때 저장된 캐시를 삭제합니다.
캐싱된 데이터가 변경될 경우 (Insert, update, delete) 기존의 캐싱된 데이터가 바뀌어야 하기 때문에 지워주는 작업이 필수겠죠?
✔ Optional Element
Element | Description | Type |
cacheName | 제거할 캐시 이름 | String[] |
value | cacheName의 Alias | String[] |
key | 동적인 키 값을 사용하는 SpEL 표현식 동일한 cache name을 사용하지만 구분될 필요가 있을 경우 사용되는 값 |
String |
allEntries | 캐시 내의 모든 리소스를 삭제할지의 여부 | boolean |
condition | SpEL 표현식이 참일 경우에만 삭제 진행 - or, and 등 조건식, 논리연산 가능 |
String |
cacheManager | 사용 할 CacheManager 지정 (EHCacheCacheManager, RedisCacheManager 등) |
String |
beforeInvocation | true - 메서드 수행 이전 캐시 리소스 삭제 false - 메서드 수행 후 캐시 리소스 삭제 |
boolean |
✔ Example
@CacheEvict(key = "testKey", condition="#caching")
public Object removeSome(boolean caching) { /* ... */}
@CacheEvict(value = "platformTeamBooks", allEntries = true)
public Book insertPlatformTeamBook(BookVO bookVO) { /* ... */}
@CachePut
캐시에 값을 저장하는 용도로만 사용합니다.
@Cacheable과 유사하게 실행 결과를 캐시에 저장하지만,
조회 시에 저장된 캐시의 내용을 사용하지는 않고 항상 메소드의 로직을 실행합니다.
✔ Optional Element
Element | Description | Type |
cacheName | 입력할 캐시 이름 | String[] |
value | cacheNamed의 Alias | String[] |
key | 동적인 키 값을 사용하는 SpEL 표현식 동일한 cache name을 사용하지만 구분될 필요가 있을 경우 사용되는 값 |
String |
cacheManager | 사용 할 CacheManager 지정 (EHCacheCacheManager, RedisCacheManager 등) |
String |
condition | SpEL 표현식이 참일 경우에만 캐싱 적용 - or, and 등 조건식, 논리연산 가능 |
String |
unless | 캐싱을 막기 위해 사용되는 SpEL 표현식 condition과 반대로 참일 경우에만 캐싱이 적용되지 않음 |
String |
✔ Example
@Cacheable(key = "testKey", condition="#caching")
public Object modifySome(boolean caching) {/* ... */}
@Caching
하나의 메서드를 호출 할 때 Cacheable, CacheEvict 등 여러 개의 캐싱 동작을 수행해야 할 때 사용합니다.
✔ Optional Element
Element | Description | Type |
cacheable | 적용 될 @Cacheable array를 등록 | Cacheable[] |
evict | 적용 될 @CacheEvict array를 등록 | CacheEvict[] |
put | 적용 될 @Cacheput array를 등록 | CachePut[] |
✔ Example
@Caching(evict = {
@CacheEvict("addresses"),
@CacheEvict(value="directory", key="#customer.name") })
public String getAddress(Customer customer) { /* ... */ }
@CacheConfig
클래스 단위로 캐시 설정을 동일하게 하고 싶을 때 사용합니다.
✔ Optional Element
Element | Description | Type |
cacheNames | 해당 클래스 내 정의된 캐시 작업에서의 default 캐시 이름 | String[] |
cacheManager | 사용 할 CacheManager 지정 (EHCacheCacheManager, RedisCacheManager 등) |
String |
✔ Example
@CacheConfig(cacheNames={"addresses"})
public class CustomerDataService {
// ...
@Cacheable
public String getAddress(Customer customer) {...}
// ...
}
Spring Cache의 설정과 관련된 내용을 다뤘습니다.
Cache를 적용하기 위해서는 위에서 잠깐 언급했던 Cache Manager가 필요합니다.
다음 포스팅은 로컬 캐시 중 성능에 유리한 Caffeine Cache를 다뤄보겠습니다.
오타나 잘못된 내용을 댓글로 남겨주세요!
감사합니다 ☺️
'Spring' 카테고리의 다른 글
Spring Security, 제대로 이해하기 - FilterChain (18) | 2022.04.14 |
---|---|
Caffeine Cache, 어렵지 않게 사용하기 1 (0) | 2022.04.04 |
Spring, Bulk Insert 성능 측정 (0) | 2022.03.29 |
Spring Security, 어렵지 않게 설정하기 (6) | 2022.03.24 |
Spring WebClient, 어렵지 않게 사용하기 (25) | 2022.03.17 |
Backend Software Engineer
𝐒𝐮𝐧 · 𝙂𝙮𝙚𝙤𝙣𝙜𝙨𝙪𝙣 𝙋𝙖𝙧𝙠