Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/main/java/org/runnect/server/config/redis/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package org.runnect.server.config.redis;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@EnableCaching
@Configuration
Expand All @@ -29,5 +37,22 @@ public ListOperations<String, String> listOperations(
RedisTemplate<String, String> redisStringTemplate) {
return redisStringTemplate.opsForList();
}

// 공개 코스 조회 API(전체 페이지 수/마라톤/추천 목록) 응답 캐싱용.
// TTL을 짧게(1분) 두어, 코스가 추가/삭제돼도 오래된 응답이 너무 길게 남지 않도록 함.
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(1))
.disableCachingNullValues()
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));

return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(cacheConfiguration)
.build();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.runnect.server.user.exception.userException.NotFoundUserException;
import org.runnect.server.user.repository.UserRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
Expand All @@ -63,6 +64,8 @@ private void setMARATHON_PUBLIC_COURSE_IDS(String MARATHON_PUBLIC_COURSE_ID) {
.map(Long::parseLong).collect(Collectors.toList());
}

// 전체 유저에게 동일한 값이라 캐시 키에 별도 파라미터가 필요 없음
@Cacheable(value = "publicCourseTotalPageCount")
public GetPublicCourseTotalPageCountResponseDto getPublicCourseTotalPageCount() {
Long totalPublicCourseCount = publicCourseRepository.countBy();
if (totalPublicCourseCount % PAGE_SIZE != 0) {
Expand All @@ -71,6 +74,10 @@ public GetPublicCourseTotalPageCountResponseDto getPublicCourseTotalPageCount()
return GetPublicCourseTotalPageCountResponseDto.of(totalPublicCourseCount / PAGE_SIZE);
}

// 응답에 유저별 스크랩 여부(isScrap)가 섞여있어 userId를 캐시 키에 포함함
// (그만큼 유저별로 캐시가 나뉘어서, 여러 유저가 섞인 실트래픽에서는 히트율이
// 이번 부하테스트(단일 유저 반복 호출)만큼 극적이진 않을 수 있음)
@Cacheable(value = "marathonPublicCourse", key = "#userId")
public GetMarathonPublicCourseResponseDto getMarathonPublicCourse(Long userId) {
//1. 받은 userId가 유저가 존재하는지 확인
RunnectUser user = userRepository.findById(userId)
Expand Down Expand Up @@ -150,6 +157,8 @@ public SearchPublicCourseResponseDto searchPublicCourse(Long userId, String keyw

}

// marathonPublicCourse와 동일한 이유로 userId를 캐시 키에 포함
@Cacheable(value = "recommendPublicCourse", key = "#userId + '_' + #pageNo + '_' + #sort")
public RecommendPublicCourseResponseDto recommendPublicCourse(Long userId, Integer pageNo, String sort) {
//1. 받은 userId가 유저가 존재하는지 확인
RunnectUser user = userRepository.findById(userId)
Expand Down
Loading