사용자의 닉네임을 검색하는 API에서 성능 개선을 위해 DB 인덱스, Redis 캐싱까지 적용해본 후에 이런저런 방법들을 더 고민해보다 지인에게 조언을 구해보았는데 단일 서버라면 Ehcache도 추천한다고 하셔서 한 번 적용해보려고 했습니다.
Ehcache
Spring에서 간단하게 사용할 수 있는 Java기반 오픈소스 캐시 라이브러리로, Redis나 Memcached와는 다르게 Spring 내부적으로 동작하여 캐싱 처리를 합니다.
따라서 Redis와 같이 별도의 서버를 띄워야 해서 생길 수 있는 네트워크 지연 혹은 단절과 같은 이슈에서 자유롭고, Spring 내부적으로 동작하기 때문에 속도가 더 빠르다는 장점이 있습니다.
그러나 서버에 저장이 되기 때문에 여러 개의 서버를 띄워서 사용하게 된다면 각 서버마다 저장된 캐시가 다르다는 점이 단점입니다.
Ehcache는 2.x 버전과 3 버전이 있는데, Spring Boot 3.0에서는 Ehcache2에 대한 지원이 제거되었기 때문에 3 버전을 사용했습니다.
그리고 Ehcache를 적용한 블로그 글들을 보면 대부분 xml 파일을 생성해 configuration을 관리해주는데, 저는 @Configuration을 통해 관리하고 싶었습니다.
그래서 Ehcache Document를 보고 Config 파일을 생성해주었으나 아래와 같은 오류가 발생했습니다.

userCache라는 이름의 캐시를 찾을 수 없다고 하는데
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {
@Transactional
@Cacheable(cacheNames="userCache", key="#nickname")
public UserListResponse searchUserNickname(String nickname) {
if (Strings.isBlank(nickname)) throw new InvalidRequestException("User nickname cannot be empty");
return UserListResponse.from(userRepository.findByNickname(nickname));
}
}
UserService 클래스를 보더라도, CacheConfig 파일을 보더라도 오타가 난 부분은 없습니다.
Ehcache는 JCache를 지원하며, JCache는 javax.cache API를 정의하는 사양(소프트웨어 구현이 아님)입니다. 이 사양은 자바 커뮤니티 프로세스에 따라 개발되었으며, 그 목적은 자바 애플리케이션에 대한 표준화된 캐싱 개념과 메커니즘을 제공하는 것입니다. 우리는 JCache를 사용하여 구현하면 됩니다.
JCache aka JSR-107를 통해 진행하였습니다.
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public JCacheCacheManager jCacheCacheManager() {
CachingProvider cachingProvider = Caching.getCachingProvider();
EhcacheCachingProvider ehcacheProvider = (EhcacheCachingProvider) cachingProvider;
org.ehcache.config.Configuration configuration = ConfigurationBuilder.newConfigurationBuilder().build();
CacheManager cacheManager = ehcacheProvider.getCacheManager(ehcacheProvider.getDefaultURI(), configuration);
CacheConfiguration<String, Object> cacheConfiguration =
CacheConfigurationBuilder
.newCacheConfigurationBuilder(String.class, Object.class, ResourcePoolsBuilder.heap(10))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(5)))
.build();
cacheManager.createCache("userCache",
Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration));
return new JCacheCacheManager(cacheManager);
}
}