Contents
see ListSpring Cache 추상화 - Redis 캐시 적용
Spring의 캐시 추상화를 사용하면 비즈니스 로직을 변경하지 않고 다양한 캐시 구현체(Redis, Ehcache, Caffeine 등)를 적용할 수 있습니다.
언제 사용하나요?
- DB 조회 결과를 캐싱하여 응답 속도 개선
- 외부 API 호출 결과 캐싱
- 계산 비용이 높은 연산 결과 저장
- 세션 클러스터링, 분산 캐시 환경
의존성 추가 (Maven)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>Redis 설정 (application.yml)
spring:
redis:
host: localhost
port: 6379
password: yourpassword # 없으면 생략
timeout: 3000
cache:
type: redis
redis:
time-to-live: 3600000 # 1시간 (밀리초)캐시 설정 클래스
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 기본 TTL 1시간
.serializeKeysWith(
SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(
SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.withCacheConfiguration("users",
config.entryTtl(Duration.ofMinutes(30))) // users 캐시는 30분
.withCacheConfiguration("products",
config.entryTtl(Duration.ofHours(24))) // products는 24시간
.build();
}
}캐시 어노테이션 사용
@Service
public class UserService {
// 캐시에서 조회, 없으면 메서드 실행 후 캐시 저장
@Cacheable(value = "users", key = "#userId")
public User findById(Long userId) {
return userRepository.findById(userId).orElse(null);
}
// 메서드 실행 후 캐시 갱신
@CachePut(value = "users", key = "#user.id")
public User update(User user) {
return userRepository.save(user);
}
// 캐시 삭제
@CacheEvict(value = "users", key = "#userId")
public void delete(Long userId) {
userRepository.deleteById(userId);
}
// 해당 캐시 전체 삭제
@CacheEvict(value = "users", allEntries = true)
public void clearCache() {
// 캐시만 삭제
}
}어노테이션 상세 설명
| 어노테이션 | 설명 |
|---|---|
| @Cacheable | 캐시 조회 → 없으면 메서드 실행 → 결과 캐시 |
| @CachePut | 항상 메서드 실행 → 결과로 캐시 갱신 |
| @CacheEvict | 캐시 삭제 |
| @Caching | 여러 캐시 작업 조합 |
조건부 캐싱
// 조건이 true일 때만 캐싱
@Cacheable(value = "users", key = "#userId",
condition = "#userId > 0")
public User findById(Long userId) { ... }
// 결과가 null이 아닐 때만 캐싱
@Cacheable(value = "users", key = "#userId",
unless = "#result == null")
public User findById(Long userId) { ... }
// 복합 조건
@Cacheable(value = "users", key = "#userId",
condition = "#userId > 0",
unless = "#result.status == \047INACTIVE\047")
public User findById(Long userId) { ... }SpEL로 동적 키 생성
// 단순 파라미터
@Cacheable(value = "users", key = "#userId")
// 객체 필드
@Cacheable(value = "users", key = "#user.id")
// 여러 파라미터 조합
@Cacheable(value = "search", key = "#keyword + \047_\047 + #page")
// 메서드명 포함
@Cacheable(value = "data", key = "#root.methodName + #param")
// 전체 인자 해시
@Cacheable(value = "data", key = "#root.args")Redis CLI로 캐시 확인
# 모든 키 조회
redis-cli KEYS "*"
# 특정 패턴 조회
redis-cli KEYS "users::*"
# 값 조회
redis-cli GET "users::1"
# TTL 확인
redis-cli TTL "users::1"
# 캐시 삭제
redis-cli DEL "users::1"
# 패턴으로 삭제
redis-cli KEYS "users::*" | xargs redis-cli DEL주의사항
- 같은 클래스 내부 호출은 캐시가 적용되지 않음 (프록시 우회)
- 캐시 키로 사용되는 객체는 equals/hashCode 구현 필요
- Serializable 구현 필요 (또는 JSON 직렬화 설정)
- TTL 설정으로 메모리 관리 필수