[Spring][Redis] Redis 캐싱 기능을 활용한 조회 성능 개선 (CacheManager)
데이터 캐싱의 개념에 대해 알아보고, Redis의 캐싱 기능을 활용하여조회 성능을 개선하는 예제를 포함한 포스팅입니다.✅ 캐싱이란?캐시(Cache)는 데이터나 값을 저장하는 임시 저장소로, 데이터
zapzook.tistory.com
이전 포스팅에 이어서, 본 포스팅에선 CacheManager를 활용한 방식이 아닌,
RedisTemplate을 활용한 캐싱을 적용해 볼 것이다.
RedisTemplate은 Redis 데이터 구조에 접근하고, CRUD 작업을 수행할 수 있도록 도와주는 고수준의 추상화된 템플릿이다.
✅ CacheManager VS RedisTemplate
📌 CacheManager을 활용한 캐싱
장점
- 간결한 코드:
- @Cacheable, @CachePut, @CacheEvict 등의 애너테이션을 활용하여 간결하게 캐싱 로직을 적용 가능
- 비즈니스 로직과 캐싱 로직이 분리되어 코드가 깔끔
- 일관성 및 유지보수성:
- 캐시 관리가 중앙 집중화되어 일관된 캐시 정책을 유지할 수 있음
- 캐시 설정과 관리가 표준화
- 캐싱 로직이 표준화되어 유지보수가 용이
단점
- 제한된 유연성:
- 세부적인 캐싱 로직을 커스터마이즈하기 어려움
- 복잡한 캐시 키 관리나 조건부 캐싱 로직을 구현하기 어려움
- 추상화된 접근:
- 캐시의 동작 방식이 추상화되어 있어, 캐시 미스 시의 동작 등을 세밀하게 제어하기 어려움
- 기본 설정 외에 세부적인 캐싱 전략을 적용하려면 추가적인 설정이 필요
요약
간단한 캐싱 로직을 구현하고자 할 때, 캐시 설정과 관리의 일관성을 유지하고자 할 때 사용하면 좋음
📌 RedisTemplate을 활용한 캐싱
장점
- 유연성 및 제어력:
- 직접적인 Redis 접근을 통해 키 생성, 만료 시간 설정, 직렬화 방법 등 세부적인 제어가 가능
- 캐시 데이터를 개별적으로 관리할 수 있어 복잡한 캐시 로직의 구현 가능
- 직관적인 키 관리:
- 개발자가 직접 키를 생성하고 관리할 수 있으므로, 키의 형식을 세밀하게 제어할 수 있음
- 캐시 키를 커스터마이즈하기 용이
- 특정 조건 처리 용이:
- 특정 조건에 따라 캐시 데이터를 삽입하거나 삭제하는 등의 복잡한 로직을 쉽게 구현할 수 있음
단점
- 반복되는 코드:
- 캐시를 설정하고 검증하는 코드가 반복적으로 작성될 수 있음
- 캐싱 로직이 비즈니스 로직과 섞여 코드가 복잡해질 수 있음
- 표준화 부족:
- 코드마다 캐싱 방식이 다를 수 있어 일관성이 떨어질 수 있음
- 유지보수 시 각기 다른 캐싱 로직을 이해하고 관리해야 함
- 추가적인 오류 가능성:
- 잘못된 키 관리나 TTL 설정으로 인해 버그가 발생할 가능성이 있음
요약
복잡하고 디테일한 캐싱 로직을 구현해야 하며, 세부적인 캐싱 제어가 필요한 경우 사용하면 좋음
✅ 예제
📌 RedisConfig
@EnableRedisRepositories // redis 활성화
@EnableCaching
@Configuration
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
// 연결정보
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
// 직렬화 / 역직렬화
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
@Bean
public RedisTemplate<String, Page<ItemResponse>> listRedisTemplate() {
RedisTemplate<String, Page<ItemResponse>> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
}
Config에 원하는 데이터 형식에 맞게 RedisTemplate을 설정해준다.
📌 서비스 레이어에 캐싱 적용
public Page<ItemResponse> pageItem(SearchRequest searchRequest, Pageable pageable) {
String key = searchRequest.toString() + pageable;
Page<ItemResponse> itemResponseList = listRedisTemplate.opsForValue().get(key);
if (itemResponseList != null) {
return itemResponseList;
}
Page<ItemResponse> itemList = itemRepository.pageItem(searchRequest, pageable);
listRedisTemplate.opsForValue().set(key, itemList, 5, TimeUnit.SECONDS);
return itemList;
}
위 코드처럼 RedisTemplate의 opsForValue 메서드를 통해 캐싱 로직을 서비스 레이어에 구현할 수 있다.
CacheManager를 활용한 방식에 비해 코드가 다소 복잡해지지만, 캐시 키 값, TTL, 캐싱 로직을 입맛에 맞게
세밀하게 설정할 수 있다는 장점이 있다.
✅ 결론
필자의 경우엔 프로젝트의 여러 서비스 API에 캐싱이 적용되어 있는데,
CacheManager를 통해 캐싱 로직을 표준화 하여 사용하다가 별도의 TTL 값과 키 값이 필요한
서비스 API가 생겨서 따로 RedisTemplate을 활용해 캐싱을 적용하였다.
이처럼 요구사항에 맞게 필요에 따라 두 방식중 하나를 선택하여 캐싱을 적용해보면 좋을 것 같다.
'TIL' 카테고리의 다른 글
[AWS ECS와 Fargate를 활용한 Auto-Scalable 아키텍처 구현] Intro (1) | 2024.04.01 |
---|---|
[Spring][Redis] Redis 캐싱 기능을 활용한 조회 성능 개선 (CacheManager) (0) | 2024.03.28 |
[Spring][DB] 데이터의 순서 컬럼 정렬 전략 (0) | 2024.03.22 |
JWT의 무상태성 (+쿼리 최적화) (0) | 2024.03.08 |
[Spring] CRUD 기능 구현 중 알게 된 Dto를 사용해야 하는 이유 (0) | 2024.02.26 |