Spring Cache Abstraction
Spring 에서는 Cache 를 애플리케이션에 쉽게 적용할 수 있는 Cache Abstraction 을 지원합니다.
주요 특징
캐시 추상화 계층
추상화를 통해 다양한 Cache 구현체(Ehcache, Redis, Hazelcast)를 적용할 수 있게 해주어 어떤 구현체든 일관된 API를 통해 사용하고, 손쉽게 구현체를 변경할 수 있도록 해줍니다.
Spring 에서는 이를 CacheManager 인터페이스를 통해 지원하며, CacheManager 는 캐시 생성, 접근, 제거 등의 기능을 일관된 형태로 제공하고, 캐시에 관련된 설정(만료시간, 최대 크기) 등을 손쉽게 할 수 있도록 지원합니다.
어노테이션 기반의 선언적 캐싱 적용
Spring cache abstraction 에서는 어노테이션 기반의 AOP 를 통해 캐싱 기능을 적용합니다.
캐싱 관련 어노테이션을 적용하면, AOP 를 통해 프록시 객체가 생성되고, 내부에서 CacheInterceptor 를 통해 캐싱 로직을 적용하게 됩니다.
다양한 설정을 통한 캐시 설정
key 생성, cache 조건, 만료 시간 등의 설정을 SpEL 혹은 CacheManager 에서 적용하여 유연하게 사용할 수 있도록 지원합니다.
간단한 적용 예시
아래의 예시에서는 캐시 구현체를 Redis로 사용하였습니다.
Redis 설정
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
}
CacheManager 설정
@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory cf) {
// 구현체로 redisCacheManager 를 선택하여 bean 등록
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
// cache 만료 시간 설정 - 생성 혹은 업데이트
.entryTtl(Duration.ofMinutes(60))
// null value 캐시 지정 안하도록
.disableCachingNullValues()
// key 직렬화 클래스 지정
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// value 직렬화 클래스 지정
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(cf)
.cacheDefaults(redisCacheConfiguration)
.build();
}
}
Service 에서 사용
사용되는 주요 어노테이션은 다음과 같습니다.
| 이름 | 설명 | 주요 파라미터 |
| @Cacheable | 지정된 Key에 대해 캐시에 메서드 결과가 없다면 캐시에 저장하고, 캐시에 메서드 결과가 있다면 캐시된 결과를 반환하도록 합니다. |
cacheName - 캐시 이름을 지정합니다. 캐시 이름이 다르다면, 서로 다른 캐시에 저장됩니다. key - 캐시 key 값을 파라미터에서 지정합니다. SpEL 을 사용하여 동적으로 설정할 수 있습니다. (파라미터의 메서드 호출 값 등) cacheManager - 사용할 cacheManager 를 지정합니다. condition - 캐시에 저장할 파라미터의 조건을 지정합니다. SpEL 을 사용하여 동적으로 조건을 지정할 수 있습니다. unless - 캐시에 저장하지 않을 파라미터의 조건을 지정합니다. sync - 동기화 모드를 설정합니다. true 로 지정한다면, 여러 스레드에서 같은 key 에 대해 해당 메서드가 실행되지 않도록 합니다. |
| @CachePut | 메서드 결과를 캐시에 저장합니다. 이 때, 반드시 캐시에 저장하며, 캐시 값을 갱신하는 목적으로 사용합니다. |
cacheName - 캐시 이름을 지정합니다. 캐시 이름이 다르다면, 서로 다른 캐시에 저장됩니다. key - 캐시 key 값을 파라미터에서 지정합니다. SpEL 을 사용하여 동적으로 생성할 수 있습니다. cacheManager - 사용할 cacheManager 를 지정합니다. condition - 캐시에 저장할 파라미터의 조건을 지정합니다. SpEL 을 사용하여 동적으로 조건을 지정할 수 있습니다. unless - 캐시에 저장하지 않을 파라미터의 조건을 지정합니다. |
| @CacheEvict | 캐시에서 지정한 key 값 혹은 모든 항목을 제거합니다. |
cacheName - 캐시 이름을 지정합니다. 캐시 이름이 다르다면, 서로 다른 캐시에 저장됩니다. key - 캐시 key 값을 파라미터에서 지정합니다. SpEL 을 사용하여 동적으로 생성할 수 있습니다. allEntries - 해당 캐시의 모든 항목을 제거합니다. beforeInvocation - 메서드 호출 전 캐시 항목을 제거합니다. 기본값은 false 이며, 메서드 호출 후 캐시 항목을 제거합니다. |
@Transactional
@RequiredArgsConstructor
@Service
public class PostServiceImpl implements PostService {
private final PostRepository postRepository;
@Override
public PostResponseDto createPost(PostCreateRequestDto dto) {
Post post = dto.toEntity();
post = postRepository.save(post);
return PostResponseDto.fromEntity(post);
}
@Override
@Transactional(readOnly = true)
@Cacheable(cacheNames = CacheKey.POST, key = "#id", cacheManager = "cacheManager")
public PostResponseDto getPost(Long id) {
Post post = postRepository.findById(id)
.orElseThrow(EntityNotFoundException::new);
return PostResponseDto.fromEntity(post);
}
@Override
@CachePut(cacheNames = CacheKey.POST, key = "#id", cacheManager = "cacheManager")
public void updatePost(Long id, PostUpdateRequestDto dto) {
Post post = postRepository.findById(id)
.orElseThrow(EntityNotFoundException::new);
post.updateTitle(dto.title());
post.updateContent(dto.content());
postRepository.save(post);
}
@Override
@CacheEvict(cacheNames = CacheKey.POST, key = "#id", cacheManager = "cacheManager")
public void deletePost(Long id) {
postRepository.deleteById(id);
}
'스프링' 카테고리의 다른 글
| 트랜잭션 추상화와 @Transactional (0) | 2024.07.15 |
|---|---|
| 스프링이란? (0) | 2024.07.12 |