자바캐시표준api (1)

💻 Programming

[5분 코딩] Spring4 + JCache (feat. Ehcache)

자바에서 초간단 캐시 사용하기

 

안녕하세요, 오늘은 캐시 관련 내용을 들고 왔습니다.

회사에서 관리중인 관리자 웹앱에서 특정 기능을 하는 버튼을 광클릭 하시는 전직 프로게이머 같은 분이 계셔서 자꾸 에러가 나길래 개선 방안을 고민하다가 한 번 클릭해서 정상적으로 실행이 되었다면 1분 동안 재처리를 하지 않도록 캐시를 이용해보면 좋겠다는 생각이 들었습니다. 즉, 특정 API를 호출할 때 캐시에 관련 데이터를 저장해놓고 1분 내에 동일한 내용으로 API 요청이 들어올 경우 에러 메시지를 출력하고 해당 기능을 실행하지 않도록 구현하면 되겠다 싶었죠. 그리고 예전에 카카오톡과 연동하여 G사의 매니저들이 사용하는 챗봇을 만든 적이 있는데 이 때 Ehcache 를 사용해본적이 있어서 이번에도 동일하게 Ehcache를 사용하면 되겠다 싶었습니다.

우선 현재 스펙은 백엔드 SpringFramework 4 기반이고 프론트엔드는 부트스트랩을 이용한 구식 앱입니다. (스프링 부트로 전환하고 싶지만 더 중요한 업무들이 많아 생각만 하고 있다는...ㅜㅜ) AWS의 Tomcat 8 플랫폼을 사용하여 서비스 중이죠.

 

Ehcache 에 대해서 오랜만에 구글링을 해보니 Jcache를 지원한다고 하여 표준인 Jcache를 사용할 수 있도록 구현해보았습니다. Jcache는 JSR-107 표준을 따르는 자바에서 제공하는 인터페이스입니다. 이 표준 인터페이스를 구현하고 있는 구현체들에는 Ehcache 뿐 아니라 Hazelcast, Oracle Coherence, Infinispan 등도 있습니다.

 

자바에서 간단하게 캐싱하기 (Ehcache를 이용한 Jcache 사용법)

우선 pom.xml 파일에 아래 두 개의 의존성을 추가합니다.

        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>3.9.0</version>
        </dependency>

        <dependency>
            <groupId>javax.cache</groupId>
            <artifactId>cache-api</artifactId>
            <version>1.1.1</version>
        </dependency>

ehcache, jcache 모두 포스팅 시점 최신버전으로 추가했습니다.

 

그리고 CacheConfig 클래스를 만들어서 아래처럼 작성했습니다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
import javax.cache.spi.CachingProvider;


@Configuration
public class CacheConfig {

    @Bean
    public Cache<String, String> syncCache() {

        CachingProvider provider = Caching.getCachingProvider();
        CacheManager cacheManager = provider.getCacheManager();
        MutableConfiguration<String, String> configuration =
                new MutableConfiguration<String, String>()
                        .setTypes(String.class, String.class)
                        .setStoreByValue(false)
                        .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE));
        return cacheManager.createCache("syncCache", configuration);
    }
}

이 설정 부분이 가장 중요한 부분일텐데요, 특히 캐시만료정책(expiry policy) 설정하는 부분은 잘 확인하시는 것을 권장드립니다.

jcache에서 제공하는 만료정책은 5가지가 있습니다.

 

- AccessExpiryPolicy

- CreatedExpiryPolicy

- EternalExpiryPolicy

- ModifiedExpiryPolicy

- TouchedExpiryPolicy

 

각각 특정 만료정책을 위해 기본적으로 제공하는 것들입니다. 예를들어 EternalExpiryPolicy는 캐싱된 데이터가 만료되지 않는 무기한 저장하는 정책이죠. 자세한 내용은 구글링 해보시면 많은 자료들이 있으니 참고하시기 바랍니다.

 

여기서는 CreatedExpiryPolicy를 사용해서 Duration으로 1분을 주었습니다. 기본적으로 많이 사용될만한 시간들이 static하게 정의되어 있으나 정의되어있지 않은 다른 시간을 사용하고자 할 경우 new Duration(TimeUnit timeUnit, long durationAmount) 을 이용하여 커스텀하게 정의해줄 수도 있습니다.

 

이제 저 캐시를 사용할 CacheService라는 서비스 클래스를 하나 만들겠습니다.

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import javax.cache.Cache;

@Service
@RequiredArgsConstructor
public class CacheService {

    private final Cache<String, String> syncCache;

    public boolean isCached(String key) {
        return syncCache.containsKey(key);
    }

    public void doCache(String key, String value) {
        syncCache.put(key, value);
    }

    public boolean remove(String key) {
        return syncCache.remove(key);
    }

}

 

이제 해당 서비스를 가져다 쓰기만 하면 됩니다.

꼼꼼하신 분들은 좀 이상한게 눈에 띌 수도 있습니다. 

ehcache 패키지에서 제공하는 클래스를 사용한 부분이 하나도 없다는 것이죠.

그럼 pom.xml 파일에서 ehcache 의존성을 제거 해볼까요??

그럼 아래 에러가 발생하는 것을 확인하실 수 있습니다.

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.cache.Cache]: Factory method 'syncCache' threw exception; nested exception is javax.cache.CacheException: No CachingProviders have been configured
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
	... 67 more
Caused by: javax.cache.CacheException: No CachingProviders have been configured
	at javax.cache.Caching$CachingProviderRegistry.getCachingProvider(Caching.java:391)
	at javax.cache.Caching$CachingProviderRegistry.getCachingProvider(Caching.java:361)
	at javax.cache.Caching.getCachingProvider(Caching.java:151)

 

즉, 실제로 사용하는 API는 javax.cache 패키지 내에 정의되어있는 것이지만 내부적으로 ehcache에서 구현한 구현체로 실행된다는 결론을 내릴 수 있습니다.

 

어떤가요? 정말 쉽게 구현이 가능하죠?

 

이상으로 ehcache를 활용한 jcache 사용법에 대한 포스팅을 마칩니다.

 

 

 

참고자료

- www.ehcache.org/

- www.ehcache.org/documentation/3.3/107.html

- www.baeldung.com/jcache