[Spring Boot] Spring Cache

Spring Boot

(Update : 2023-02-18)

Language :

캐시 추상화를 제공한다.

  • Spring AOP(Aspect-oriented programming)를 이용하여 Bean으로 캐시를 설정한다.
  • 특정 캐시 기술에 종속되지 않기 때문에 코드에 미치는 영향을 최소화하고 다양한 Cache Solutions을 일관되게 사용 할 수 있다.

기본적으로 ConcurrentHashMap 기반으로 만들어진 Map 저장소를 사용한다.

Annotation 기반 캐싱과 XML 기반 캐싱이 있다.

Cache Manager

ConcurrentMapCacheManager: 캐시 정보를 ConcurrentHashMap에 저장하는 간단한 캐시 매니저. 기능이 적어서 실제 서비스 사용을 권장하지 않는다.

SimpleCacheManager: 기본적으로 제공하는 캐시가 없는 캐시 매니저. setCache로 사용할 캐시를 직접 등록해야 한다.

EhCacheCacheManager: 캐시 프레임워크 Ehcache를 지원하는 캐시 매니저

CompositeCacheManager: 캐시 매니저를 여러 개 사용할 수 있게 하는 복합 캐시 매니저

JCacheCacheManager: JSR-107 기반 캐시를 사용하는 캐시 매니저

의존성 추가

xml

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-cache")
}

캐시 기능 활성화

방법1. 기본 Spring Cache 기능 사용 설정

kotlin

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.cache.annotation.EnableCaching

@SpringBootApplication
@EnableCaching
class DevApplication

방법2. 서브더티 모듈 캐시 사용 설정

kotlin

@Configuration
@EnableCaching
class CacheConfig {

    @Bean
    fun cacheManager(): CacheManager {
        val manager = SimpleCacheManager()
        manager.setCaches(
            listOf(
                ConcurrentMapCache("cacheStore1"),
                ConcurrentMapCache("cacheStore2")
            )
        )
        return manager
    }

}

Annotation 기반 캐싱

@Cacheable

캐시에 메서드 결과를 저장한다. 캐시 데이터가 있으면 메서드 내부 로직을 수행하지 않고 캐싱된 결과를 반환한다.

kotlin

package org.springframework.cache.annotation;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable

기본 사용. 캐시 이름을 지정하여 메소드 위에 선언한다.

kotlin

@Cacheable("cacheStore")
fun findComment(userId: Long): Comment

인자가 Entity면 반드시 캐시에 저장할 키 값을 지정해야 한다.

kotlin

@Cacheable(cacheNames = ["cacheStore"], key = "#user.id")
fun findComment(user: User): Comment

동일한 캐시에서 key는 중복될 수 없다. 아래 코드는 오류가 발생한다.

kotlin

@Cacheable(cacheNames = ["cacheStore"], key = "#user.id")
fun findComment1(user: User): Comment

@Cacheable(cacheNames = ["cacheStore"], key = "#user.id")
fun findComment2(user: User): Comment

@CachePut

캐시 갱신

@Cacheable과 같이 사용하지 않는 것을 권장한다.

kotlin

@CachePut(cacheNames = ["cacheStore"], key = "#user.id")
fun updateComment(user: User): Comment

@CacheEvict

캐시 제거

캐시 생명주기와 별개로 캐싱된 데이터 원본이 변경, 삭제되어서 정합성이 깨지는 것을 방지하기 위해 사용한다.

반환값이 없는 메서드에 사용 가능하다

kotlin

@CacheEvict(cacheNames = ["cacheStore"], key = "#user.id")
fun loadComment(user: User)

@Caching

동일한 유형을 여러 개 달아야할 때 사용한다

kotlin

@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

단일 클래스 수준으로 캐시 이름을 지정한다.

kotlin

@CacheConfig(cacheNames = ["cacheStore"])
class CommentRepositoryImpl : CommentRepository {
    @Cacheable(key = "#user.id")
    fun findComment(user: User): Comment
}

ERROR

text

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

Integration

Spring Cache 캐시 추상화 기본적인 사용법 @Cacheable @CachePut @CacheEvict

Spring boot 에 caffeine 캐시를 적용해보자 - 어떻게하면 일을 안 할까?

스프링에서 캐시 사용하여 프로젝트 성능 개선해보기 - Jinia's LOG'

스프링 책 읽기(Spring in action) - 13. 데이터 캐싱하기

Cache에 대하여.. (Spring+EHCache) | Carrey`s 기술블로그

Spring Cache, 제대로 사용하기

스프링부트 Caching 도입하기(Redis, Ehcache)

민갤

Back-End Developer

백엔드 개발자입니다.