Spring Boot 3 환경에서 @EnableCaching를 활용한 다양한 캐싱 전략
Spring Boot 3에서 @EnableCaching을 활용해 다양한 캐싱 전략을 적용하는 방법을 소개합니다. 실전 예제와 함께 메모리, Redis, Caffeine 등 다양한 캐시 구현체의 특징과 적용법을 쉽게 설명합니다.
개요
현대 웹 애플리케이션에서 성능 최적화는 매우 중요한 요소입니다. 그 중에서도 캐싱은 데이터베이스나 외부 API 호출을 줄이고, 응답 속도를 향상시키는 데 큰 역할을 합니다. Spring Boot 3에서는 @EnableCaching
어노테이션을 통해 손쉽게 캐싱 기능을 활성화할 수 있습니다. 본 포스트에서는 Spring Boot 3 환경에서 다양한 캐시 구현체(메모리, Redis, Caffeine 등)를 활용해 캐싱을 적용하는 방법을 상세히 다룹니다.
1. @EnableCaching 기본 개념
@EnableCaching
은 Spring에서 캐싱 기능을 활성화하는 어노테이션입니다. 이 어노테이션을 사용하면, @Cacheable
, @CachePut
, @CacheEvict
와 같은 캐시 관련 어노테이션을 사용할 수 있습니다.
// Application 클래스에 추가
@SpringBootApplication
@EnableCaching
class Application
이제 서비스 레이어에서 메서드 캐싱이 가능합니다.
2. 기본 메모리 캐시(SimpleCacheManager)
Spring Boot는 기본적으로 ConcurrentMap 기반의 SimpleCacheManager를 제공합니다. 별도의 설정 없이도 바로 사용할 수 있습니다.
@Service
class UserService {
@Cacheable("users")
fun getUserById(id: Long): User {
// DB 조회 로직
}
}
@Cacheable("users")
: users라는 이름의 캐시에 결과를 저장합니다.- 기본적으로 JVM 내 메모리를 사용하므로, 서버 재시작 시 캐시가 사라집니다.
3. Redis 캐시 적용하기
대규모 서비스에서는 분산 캐시가 필요합니다. Redis는 대표적인 인메모리 데이터 저장소로, Spring Boot와 쉽게 연동할 수 있습니다.
의존성 추가 (build.gradle.kts)
dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-redis")
}
설정 파일(application.yml)
spring:
cache:
type: redis
redis:
host: localhost
port: 6379
RedisCacheManager 빈 등록 (옵션)
@Configuration
class CacheConfig {
@Bean
fun cacheManager(redisConnectionFactory: RedisConnectionFactory): CacheManager {
return RedisCacheManager.builder(redisConnectionFactory).build()
}
}
- Redis 서버가 필요하며, 네트워크를 통해 여러 인스턴스가 캐시를 공유할 수 있습니다.
- TTL(Time To Live) 설정, 직렬화 포맷 등 세부 설정도 가능합니다.
4. Caffeine 캐시 적용하기
Caffeine은 JVM에서 동작하는 고성능 캐시 라이브러리입니다. Guava Cache의 후속작으로, LRU, LFU 등 다양한 정책을 지원합니다.
의존성 추가 (build.gradle.kts)
dependencies {
implementation("org.springframework.boot:spring-boot-starter-cache")
implementation("com.github.ben-manes.caffeine:caffeine")
}
설정 파일(application.yml)
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=500,expireAfterWrite=600s
- JVM 내에서 동작하지만, 다양한 만료 정책과 높은 성능을 제공합니다.
- 대규모 분산 캐시가 필요 없을 때 적합합니다.
5. 커스텀 캐시 구현 및 활용
특정 요구사항에 맞춰 커스텀 캐시를 직접 구현할 수도 있습니다. CacheManager
인터페이스를 구현하거나, 기존 캐시 구현체를 조합할 수 있습니다.
import org.springframework.cache.Cache
import org.springframework.cache.CacheManager
import org.springframework.cache.support.SimpleValueWrapper
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.concurrent.ConcurrentHashMap
// 단일 메모리 캐시 구현 예시
class SimpleCustomCache(private val name: String) : Cache {
private val store = ConcurrentHashMap<Any, Any?>()
override fun getName(): String = name
override fun getNativeCache(): Any = store
override fun get(key: Any): Cache.ValueWrapper? =
store[key]?.let { SimpleValueWrapper(it) }
override fun <T : Any?> get(key: Any, type: Class<T>): T? =
store[key]?.let { type.cast(it) }
override fun put(key: Any, value: Any?) {
store[key] = value
}
override fun evict(key: Any) {
store.remove(key)
}
override fun clear() {
store.clear()
}
}
// 커스텀 CacheManager 구현
class CustomCacheManager : CacheManager {
private val caches = listOf(SimpleCustomCache("customCache"))
override fun getCache(name: String): Cache? =
caches.find { it.name == name }
override fun getCacheNames(): Collection<String> =
caches.map { it.name }
}
@Configuration
class CacheConfig {
@Bean
fun cacheManager(): CacheManager = CustomCacheManager()
}
- 위 예시는
customCache
라는 이름의 단일 메모리 캐시를 제공합니다. - 실제 서비스에서는 여러 캐시 인스턴스를 동적으로 생성하거나, 만료 정책 등을 추가할 수 있습니다.
6. 실전 예제: 다양한 캐시 전략 조합하기
여러 캐시 전략을 조합해 사용하는 것도 가능합니다. 예를 들어, 일부 데이터는 Redis에, 일부는 Caffeine에 저장할 수 있습니다.
@Configuration
class MultiCacheConfig {
@Bean
fun cacheManager(redisConnectionFactory: RedisConnectionFactory): CacheManager {
val caffeineCacheManager = CaffeineCacheManager()
caffeineCacheManager.setCaffeine(Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(10, TimeUnit.MINUTES))
val redisCacheManager = RedisCacheManager.builder(redisConnectionFactory).build()
return CompositeCacheManager(caffeineCacheManager, redisCacheManager)
}
}
7. 캐시 무효화와 주의사항
@CacheEvict
로 캐시를 삭제하거나, 조건부로 무효화할 수 있습니다.- 캐시 데이터와 실제 데이터의 불일치(캐시 일관성 문제)에 주의해야 합니다.
- 적절한 만료 정책과 캐시 크기 설정이 중요합니다.
8. 캐시 테스트 및 모니터링
- Spring Boot Actuator를 활용해 캐시 상태를 모니터링할 수 있습니다.
- JMX, 로그, 별도 모니터링 툴과 연동해 캐시 적중률, 만료, 용량 등을 확인하세요.
참고 자료 및 레퍼런스
Spring Boot 3에서 다양한 캐시 전략을 조합하면, 서비스의 성능과 확장성을 크게 향상시킬 수 있습니다. 상황에 따라 적절한 캐시 구현체를 선택하고, 일관성 유지와 모니터링에도 신경쓰세요.