-
Notifications
You must be signed in to change notification settings - Fork 1
캐싱 적용 및 중복 로직 제거 #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
캐싱 적용 및 중복 로직 제거 #35
Changes from all commits
e72c534
1dbbc56
b91f4a7
f19aa3d
7f9a50e
0c7cd50
dd851d2
c098a0c
2a7290c
fbca9f3
4e67e9e
a4eb0a9
e70e82b
9b7b803
00f3532
6f829d9
931b37c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<DietDto> getDietList(HandleRequestDto parameters, int cafeteriaId) { | ||
| // 오늘, 내일 문자열로 날짜 설정하기 | ||
| Date dietDate = parameters.getDietDate(); | ||
| // 식단 데이터 반환 | ||
| return dietRepositoryV2.findDietList(dietDate, parameters.getPeriod(), cafeteriaId); | ||
| String key = parameters.getDietDate() + "::" + parameters.getPeriod() + "::" + cafeteriaId; | ||
|
|
||
| List<DietDto> cached = (List<DietDto>) redisConfig.redisTemplate().opsForValue().get(key); | ||
| if (cached != null) return cached; | ||
|
|
||
| List<DietDto> 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)); | ||
| } | ||
|
Comment on lines
+31
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Skip the If redisTemplate.opsForValue().set(key, result, Duration.ofSeconds(0));With Redis this translates to a long ttlSeconds = calculateTtlUntilNextMidnight(parameters.getDietDate());
-if (ttlSeconds > 0) {
- redisTemplate.opsForValue().set(key, result, Duration.ofSeconds(ttlSeconds));
-}
+if (ttlSeconds > 0) {
+ redisTemplate.opsForValue().set(key, result, Duration.ofSeconds(ttlSeconds));
+} else {
+ log.debug("Diet cache not written because TTL is non‑positive for key={}", key);
+} |
||
|
|
||
| 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<CafeteriaDto> getCafeteriaList(int campusId) { | ||
| return cafeteriaRepositoryV2.findCafeteriaListByCampusId(campusId); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Prefer constructor‑injected
RedisTemplateover calling theRedisConfigfactory on every invocationredisConfig.redisTemplate()is executed on every call togetDietList.Even though the factory method is likely annotated with
@Bean, Spring still has to look the bean up each time, and the indirection hides the real dependency of this service (the template, not the config).Injecting the template directly makes dependencies explicit, avoids the per‑call lookup, and removes the need to expose the
redisTemplate()factory method altogether.Update the two call‑sites (
get/set) to useredisTemplateinstead ofredisConfig.redisTemplate().This keeps the service focused and eases unit testing (you can now pass a mocked template).