포스트

Caffeine

I. SpringBoot Cache

어플리케이션은 기능 구현을 위해 다양한 DataSource를 사용한다. 자주 사용되는 기능일수록 특정 DataSource에 대한 접근이 늘어나고, 그에 따른 부담이 증가한다. Cache는 어플리케이션이 특정 DataSource에 접근하지 않고, 데이터가 임시로 저장된 공간에 접근해 빠르게 데이터를 반환할 수 있도록 한다. SpringBoot는 이러한 Cache를 추상화한 Annotation을 통해 쉽게 사용할 수 있도록 지원한다.

1. QuickStart

1) Dependency

1
2
3
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-cache'
}

2) Enable Caching

1
2
3
4
5
6
7
@SpringBootApplication
@EnableCaching
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

@EnableCaching을 통해 Spring Cache를 활성화한다. 이를 통해 @Cacheable, @CachePut, @CacheEvict 등의 Cache 관련 Annotation을 사용할 수 있다. @EnableCaching을 적용하는 것만으로 Cache를 사용할 수는 없고, CacheManager 구현체를 @Bean으로 등록해야한다.

3) Config CacheManager

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@EnableCaching
public class LocalCacheConfig {

	@Bean
	public CacheManager cacheManager() {

		SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
		simpleCacheManager.setCaches(/* ${Collection<? extends Cache> caches} */);
		return simpleCacheManager;
	}
}

simpleCacheManager.setCaches()를 통해 사용하고자 하는 CacheProvider(Eh, Guava, Caffeine 등)를 지정할 수 있다. CacheProvider를 지정하지 않으면, ConcurrentMapCache를 사용한다. ConcurrentMapCache는 null 허용 여부, concurrentMap 지정 여부에 따라 생성자가 다르므로, 필요에 따라 직접 생성해 사용할 수도 있다.

4) @Cacheable(value="cacheName", key="cacheKey", condition="#parameter=True")

@Cacheable은 메소드 호출 결과를 캐시에 저장한다. 또한 동일한 캐시가 존재할 경우, 저장된 데이터를 반환한다. value, key, condition 설정에 따라 저장 조건을 다양하게 가져갈 수 있다. 또한 SpringExpressionLanguage를 통한 동적인 핸들링도 가능하다.

  1. value: 캐시 이름을 지정한다. value={"cacheName1", "cacheName2"}와 같은 형태로 두 종류의 캐시에 한번에 저장하는 것도 가능하다.
  2. key: 캐시 key를 지정한다.
  3. condition: 특정 조건을 만족할 경우에만 저장 혹은 조회되도록 한다.

5) @CachePut(value="cacheName", key="cacheKey", condition="#parameter=True")

@CachePut은 기본적으로 @Cacheable과 사용 방식이 동일하나, Cache에 저장된 데이터를 조회하지 않고 실행 결과를 무조건 새로 저장한다.

6) @CacheEvict(value="cacheName", allEntries="True")

Cache에 메소드 실행 결과를 저장하다보면, 자연스럽게 Cache의 데이터양이 늘어나고 이는 곧 저장 공간의 낭비와 캐시 성능 저하로 이어진다. @CacheEvict는 이를 방지하기 위해, 호출 시 조건에 따라 저장된 캐시를 삭제한다.

II. Caffeine

Caffeine은 신뢰도가 높은 CacheProvider다. 주요 특징은 다음과 같다.

  • Cache 항목 자동 로딩
  • Cache Frequency, Recency에 따른 size-based-eviction
  • Cache 만료에 대한 추상화 제공

1. QuickStart

1) Dependency

1
2
3
4
5
dependencies {
    // Cache
    implementation 'org.springframework.boot:spring-boot-starter-cache'
    implementation "com.github.ben-manes.caffeine:caffeine:3.1.8"
}

2) Enable Caching

1
2
3
4
5
6
7
@SpringBootApplication
@EnableCaching
public class Application {
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

4) CacheEnum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter
public enum CacheType {
	TEST("test", 60 * 60 * 24 * 7, 1);

	private final String cacheName;
	private final int secsToExpireAfterWrite;
	private final int entryMaxSize;

	CacheType(String cacheName, int secsToExpireAfterWrite, int entryMaxSize) {
		this.cacheName = cacheName;
		this.secsToExpireAfterWrite = secsToExpireAfterWrite;
		this.entryMaxSize = entryMaxSize;
	}
}

사용될 Cache 형태를 Enum으로 관리함으로써, 사용될 캐시 관리를 원활하게 할 수 있다.

3) Config CacheManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
@EnableCaching
public class CacheConfig {

	@Bean
	public CacheManager cacheManager() {
		List<CaffeineCache> caches = Arrays.stream(CacheType.values())
      .map(cacheType ->
        new CaffeineCache(
          cacheType.getCacheName(),
          Caffeine.newBuilder()
            .recordStats()
            .expireAfterWrite(cacheType.getSecsToExpireAfterWrite(), TimeUnit.SECONDS)
            .maximumSize(cacheType.getEntryMaxSize())
            .build()))
      .toList();

		SimpleCacheManager cacheManager = new SimpleCacheManager();
		cacheManager.setCaches(caches);
		return cacheManager;
	}
}

stream을 통해 Enum에 정의된 Cache들을 등록한다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.