SpringBoot

[Spring-boot] Spring cache란? 사용방법 및 기본

Jeong Jeon
반응형

최근 면접에서 알게된 Spring Cache에 대해 알아보려고한다.

기존 Cache라고 하면 Redis와 같은 Inmemory DB를 사용해서 캐싱해두고 (이를 글로벌캐시라고 한다.) 데이터를 빠르게 가져다 쓰는것만 알고있었는데, 각 모듈(서버)별 동기화가 필요없거나 해당 모듈에서만 사용하기엔 (로컬캐시 형태) Spring Cache가 적합하다고 느껴졌다.

 

캐시는 반복적으로 동일한 데이터를 사용할떄 유용하게 사용된다.

예를들어 한번 조회했던 결과를 캐싱해놓고 다음번엔 굳이 조회를 통하지않고 캐싱되어있는 데이터만 사용하면 효율적인것...!

 

Spring 캐시(Cache)의 추상화

캐시 서비스는 트랜잭션 처럼 AOP를 이용해 메소드 실행 과정에 적용될수 있다. 간단하게 보면 Aop를 사용해서 캐싱시켜놓고 메인 비지니스로직과 분리해서 사용할 수 있다는것...!

또 스프링은 캐시 구현 기술에 종속되지 않도록 추상화된 서비스를 제공하고 있다고한다. 그러므로 캐시 기술이 변경되어도 기존 어플리케이션에는 큰 영향을 주지 않아 효과적으로 사용할 수 있다.

 

간단하게 기본 개념을 정리해두고, 더 자세한건 기존 코드에 반영해서 최적화 시켜보거나, 테스트코드를 더 만들어서 알아보는게 좋을것 같다.

 

기본설정

 

1). Gradle Dependency추가

implementation 'org.springframework.boot:spring-boot-starter-cache'

 

2). Cache Config와 CacheManager를 설정해본다.

 @EnableCaching
 @Configuration
 public class CacheConfig(){

     @Bean
     public CacheManager cacheManager(){
         SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
         return simpleCacheManager;            

     }
 }
  • @EnabbleCaching : 캐시를 사용하겠다를 선언한다.
  • CacheManager : 스프링에서 제공하는 캐시 추상화 API
    • SimpleCacheManager : 구현체가 없는 기본 캐시매니저이다. setCaches를 통해 구현체를 등록해서 사용할 수 있다. 스프링의 기본 구현체는 ConcurrentMapCache이다.
    • ConcurrentMapCacheManager : ConcurrentMapCache만 사용하는 캐시 매니저. 캐시 정보를 ConcurrentHashMap을 사용해 메모리에 저장한다.
      속도가 매우 빠르지만 기능이 많지 않다라는 단점이있다. 캐시별 용량제한 기능이나 다중 서버분산 대응 기능이 없다.
    • EhCacheManager : 캐시 프레임워크인 EhCache를 지원하는 캐시 매니저. 가장 많이 사용된다고하는데, 하나하나 자세히 써보기 전까진 뭐..ㅎㅎ
    • CompositeCacheManager : 1개 이상의 캐시 매니저를 사용하도록 지원해주는 혼합 캐시 매니저
    • CaffeineCacheManager : Caffeine 캐시를 사용하는 캐시 매니저
    • JCacheCacheManager : JSR-107 기반의 캐시를 사용하는 캐시 매니저

 

EhCacheManager가 가장 많이 쓰인다고하니 일단 접근해보자...!

implementation group: 'net.sf.ehcache', name: 'ehcache' , 버전

버전은 현재 상황에 맞춰 사용하자.

 

위 Config파일에 EhCacheManager를 위한 설정을 추가해보자

 

@Configuration
public class CacheConfig {

     @Bean
     public EhCacheManagerFactoryBean cacheManagerFactoryBean(){
         return new EhCacheManagerFactoryBean();
     }

