오늘 알아 볼 내용은 Spring Boot의 Cache를 알아보자.

자주 읽기가 일어나는 곳에는 cache를 사용하는 것은 좋은 방법이라고 생각한다. 데이터베이스도 읽기 작업이 대부분이니 잘 사용하면 좋을 듯하다. 물론 아무대나 사용하지 말고 적당한 곳을 잘 고민해서 사용해야 될 것이다.

Spring Boot에서는 기본적으로 여러 서드파트 cache 라이브러리들을 지원한다. 꽤나 많은 캐시들을 지원하니 자신에게 맞는 캐시을 골라서 사용하면 된다. 기본적으로 지원할 뿐이지 만약 아래에 자신이 맞는 캐시가 없다면 따로 설정해주면 될 것같다.
일단 JSR-107(JCache) 구현체들은 모두 지원한다. 스펙에 맞게 잘 구현되었다면 모두 지원하나 스펙에 맞지 않으면 장담하지 못할 듯하다. cache로 유명한 EhCache도 지원하고 Hazelcast, Infinispan, Couchbase, Redis, Caffeine, Guava 등이 기본적으로 자동설정에 포함되어 있다. 참고할 사항은 Guava 캐시는 향후에는 지원을 하지 않을 예정이다. 1.5에서는 @Deprecated 되었고 2.0에서는 제거 되었다.

Spring에서는 추상화된 캐시를 지원하는데 CacheManager라는 인터페이스이다. CacheManager인터페이스만 구현해주면 된다. 물론 그에 따른 Cache 도 구현해 줘야 한다.

기본적인 설정 방법이다. spring-boot-starter-cache를 디펜더시 받고 @EnableCaching 어노테이션을 선언하면 설정은 끝난다.

@SpringBootApplication
@EnableCaching
public class Application  {
  //blabla
}
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

기본적으로 spring-boot-starter-cache 이외에 아무 서드파트 라이브러리를 추가 하지 않느다면 기본설정인 SimpleCacheConfiguration 가 동작하며 CacheManager로는 ConcurrentMapCacheManager가 빈으로 등록이 된다.

이 외 서드파트 디펜더시를 살펴보자.
아래는 caffeine cache 디펜더시 방법이다.

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

그 이외에 다른 서드파트 디펜더시들이다.

//redis
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

//couchbase
<dependency>
    <groupId>com.couchbase.client</groupId>
    <artifactId>java-client</artifactId>
</dependency>
<dependency>
    <groupId>com.couchbase.client</groupId>
    <artifactId>couchbase-spring-cache</artifactId>
</dependency>

//hazelcast
<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast</artifactId>
</dependency>
<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast-spring</artifactId>
</dependency>

좀 더 많은 서드파트 캐시들이 있으니 문서나 예제를 참고 하길 바란다. 설정하는 법은 알아봤으니 한번 사용해보자.

@Cacheable 어노테이션을 사용해서 캐시를 설정할 수 있다. 속성에는 value, key, condition, keyGenerator, cacheManager등 여러개의 속성들이 존재한다. 좀 더 많은 속성이 있으니 문서를 참고하고 자주 사용하는 속성들만 작성하였다.

value 는 캐시의 이름을 나타내고, key는 캐시할 키를 지정하면 된다. 키를 지정하지 않을 경우에는 파라미터로 기본적인 키를 설정한다. 만약 파라미터가 여러개라면 모든 파라미터의 해시코드를 생성해 합쳐서 키로 만들어준다. condition은 특정 조건을 넣어서 상황에 따라 캐시를 할지 하지 않을지 결정하는 속성이다. cacheManager 는 해당하는 cacheManager를 설정할 수 있다.

한번 사용해보자.

@Cacheable(value = "wonwoo", condition = "#name.length() < 10")
public String find(String name) {
  logger.info("cache find .. {}", name);
  return name;
}

만약 위와 같이 캐시를 설정 하였다면 캐시명은 wonwoo 되고 key는 name이 된다. 그리고 파라미터(name)길이가 10보다 작을 때만 캐시를 한다고 설정 한 것과 같다.

@Cacheable(key = "#name", value = "wonwoo", condition = "#name.length() < 10")

위와 동일한 설정을 갖게 된다. 만약 Object 형태를 파라미터로 넘긴다면 모든 프로퍼티를 키로 사용해도 되지만 특정 프로퍼티만 사용하고 싶다면 SpEL를 작성 하면 된다.

@Cacheable(key = "#myObject.name", value = "wonwoo", condition = "#name.length() < 10")

키 전략을 자신에 맞게 커스텀하여 만들 수 있는데 아래와 같이 keyGenerator 속성을 이용하면 된다.

@Cacheable(value = "wonwoo", condition = "#name.length() < 10", keyGenerator = "keyGenerator")

위와 같이 설정 한 후에 KeyGenerator 인터페이스 구현하고 빈으로 등록 해주면 된다.

public class MyGenerator implements KeyGenerator {
  @Override
  public Object generate(Object o, Method method, Object... objects) {
    //blabla
  }
}

이때 주의할 점은 key와 같이 사용하면 안된다.

@Cacheable 어노테이션 말고 @CacheEvict 어노테이션도 존재하는데 이는 캐시 데이터를 삭제할 때 처리 하면 된다.

@CacheEvict(key = "#name", value = "wonwoo")
public String update(String name) {
  logger.info("cache update .. {}", name);
  return name;
}

특정 key의 데이터를 삭제하는 설정이다. 예를들어 key의 값이 123으로 캐시된 값이 한개 있다고 가정해보자. 이를 삭제 하고 싶다면 name의 값을 123으로 메서드를 호출 하면된다. 이 메서드가 호출 되고 나서 다시 @Cacheable 어노테이션의 메서드를 호출 한다면 (key:123) 메서드가 다시 호출 될 것이다.
만약 그 메서드의 캐시들을 모두 지워고 싶다면 allEntries 속성을 사용하면 된다.

@CacheEvict(key = "#name", value = "wonwoo", allEntries = true)

다음은 @CachePut 어노테이션이다. 이 어노테이션은 매번 메서드를 호출 한다. 하지만 호출과 동시에 캐시에 보관하므로 보통 저장할 때 용이할 듯하다.

@CachePut(key = "#name", value = "wonwoo")
public String save(String name) {
  logger.info("cache save .. {}", name);
  return name;
}

사용법은 @Cacheable 과 비슷하다. 속성 또한 거의 비슷하다.
하지만 같은 메서드에 @CachePut@Cacheable를 선언하는 것은 보통 권장하지 않는 방식이다. 순서가 꼬여 원하지 않는 동작이 발생할 수도 있으므로 같은 메서드에서는 쓰지 않도록 주의하자.

Spring의 더욱 자세한 캐시 사용법은 문서를 살펴보길 바란다.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html

오늘 이상으로 Spring boot의 Cache 대해 알아봤다.