지난 글에서는 인메모리 캐싱 처리에 대해 정리했었다.
이번에는 팀원들과 함께 Redis를 활용한 캐싱 전략을 도입할 기능에 대해 논의했다.
그 결과, 도서 목록에서의 인기 검색어(Top 5) 기능에 Redis를 적용하기로 결정했다.
실무에서도 일반적으로 모든 검색 키워드 데이터를 전부 집계하여 인기 검색어를 보여주기보다는,
TTL(Time To Live)을 설정하여 최근 하루 기준의 데이터만을 집계하는 방식이 자주 사용된다.
이를 반영하여, 우리도 하루 동안 사용자가 많이 검색한 도서 키워드를 기준으로 인기 검색어를 선정하고,
Redis에 24시간 TTL을 적용해 매일 자동으로 초기화되도록 구현하기로 했다.
이를 통해 매일 최신의 인기 도서 키워드를 캐시로 빠르게 제공할 수 있게 되었다.
✅ QueryDSL 쿼리 설명
해당 쿼리문은 QueryDSL을 사용해 가장 많이 검색된 순으로 5개를 조회하고 있다.
public List<Top5KeywordResponseDto> findTop5Keywords() {
QKeyword keyword = QKeyword.keyword1;
List<Top5KeywordResponseDto> result = jpaQueryFactory
.select(Projections.constructor(
Top5KeywordResponseDto.class,
keyword.keyword,
count
))
.from(keyword)
.groupBy(keyword.keyword)
.orderBy(count.desc())
.limit(5)
.fetch();
return result;
}
코드를 간단하게 설명하자면 키워드별 검색 횟수를 집계해 많이 검색된 순으로 정렬한 뒤, 상위 5개의 인기 키워드를 조회하는 쿼리이다.
✅ 도입 배경
하루 동안 많이 검색된 키워드 Top 5를 보여주되, 매번 DB를 조회하지 않도록 하여 성능을 최적화할 필요가 있었다.
TTL을 통해 하루마다 캐시가 자동 만료되도록 구성함으로써 데이터의 최신성과 성능을 동시에 확보할 수 있었다.
✅ Redis 설정 (TTL 적용)
1. RedisCacheConfig 설정 코드
@Configuration
public class RedisCacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 기본 TTL: 6시간
RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(6))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()));
// 캐시 이름별 개별 TTL 설정
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
configMap.put("top5Keywords", defaultConfig.entryTtl(Duration.ofDays(1))); // 인기 검색어 캐시는 1일 유지
configMap.put("bookSearchCache", defaultConfig.entryTtl(Duration.ofHours(12))); // 도서 검색 캐시는 12시간
configMap.put("likes", defaultConfig.entryTtl(Duration.ofHours(6))); // 좋아요 목록은 6시간 유지
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(defaultConfig)
.withInitialCacheConfigurations(configMap)
.build();
}
}
- Redis를 캐시 저장소로 쓰기 위한 설정이다.
- @Cacheable에서 사용하는 캐시 이름별로 TTL(유효 시간)을 따로 정할 수 있게 구성하였고, 위 내용과 데이터를 캐시로 저장하면서 일정 시간이 지나면 자동으로 삭제되도록 설정했다.
2. @Cacheable 사용한 서비스 메서드
@Service
@RequiredArgsConstructor
public class KeywordService {
private final KeywordRepository keywordRepository;
// 캐싱 X
@Transactional(readOnly = true)
public List<Top5KeywordResponseDto> getFavoriteKeyword() {
return keywordRepository.findTop5Keywords();
}
// 캐싱 O
@Cacheable(value = "top5Keywords")
@Transactional(readOnly = true)
public List<Top5KeywordResponseDto> getFavoriteKeyword2() {
return keywordRepository.findTop5Keywords();
}
}
- top5Keywords라는 이름으로 캐시 저장소에 데이터를 저장.
- 이 메서드는 처음 실행된 후의 결과를 Redis에 저장하였고, 같은 요청이 들어오면 DB 조회 없이 캐시된 값을 반환하도록 하였다.
✅ Redis 캐싱 적용 효과
실시간 트래픽이 몰릴 수 있는 인기 키워드 API에 대해 Redis를 활용한 캐싱 전략을 적용하였고, 성능 차이를 확인하기 위해 테스트를 해보았다.
테스트 시나리오
- 사용자 수: 500명
- 요청 패턴: 10초 동안 점진적으로 접속
- 호출 API: 인기 검색어 TOP 5 조회 API
- 요청 횟수: 사용자당 3회 반복 호출 (총 1,500회)
- 데이터 조건: DB에 저장된 검색어 약 5만 개
캐싱 전
캐싱 후
🔥 Redis + TTL 캐싱 적용 전에는 요청마다 DB에서 Group By, Count 쿼리를 수행해 과부하로 인해 에러율 25%, 처리량 4.8 req/sec, 총 5분 15초가 소요되었지만, 적용 후에는 첫 요청만 DB에서 조회되고 이후에는 Redis를 통해 응답되어 에러율 0%, 처리량 150.0 req/sec로 약 31배 개선되며 전체 처리 시간이 10초로 단축되는 차이점을 볼 수 있다.
✅ Redis + TTL 캐싱 전략 결론
인기 검색어(Top 5) 기능에 Redis와 TTL 전략을 도입함으로써, 매 요청마다 DB에 부담을 주던 집계 연산을 줄이고, 빠른 응답성과 높은 안정성을 확보할 수 있었다.
특히 TTL(Time To Live) 설정을 통해 캐시의 수명을 자동으로 관리할 수 있어, 더 안정적이고 효율적인 캐시 운영 모두 충족하는 구조를 만들었다.
'Spring' 카테고리의 다른 글
Spring - 엔티티를 직접 Response로 반환할 경우 (1) | 2025.06.20 |
---|---|
Spring Boot + Redis + Docker 연결 문제 트러블 슈팅 (1) | 2025.06.20 |
15만권 도서 검색 서비스에 캐시 적용하기 (4) | 2025.06.09 |
EC2 인바운드 규칙 미설정으로 인한 접속 불가 문제 (3) | 2025.05.27 |
실전 프로젝트 - 도서 관리 앱 만들기 (4) | 2025.05.20 |