diff --git a/src/main/java/com/example/Jinus/config/RedisCacheManager.java b/src/main/java/com/example/Jinus/config/RedisCacheManager.java index c01fc88..7fa82d9 100644 --- a/src/main/java/com/example/Jinus/config/RedisCacheManager.java +++ b/src/main/java/com/example/Jinus/config/RedisCacheManager.java @@ -21,37 +21,53 @@ public class RedisCacheManager { public CacheManager contentCacheManager(RedisConnectionFactory cf) { Map cacheConfigurations = new HashMap<>(); - // 1. 식당 리스트 캐시 (불변 데이터, 7일 유지) + // 1. 식당 리스트 캐시 (불변 데이터, 30일 유지) cacheConfigurations.put("cafeteriaList", RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // Key Serializer .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) // Value Serializer - .entryTtl(Duration.ofDays(7)) // 7일 유지 + .entryTtl(Duration.ofDays(30)) .disableCachingNullValues()); // null 캐싱 방지 // 2. 식단 데이터 캐시 (하루 단위로 갱신) - cacheConfigurations.put("dietList", +// cacheConfigurations.put("dietList", +// RedisCacheConfiguration.defaultCacheConfig() +// .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // Key Serializer +// .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) // Value Serializer +// .entryTtl(Duration.ofHours(12)) +// .disableCachingNullValues()); + + // 3. 캠퍼스 이름 캐시 (불변 데이터, 30일 유지) + cacheConfigurations.put("campusName", RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // Key Serializer .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) // Value Serializer - .entryTtl(Duration.ofHours(12)) // 12시간 유지 - .disableCachingNullValues()); + .entryTtl(Duration.ofDays(30)) + .disableCachingNullValues()); // null 캐싱 방지 - // 3. 캠퍼스 이름별 캠퍼스 id 캐시 (불변 데이터, 7일 유지) - cacheConfigurations.put("campusName", + // 3. 식당 id 캐시 (불변 데이터, 30일 유지) + cacheConfigurations.put("cafeteriaId", RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // Key Serializer .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) // Value Serializer - .entryTtl(Duration.ofDays(7)) // 7일 유지 + .entryTtl(Duration.ofDays(30)) .disableCachingNullValues()); // null 캐싱 방지 - // 3. 식당별 식당 id 캐시 (불변 데이터, 7일 유지) - cacheConfigurations.put("cafeteriaId", + // 4. 캠퍼스 id 캐시 (불변 데이터, 30일 유지) + cacheConfigurations.put("campusId", RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // Key Serializer .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) // Value Serializer - .entryTtl(Duration.ofDays(7)) // 7일 유지 + .entryTtl(Duration.ofDays(30)) + .disableCachingNullValues()); // null 캐싱 방지 + + // 5. 식당 url 캐시 (불변 데이터, 30일 유지) + cacheConfigurations.put("cafeteriaUrl", + RedisCacheConfiguration.defaultCacheConfig() + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // Key Serializer + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) // Value Serializer + .entryTtl(Duration.ofDays(30)) .disableCachingNullValues()); // null 캐싱 방지 @@ -60,4 +76,5 @@ public CacheManager contentCacheManager(RedisConnectionFactory cf) { .withInitialCacheConfigurations(cacheConfigurations) // 캐시별 설정 적용 .build(); } + } diff --git a/src/main/java/com/example/Jinus/config/RestTemplateConfig.java b/src/main/java/com/example/Jinus/config/RestTemplateConfig.java deleted file mode 100644 index b7046fc..0000000 --- a/src/main/java/com/example/Jinus/config/RestTemplateConfig.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.example.Jinus.config; - -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.BufferingClientHttpRequestFactory; -import org.springframework.http.client.SimpleClientHttpRequestFactory; -import org.springframework.http.converter.StringHttpMessageConverter; -import org.springframework.web.client.RestTemplate; - -import java.nio.charset.StandardCharsets; - -@Configuration -public class RestTemplateConfig { - @Bean - public RestTemplate restTemplate(RestTemplateBuilder builder) { - return builder - .requestFactory( - () -> new BufferingClientHttpRequestFactory( - new SimpleClientHttpRequestFactory() - ) - ).additionalMessageConverters( - new StringHttpMessageConverter( - StandardCharsets.UTF_8 - ) - ).build(); - } -} diff --git a/src/main/java/com/example/Jinus/controller/v2/DietControllerV2.java b/src/main/java/com/example/Jinus/controller/v2/DietControllerV2.java index 4fcdd3f..a4e95fa 100644 --- a/src/main/java/com/example/Jinus/controller/v2/DietControllerV2.java +++ b/src/main/java/com/example/Jinus/controller/v2/DietControllerV2.java @@ -2,9 +2,7 @@ import com.example.Jinus.dto.request.RequestDto; import com.example.Jinus.service.v2.cafeteria.DietServiceV2; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -16,9 +14,6 @@ public class DietControllerV2 { private final DietServiceV2 dietServiceV2; - @Autowired - private ObjectMapper objectMapper; - @PostMapping("/v2/dish") public String handleRequest(@RequestBody RequestDto requestDto) { return dietServiceV2.requestHandler(requestDto); diff --git a/src/main/java/com/example/Jinus/service/v2/cafeteria/CacheServiceV2.java b/src/main/java/com/example/Jinus/service/v2/cafeteria/CacheServiceV2.java index 2353549..6eb741b 100644 --- a/src/main/java/com/example/Jinus/service/v2/cafeteria/CacheServiceV2.java +++ b/src/main/java/com/example/Jinus/service/v2/cafeteria/CacheServiceV2.java @@ -1,40 +1,65 @@ package com.example.Jinus.service.v2.cafeteria; +import com.example.Jinus.config.RedisConfig; import com.example.Jinus.dto.data.CafeteriaDto; import com.example.Jinus.dto.data.DietDto; import com.example.Jinus.dto.data.HandleRequestDto; import com.example.Jinus.repository.v2.cafeteria.CafeteriaRepositoryV2; import com.example.Jinus.repository.v2.cafeteria.DietRepositoryV2; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.sql.Date; +import java.time.Duration; +import java.time.LocalDateTime; import java.util.List; @Service +@Slf4j @RequiredArgsConstructor public class CacheServiceV2 { private final DietRepositoryV2 dietRepositoryV2; private final CafeteriaRepositoryV2 cafeteriaRepositoryV2; + private final RedisConfig redisConfig; -// @Cacheable( -// value = "dietList", -// key = "#parameters?.dietDate?.toString() + '::' + #parameters?.period + '::' + #cafeteriaId", -// unless = "#result == null || #result.isEmpty()", -// cacheManager = "contentCacheManager" -// ) - // 식단 데이터 가져오기 public List getDietList(HandleRequestDto parameters, int cafeteriaId) { - // 오늘, 내일 문자열로 날짜 설정하기 - Date dietDate = parameters.getDietDate(); - // 식단 데이터 반환 - return dietRepositoryV2.findDietList(dietDate, parameters.getPeriod(), cafeteriaId); + String key = parameters.getDietDate() + "::" + parameters.getPeriod() + "::" + cafeteriaId; + + List cached = (List) redisConfig.redisTemplate().opsForValue().get(key); + if (cached != null) return cached; + + List result = dietRepositoryV2.findDietList(parameters.getDietDate(), parameters.getPeriod(), cafeteriaId); + + if (result != null && !result.isEmpty()) { + long ttlSeconds = calculateTtlUntilNextMidnight(parameters.getDietDate()); + redisConfig.redisTemplate().opsForValue().set(key, result, Duration.ofSeconds(ttlSeconds)); + } + + return result; + } + + // 식단 데이터 TTL 동적 생성 -> 조회 시간과 하루 뒤의 자정시간까지의 시간차이가 TTL 시간 + // 즉, 날짜가 바뀌면 만료되어야 함 + private long calculateTtlUntilNextMidnight(Date dietDate) { + LocalDateTime expireAt = dietDate.toLocalDate().plusDays(1).atStartOfDay(); // dietDate + 1일 자정 + LocalDateTime now = LocalDateTime.now(); + + Duration duration = Duration.between(now, expireAt); + long seconds = duration.getSeconds(); + + // 만약 이미 만료되었거나, 현재 시간이 더 크면 0 리턴 (저장 안 함) + return seconds > 0 ? seconds : 0; } // Redis에서 조회 (없으면 DB에서 가져옴) -// @Cacheable(value = "cafeteriaList", key = "#campusId", cacheManager = "contentCacheManager") + @Cacheable( + value = "cafeteriaList", + key = "#p0", + cacheManager = "contentCacheManager") public List getCafeteriaList(int campusId) { return cafeteriaRepositoryV2.findCafeteriaListByCampusId(campusId); } diff --git a/src/main/java/com/example/Jinus/service/v2/cafeteria/CafeteriaQueryServiceV2.java b/src/main/java/com/example/Jinus/service/v2/cafeteria/CafeteriaQueryServiceV2.java index 305f4fa..b974ae5 100644 --- a/src/main/java/com/example/Jinus/service/v2/cafeteria/CafeteriaQueryServiceV2.java +++ b/src/main/java/com/example/Jinus/service/v2/cafeteria/CafeteriaQueryServiceV2.java @@ -2,21 +2,29 @@ import com.example.Jinus.repository.v2.cafeteria.CafeteriaRepositoryV2; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor +@Slf4j public class CafeteriaQueryServiceV2 { private final CafeteriaRepositoryV2 cafeteriaRepositoryV2; -// @Cacheable(value = "cafeteriaId", key = "#campusId + '::' + #cafeteriaName", -// unless = "#result == -1", -// cacheManager = "contentCacheManager") + @Cacheable( + value = "cafeteriaId", + key = "#p0 + '::' + #p1", + unless = "#result == -1", + cacheManager = "contentCacheManager") public int getCafeteriaId(String cafeteriaName, int campusId) { return cafeteriaRepositoryV2.findCafeteriaId(cafeteriaName, campusId).orElse(-1); } + @Cacheable( + value = "cafeteriaUrl", + key = "#p0", + cacheManager = "contentCacheManager") public String getImgUrl(int cafeteriaId) { return cafeteriaRepositoryV2.findImgUrlByCafeteriaId(cafeteriaId); } diff --git a/src/main/java/com/example/Jinus/service/v2/cafeteria/CampusServiceV2.java b/src/main/java/com/example/Jinus/service/v2/cafeteria/CampusServiceV2.java index 4f4e938..8e4c1bc 100644 --- a/src/main/java/com/example/Jinus/service/v2/cafeteria/CampusServiceV2.java +++ b/src/main/java/com/example/Jinus/service/v2/cafeteria/CampusServiceV2.java @@ -3,15 +3,22 @@ import com.example.Jinus.entity.cafeteria.CampusEntity; import com.example.Jinus.repository.v2.cafeteria.CampusRepositoryV2; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.List; @Service @RequiredArgsConstructor +@Slf4j public class CampusServiceV2 { private final CampusRepositoryV2 campusRepositoryV2; + @Cacheable( + value = "campusId", + key = "#p0", + cacheManager = "contentCacheManager") // 사용자의 campusId 받아 캠퍼스 이름 찾기 public String getUserCampusName(int campusId) { return campusRepositoryV2.findCampusNameByCampusId(campusId); @@ -22,7 +29,10 @@ public List getCampusList() { return campusRepositoryV2.findCampusList(); } -// @Cacheable(value = "campusName", key = "#campusName", cacheManager = "contentCacheManager") + @Cacheable( + value = "campusName", + key = "#p0", + cacheManager = "contentCacheManager") // 캠퍼스 이름으로 id 찾기 public int getCampusId(String campusName) { return campusRepositoryV2.findCampusIdByName(campusName); diff --git a/src/main/java/com/example/Jinus/service/v2/cafeteria/DietParameterServiceV2.java b/src/main/java/com/example/Jinus/service/v2/cafeteria/DietParameterServiceV2.java index 60d5139..c13a527 100644 --- a/src/main/java/com/example/Jinus/service/v2/cafeteria/DietParameterServiceV2.java +++ b/src/main/java/com/example/Jinus/service/v2/cafeteria/DietParameterServiceV2.java @@ -5,6 +5,7 @@ import com.example.Jinus.dto.request.RequestDto; import com.example.Jinus.service.v2.userInfo.UserServiceV2; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.sql.Date; @@ -14,6 +15,7 @@ @Service @RequiredArgsConstructor +@Slf4j public class DietParameterServiceV2 { private final UserServiceV2 userServiceV2; diff --git a/src/main/java/com/example/Jinus/service/v2/cafeteria/DietQueryServiceV2.java b/src/main/java/com/example/Jinus/service/v2/cafeteria/DietQueryServiceV2.java index 8ec2634..5cd0c6f 100644 --- a/src/main/java/com/example/Jinus/service/v2/cafeteria/DietQueryServiceV2.java +++ b/src/main/java/com/example/Jinus/service/v2/cafeteria/DietQueryServiceV2.java @@ -4,6 +4,7 @@ import com.example.Jinus.dto.data.HandleRequestDto; import com.example.Jinus.repository.v2.cafeteria.DietRepositoryV2; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -15,6 +16,7 @@ @Service @RequiredArgsConstructor +@Slf4j public class DietQueryServiceV2 { private final CacheServiceV2 cacheServiceV2; @@ -22,29 +24,20 @@ public class DietQueryServiceV2 { // 메뉴 존재 여부에 따른 반환값 처리 로직 public String getDietResponse(HandleRequestDto parameters, int cafeteriaId) { + // 메뉴 가져오기 + List dietDtos = cacheServiceV2.getDietList(parameters, cafeteriaId); // 메뉴 존재하는 경우 - if (checkThereIsDiet(parameters, cafeteriaId) != -1) { + if (!dietDtos.isEmpty()) { // 메뉴 찾기 - MultiValueMap dietList = getDiets(parameters, cafeteriaId); + MultiValueMap dietList = getDiets(dietDtos); return processDietList(dietList).toString(); } return "\n메뉴가 존재하지 않습니다."; // 메뉴가 없는 경우 } - // 식당 메뉴 존재여부 확인 - private int checkThereIsDiet(HandleRequestDto parameters, int cafeteriaId) { - // 오늘, 내일 문자열로 날짜 설정하기 - Date dietDate = parameters.getDietDate(); - List dietDtos = - dietRepositoryV2.findDietList(dietDate, parameters.getPeriod(), cafeteriaId); - return (!dietDtos.isEmpty()) ? 1 : -1; - } - - // 카테고리별 메뉴 리스트 생성하기 - private MultiValueMap getDiets(HandleRequestDto parameters, int cafeteriaId) { - List dietDtos = cacheServiceV2.getDietList(parameters, cafeteriaId); + private MultiValueMap getDiets(List dietDtos) { MultiValueMap dietList = new LinkedMultiValueMap<>(); // 중복 키 허용(값을 리스트로 반환) for (DietDto o : dietDtos) { diff --git a/src/main/java/com/example/Jinus/service/v2/cafeteria/DietServiceV2.java b/src/main/java/com/example/Jinus/service/v2/cafeteria/DietServiceV2.java index e6a4598..9439e14 100644 --- a/src/main/java/com/example/Jinus/service/v2/cafeteria/DietServiceV2.java +++ b/src/main/java/com/example/Jinus/service/v2/cafeteria/DietServiceV2.java @@ -2,20 +2,18 @@ import com.example.Jinus.dto.data.HandleRequestDto; import com.example.Jinus.dto.request.RequestDto; -import com.example.Jinus.repository.v2.cafeteria.DietRepositoryV2; import com.example.Jinus.utility.DateUtils; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.springframework.util.MultiValueMap; import java.sql.Date; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; -import java.util.Set; -import java.util.TreeSet; @Service +@Slf4j @RequiredArgsConstructor public class DietServiceV2 { diff --git a/src/main/java/com/example/Jinus/service/v2/userInfo/UserServiceV2.java b/src/main/java/com/example/Jinus/service/v2/userInfo/UserServiceV2.java index ae0a802..387e0b4 100644 --- a/src/main/java/com/example/Jinus/service/v2/userInfo/UserServiceV2.java +++ b/src/main/java/com/example/Jinus/service/v2/userInfo/UserServiceV2.java @@ -2,6 +2,7 @@ import com.example.Jinus.repository.v2.userInfo.UserRepositoryV2; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @@ -11,6 +12,7 @@ @Service @RequiredArgsConstructor +@Slf4j public class UserServiceV2 { private final UserRepositoryV2 userRepositoryV2;