[Spring Boot] Spring Cache
Spring Boot캐시 추상화를 제공한다.
- Spring AOP(Aspect-oriented programming)를 이용하여 Bean으로 캐시를 설정한다.
- 특정 캐시 기술에 종속되지 않기 때문에 코드에 미치는 영향을 최소화하고 다양한 Cache Solutions을 일관되게 사용 할 수 있다.
기본적으로 ConcurrentHashMap 기반으로 만들어진 Map 저장소를 사용한다.
Annotation 기반 캐싱과 XML 기반 캐싱이 있다.
Cache Manager
ConcurrentMapCacheManager: 캐시 정보를 ConcurrentHashMap에 저장하는 간단한 캐시 매니저. 기능이 적어서 실제 서비스 사용을 권장하지 않는다.
SimpleCacheManager: 기본적으로 제공하는 캐시가 없는 캐시 매니저. setCache로 사용할 캐시를 직접 등록해야 한다.
EhCacheCacheManager: 캐시 프레임워크 Ehcache를 지원하는 캐시 매니저
CompositeCacheManager: 캐시 매니저를 여러 개 사용할 수 있게 하는 복합 캐시 매니저
JCacheCacheManager: JSR-107 기반 캐시를 사용하는 캐시 매니저
의존성 추가
dependencies {
implementation("org.springframework.boot:spring-boot-starter-cache")
}
캐시 기능 활성화
방법1. 기본 Spring Cache 기능 사용 설정
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.cache.annotation.EnableCaching
@SpringBootApplication
@EnableCaching
class DevApplication
방법2. 서브더티 모듈 캐시 사용 설정
@Configuration
@EnableCaching
class CacheConfig {
@Bean
fun cacheManager(): CacheManager {
val manager = SimpleCacheManager()
manager.setCaches(
listOf(
ConcurrentMapCache("cacheStore1"),
ConcurrentMapCache("cacheStore2")
)
)
return manager
}
}
Annotation 기반 캐싱
@Cacheable
캐시에 메서드 결과를 저장한다. 캐시 데이터가 있으면 메서드 내부 로직을 수행하지 않고 캐싱된 결과를 반환한다.
package org.springframework.cache.annotation;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable
기본 사용. 캐시 이름을 지정하여 메소드 위에 선언한다.
@Cacheable("cacheStore")
fun findComment(userId: Long): Comment
인자가 Entity면 반드시 캐시에 저장할 키 값을 지정해야 한다.
@Cacheable(cacheNames = ["cacheStore"], key = "#user.id")
fun findComment(user: User): Comment
동일한 캐시에서 key는 중복될 수 없다. 아래 코드는 오류가 발생한다.
@Cacheable(cacheNames = ["cacheStore"], key = "#user.id")
fun findComment1(user: User): Comment
@Cacheable(cacheNames = ["cacheStore"], key = "#user.id")
fun findComment2(user: User): Comment
@CachePut
캐시 갱신
@Cacheable과 같이 사용하지 않는 것을 권장한다.
@CachePut(cacheNames = ["cacheStore"], key = "#user.id")
fun updateComment(user: User): Comment
@CacheEvict
캐시 제거
캐시 생명주기와 별개로 캐싱된 데이터 원본이 변경, 삭제되어서 정합성이 깨지는 것을 방지하기 위해 사용한다.
반환값이 없는 메서드에 사용 가능하다
@CacheEvict(cacheNames = ["cacheStore"], key = "#user.id")
fun loadComment(user: User)
@Caching
동일한 유형을 여러 개 달아야할 때 사용한다
@Caching(cacheable = [Cacheable("store1"), Cacheable(cacheNames = ["store2"], key = "#p0")])
fun method1(id: Long, date: LocalDate)
@Caching(put = [CachePut("store1"), CachePut(cacheNames = ["store2"], key = "#p0")])
fun method2(id: Long, date: LocalDate)
@Caching(evict = [CacheEvict("store1"), CacheEvict(cacheNames = ["store2"], key = "#p0")])
fun method3(id: Long, date: LocalDate)
@CacheConfig
단일 클래스 수준으로 캐시 이름을 지정한다.
@CacheConfig(cacheNames = ["cacheStore"])
class CommentRepositoryImpl : CommentRepository {
@Cacheable(key = "#user.id")
fun findComment(user: User): Comment
}
ERROR
Exception in thread "http-nio-8082-exec-3" java.lang.NoClassDefFoundError: ch/qos/logback/classic/spi/ThrowableProxy
Docker restart 할 때 간혈적으로 발생
cache가 없어졌는데 존재한다고 찾으면서 발생하는 오류로 추측
spring-boot-starter-logging 또는 logback-classic 을 종속성 추가하면 해결되는지 확인 필요
Reference
Spring Cache 캐시 추상화 기본적인 사용법 @Cacheable @CachePut @CacheEvict
Spring boot 에 caffeine 캐시를 적용해보자 - 어떻게하면 일을 안 할까?
스프링에서 캐시 사용하여 프로젝트 성능 개선해보기 - Jinia's LOG'
스프링 책 읽기(Spring in action) - 13. 데이터 캐싱하기