본문 바로가기
  • 실행력이 모든걸 결정한다
Spring Series/Spring Framework

[Spring] 캐시 추상화

by 김코더 김주역 2022. 10. 27.
반응형

1. 캐시와 캐시 추상화의 기본 개념

1) 캐시

- 캐시는 동일한 요청이 들어왔을 때 결과를 새롭게 만드는 대신에 저장되어 있던 기존 결과를 돌려주는 기술이다.

- 분명히 성능에 도움이 되는 기술이지만, 매번 다른 결과를 돌려주는 작업에 캐시를 적용하면 오히려 성능이 떨어지기 때문에 반복적으로 동일한 결과를 돌려주는 작업에 캐시를 적용해야 한다. 캐시를 확인하는 작업도 어느 정도 비용이 있다.

- 캐시의 내용이 유효하지 않게 되면 캐시에서 기존 내용을 바로 지워줘야 한다.

 

 

2) 캐시 추상화

- Spring 3.1에서는 AOP를 이용해 bean의 메소드에 캐시를 적용할 수 있으며, 캐시 서비스 구현 기술에 종속되지 않도록 추상화 서비스를 제공해주기 때문에 애플리케이션 코드에 영향을 주지 않는다.

- 캐시는 서버의 여러 위치에 적용이 가능한데, 애플리케이션상에서 메소드의 결과를 캐시에 저장해두는 것이 적절한 경우라면 캐시 추상화 기능을 이용하면 된다.

 

 

 

2. 캐시 어노테이션

- Spring의 캐시 서비스 추상화는 AOP를 이용하기 때문에, Spring이 제공하는 기본 AOP 설정 방법을 통해 캐시 기능을 적용할 bean과 메소드를 지정할 수 있다. 그래서 <aop:config>를 사용하거나 @Transactional 어노테이션을 활용할 수 있긴 하지만, 더 편리하게 캐시 전용 어노테이션을 사용하는 것도 고려해보자.

 

1) 캐시 어노테이션 적용을 위한 설정

- 캐시 전용 어노테이션을 사용하기 위해서는 xml 설정 파일에 <cache:annotation-driven/>을 추가하거나, @Configuration 설정 클래스에 @EnableCaching 어노테이션을 추가해주면 된다. 또한, AOP를 사용하기 때문에 프록시 모드(mode 속성)와 클래스 프록시 적용 여부(<cache:annotation-driven/>의 경우 proxy-target-class 속성, @EnableCaching의 경우 proxyTargetClass 속성)도 설정 가능하다.

 

 

2) @Cacheable

- 캐시 서비스를 지정하는 어노테이션으로 메소드, 클래스, 인터페이스 레벨에 적용 가능하다. 캐시에 저장할 내용과 캐시 속성 정보로 메소드의 파라미터와 반환 값을 사용하는 경우가 많기 때문에 보통은 메소드 단위로 지정한다.

- @Cacheable의 디폴트 속성에 캐시 이름을 지정해서 사용한다. 아래 예시는 자주 조회되는 제품을 조회하는 기능을 제공하는 메소드다.

@Cacheable("product")
public Product popularProduct(String productId) {...}

메소드 파라미터로 들어온 productId 값이 캐시의 key가 되고 Product 오브젝트는 캐시의 값이 된다. 메소드가 호출되면 메소드의 파라미터로 들어온 productId 값의 키가 product 캐시 안에 존재하는지 확인한 뒤에, 만약 존재한다면 DB를 조회하지 않고 캐시에서 해당 key에 맞는 Product를 돌려주고, 존재하지 않는다면 DB를 조회해서 그 결과를 Product 오브젝트로 변환해서 돌려주고 캐시에 추가한다.

- @Cacheable 메소드의 파라미터가 없는 경우에는 0이라는 키를 사용한다. 공지사항과 같이 조건 없이 대부분 동일한 결과를 리턴하는 메소드에 사용할 수 있을 것이다.

- @Cacheable 메소드의 파라미터가 여러 개인 경우에는 기본적으로 모든 파라미터의 hashCode() 값을 조합한 키를 사용한다. 만약 특정 파라미터만 key로 사용하고 싶다면 다음과 같이 @Cacheable의 key 속성에 SpEL 문법으로 지정해주면 된다.

// 예시 1) key가 파라미터에 노출되어 있는 경우
@Cacheable(value="product" key="#productId")
public Product popularProduct(String productId, ...) {...}

// 예시 2) key가 파라미터의 오브젝트에 숨어 있는 경우
@Cacheable(value="product", key="#searchCondition.productId")
public Product popularProduct(SearchCondition searchCondition) {...}