     @Bean
     public EhCacheCacheManager ehCacheCacheManager(){

     // 캐시 설정
         CacheConfiguration cacheConfiguration = new CacheConfiguration()
                 .eternal(false)
                 .timeToIdleSeconds(0)
                 .timeToLiveSeconds(21600)
                 .maxEntriesLocalHeap(0)
                 .memoryStoreEvictionPolicy("LRU") //LRU, LFU, FIFO
                 .name("caching"); //캐시이름
    // 설정을 가지고 캐시 생성     
         Cache cache = new net.sf.ehcache.Cache(cacheConfiguration);

      // 캐시 팩토리에 eh캐시 추가
      cacheManagerFactoryBean().getObject().addCache(cache);
      // 캐시팩토리 등록
      return new EhCacheCacheManager(Objects.requireNonNull(cacheManagerFactoryBean().getObject()));
    }
}

 

<속성설명>

name 코드에서 사용할 캐시 name 필수
maxEntriesLocalHeap 메모리에 생성 될 최대 캐시 갯수 0
maxEntriesLocalDisk 디스크에 생성 될 최대 캐시 갯수 0
eternal 영속성 캐시 설정 
external = “true” -> timeToIdleSecond, timeToLiveSeconds 설정 적용X
false
timeToIdleSecond 일정 시간동안 캐시 미호출시 삭제 0
timeToLiveSeconds 일정 시간 후 캐시 삭제 0
overflowToDisk 오버플로우 된 항목에 대해 disk에 저장할 지 여부 false
diskPersistent 캐시를 disk에 저장하여, 서버 로드 시 캐시를 보관할지 설정 false
diskExpiryThreadIntervalSeconds Disk Expiry 스레드의 작업 수행 간격 설정 0
memoryStoreEvictionPolicy 캐시의 객체 수가 maxEntriesLocalHeap에 도달하면, 객체를 추가하고 제거하는 정책 설정 (LRU, LFU, FIFO) LRU

 

eh캐시 설정이 끝났다. 기본적으로ConcurrentMapCache가 Default라는데, 가장 많이 쓴다고하니 일단 써보자.

 

@Cacheable, @CachePut, @CacheEvict 어노테이션을 통한 캐시 제어

 

1). @Cacheable : 캐시에 데이터가 없을 경우에는 기존의 로직을 실행한 후에 캐시에 데이터를 추가하고, 캐시에 데이터가 있으면 캐시의 데이터를 반환

2). @CachePut : 실행 결과를 캐시에 저장하지만, 조회 시에 저장된 캐시의 내용을 사용하지는 않고 항상 메소드의 로직을 실행

3). @CacheEvict : 캐시는 적절한 시점에 제거 혹은 업데이트 되어야한다. 만약 값이 달라진다면 캐시를 제거해야한다. 캐시를 제거하는 방법은 크게 2가지가 있다.

  • 일정한 주기로 캐시를 제거
  • 값이 변할 때 캐시를 제거

위 어노테이션을  사용해서 특정 종목의 랭킹을 가져오는것을 캐싱해서 사용해보자.

@Cacheable

@Cacheable(value = "ranking", key = "#eventName")
public Ranker getHighRanker(String eventName) {

}

이렇게 하면 ranking이라는 캐시 아래로, eventName을 Key로 한 랭킹이 캐싱된다.

만약 Object를 파라미터로 받고 해당 Object안의 값을 key로 사용하고 싶다면 ??? => key = "#Object(객체).필트" 로 사용하면 된다.

이때 특정 조건에 따라 캐싱하는것도 지원하는데, @Cacheable에 condition = "조건" 으로 넣어줄수있다.

 

 

@CachePut

@CachePut(value = "ranking", key = "#eventName")
public int updateHighRanker(Ranker ranker, String eventName) {
	
}

사용법은 같다.

 

 

@CacheEvict

@Cacheable(value = "ranking", key = "#eventName")
public int deleteEvent(String eventName) {

}

 

우선 여기까지 간단하게 사용법을 확인해보았다.

미리 알았더라면 한번 들어봤었더라면 좋았을텐데... 하지만 새로운 내용들을 알게되는것은 항상 매력적이다. 하나하나 알아가면서 매순간 발전하는 사람이되길...!

반응형