- @Cacheable 메소드의 파라미터 값이 특정 조건을 만족하는 경우에만 캐시를 적용하려면 @Cacheable의 condition 속성을 이용한다. condition 속성도 SpEL 문법으로 지정해준다.

@Cacheable(value="user", key="#user.id", condition="#user.level=='ADMIN'")
public User findUser(User user) {...}

 

 

3) @CacheEvict

- 캐시를 제거하는 방법으로는 일정한 주기로 캐시를 제거하는 방법과 캐시에 저장한 값이 변경될 때 캐시를 제거하는 방법이 있다. @CacheEvict는 이 두 가지 방법으로 캐시를 제거할 수 있도록 하는 어노테이션이다.

- 메소드에 @CacheEvict 어노테이션을 붙이고 캐시 이름과 key만 지정해주면, 이 메소드가 실행될 때 캐시의 해당 내용을 제거해준다.

@CacheEvict(value="product", key="#product.productId")
public void updateProduct(Product product) {...}

- 캐시의 모든 내용을 제거하려면 @CahceEvict의 allEntries 속성을 true로 지정해준다.

@CacheEvict(value="product", allEntries=true)
public void clearProductCache() {...}

 

 

4) @CachePut

- 캐시에 값을 저장하는 용도로만 사용하는 어노테이션이다. 저장된 캐시를 사용하지 않고 항상 메소드를 실행한다.

 

 

 

3. 캐시 매니저

마지막으로, 캐시 매니저를 bean으로 등록하는 일만 남았다.

 

1) 캐시 매니저 소개

- 캐시 매니저는 캐시 기술을 지원하는 캐시 추상화 API로, org.springframework.cache.CacheManager 인터페이스를 구현해서 만든다.

- 캐시 매니저는 bean으로 등록해서 사용할 수 있다.

 

 

2) CacheManager 구현체

(1) ConcurrentMapCacheManager

- 자바의 ConcurrentHashMap을 이용해 캐시 기능을 구현한 클래스인 ConcurrentMapCache를 캐시로 사용한다.

- 속도는 매우 빠르지만 고급 캐시 프레임워크가 제공하는 기능은 지원하지 않는다.

- cacheManager라는 이름의 bean으로 등록해서 사용해야 한다.

<bean id="cacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager" />

 

(2) SimpleCacheManager

- 기본적으로 제공하는 캐시가 없기 때문에 Cache 인터페이스의 구현체를 직접 등록해줘야 한다.

 

(3) EhCacheCacheManager

- 캐시 프레임워크인 EhCache를 지원하는 캐시 매니저다. 

- EhCache의 캐시 매니저(net.sf.ehcache.CacheManager)를 프로퍼티로 등록해도 되고, EhCache가 아직 미숙하다면 Spring이 제공하는 EhCacheManagerFactoryBean을 프로퍼티로 등록해도 된다. EhCacheManagerFactoryBean을 사용하는 경우에는 Ehcache에 대한 xml 설정 파일을 configLocation 프로퍼티에 지정해주면 된다.

xml 설정 예시

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhcacheCacheManager">
    <property name="cacheManager" ref="ehcache" />
</bean>

<bean id="ehcache" class="org.springframework.cache.ehcache.EhcacheManagerFactoryBean">
    <property name="configLocation" value="ehcache.xml" />
</bean>

자바 코드 설정 예시

@Bean
public CacheManager cacheManager() {
    EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager();
    ehCacheCacheManager.setCacheManager(ehCacheManagerFactoryBean());
    return ehCacheCacheManager;
}

@Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
    EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
    ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml", getClass()));
    return ehCacheManagerFactoryBean;
}

 

(4) CompositeCacheManager

- 하나 이상의 캐시 매니저를 동시에 사용하도록 지원해주는 캐시 매니저다.

- cacheManagers 프로퍼티에 적용할 캐시 매니저 bean을 모두 등록해주면 된다.

- addNoOpCache 프로퍼티를 true로 설정해주면, 캐시를 지원하지 않는 환경에서 캐시 관련 설정을 제거하지 않아도 에러가 발생하지 않는다.

<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
    <property name="cacheManagers"><list>
        <ref bean="jdkCache"/>
        <ref bean="gemfireCache"/>
    </list></property>
    <property name="addNoOpCache" value="true"/>
</bean>

 

 

● 참고 자료 : 토비의 스프링 3.1 Vol.2

 

 

반응형

댓글