From 337b1c852cbfb7cc1c4c1aa0256a22c018585094 Mon Sep 17 00:00:00 2001 From: Seoyeon Date: Fri, 28 Mar 2025 21:00:35 +0900 Subject: [PATCH 1/2] =?UTF-8?q?refactor(search):=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20#36?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/eightyage/domain/search/dto/PopularKeywordDto.java | 2 +- .../com/example/eightyage/domain/search/entity/SearchLog.java | 2 +- .../eightyage/domain/search/service/v1/SearchServiceV1.java | 2 +- .../eightyage/domain/search/service/v2/SearchServiceV2.java | 2 +- .../domain/search/service/v3/PopularKeywordServiceV3.java | 2 +- .../eightyage/domain/search/service/v3/SearchServiceV3.java | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/example/eightyage/domain/search/dto/PopularKeywordDto.java b/src/main/java/com/example/eightyage/domain/search/dto/PopularKeywordDto.java index 89eac11..b8db82b 100644 --- a/src/main/java/com/example/eightyage/domain/search/dto/PopularKeywordDto.java +++ b/src/main/java/com/example/eightyage/domain/search/dto/PopularKeywordDto.java @@ -14,7 +14,7 @@ public class PopularKeywordDto { private String keyword; private Long count; - public static PopularKeywordDto of(String keyword, Long score) { + public static PopularKeywordDto keywordOf(String keyword, Long score) { return new PopularKeywordDto(keyword, score); } } diff --git a/src/main/java/com/example/eightyage/domain/search/entity/SearchLog.java b/src/main/java/com/example/eightyage/domain/search/entity/SearchLog.java index 48588b4..94981a9 100644 --- a/src/main/java/com/example/eightyage/domain/search/entity/SearchLog.java +++ b/src/main/java/com/example/eightyage/domain/search/entity/SearchLog.java @@ -27,7 +27,7 @@ public class SearchLog { @CreatedDate private LocalDateTime searchedAt; - public static SearchLog of(String keyword) { + public static SearchLog keywordOf(String keyword) { SearchLog log = new SearchLog(); log.keyword = keyword; return log; diff --git a/src/main/java/com/example/eightyage/domain/search/service/v1/SearchServiceV1.java b/src/main/java/com/example/eightyage/domain/search/service/v1/SearchServiceV1.java index 3241312..753104e 100644 --- a/src/main/java/com/example/eightyage/domain/search/service/v1/SearchServiceV1.java +++ b/src/main/java/com/example/eightyage/domain/search/service/v1/SearchServiceV1.java @@ -18,7 +18,7 @@ public class SearchServiceV1 { @Transactional(propagation = Propagation.REQUIRES_NEW) public void saveSearchLog(String keyword){ if(StringUtils.hasText(keyword)){ - searchLogRepository.save(SearchLog.of(keyword)); + searchLogRepository.save(SearchLog.keywordOf(keyword)); } } } diff --git a/src/main/java/com/example/eightyage/domain/search/service/v2/SearchServiceV2.java b/src/main/java/com/example/eightyage/domain/search/service/v2/SearchServiceV2.java index c2928a6..05f40f7 100644 --- a/src/main/java/com/example/eightyage/domain/search/service/v2/SearchServiceV2.java +++ b/src/main/java/com/example/eightyage/domain/search/service/v2/SearchServiceV2.java @@ -27,7 +27,7 @@ public class SearchServiceV2 { @Transactional(propagation = Propagation.REQUIRES_NEW) public void saveSearchLog(String keyword) { if (StringUtils.hasText(keyword)) { - searchLogRepository.save(SearchLog.of(keyword)); + searchLogRepository.save(SearchLog.keywordOf(keyword)); } } diff --git a/src/main/java/com/example/eightyage/domain/search/service/v3/PopularKeywordServiceV3.java b/src/main/java/com/example/eightyage/domain/search/service/v3/PopularKeywordServiceV3.java index 843a561..95afe83 100644 --- a/src/main/java/com/example/eightyage/domain/search/service/v3/PopularKeywordServiceV3.java +++ b/src/main/java/com/example/eightyage/domain/search/service/v3/PopularKeywordServiceV3.java @@ -28,7 +28,7 @@ public List searchPopularKeywords(int limit) { if (keywordSet == null) { return List.of(); } - return keywordSet.stream().map(tuple -> PopularKeywordDto.of(tuple.getValue(), Objects.requireNonNull(tuple.getScore()).longValue())) + return keywordSet.stream().map(tuple -> PopularKeywordDto.keywordOf(tuple.getValue(), Objects.requireNonNull(tuple.getScore()).longValue())) .collect(Collectors.toList()); } } diff --git a/src/main/java/com/example/eightyage/domain/search/service/v3/SearchServiceV3.java b/src/main/java/com/example/eightyage/domain/search/service/v3/SearchServiceV3.java index 13e04aa..c366401 100644 --- a/src/main/java/com/example/eightyage/domain/search/service/v3/SearchServiceV3.java +++ b/src/main/java/com/example/eightyage/domain/search/service/v3/SearchServiceV3.java @@ -24,7 +24,7 @@ public class SearchServiceV3 { @Transactional(propagation = Propagation.REQUIRES_NEW) public void saveSearchLog(String keyword) { if (StringUtils.hasText(keyword)) { - searchLogRepository.save(SearchLog.of(keyword)); + searchLogRepository.save(SearchLog.keywordOf(keyword)); } } From c41b4a0596ba7be9190b8182a54e95b1c01676f3 Mon Sep 17 00:00:00 2001 From: Seoyeon Date: Fri, 28 Mar 2025 21:56:03 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor(search):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20DB=20=EC=A0=9C=EA=B1=B0=20#36?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../product/service/ProductService.java | 8 +- .../domain/search/entity/KeywordCount.java | 25 ------- .../repository/KeywordCountRepository.java | 8 -- .../service/v2/KeywordCountFlushService.java | 73 ------------------- .../service/v2/PopularKeywordServiceV2.java | 4 +- .../search/service/v2/SearchServiceV2.java | 37 ---------- .../search/service/v3/SearchServiceV3.java | 2 +- .../eightyage/global/config/CacheConfig.java | 2 - 8 files changed, 6 insertions(+), 153 deletions(-) delete mode 100644 src/main/java/com/example/eightyage/domain/search/entity/KeywordCount.java delete mode 100644 src/main/java/com/example/eightyage/domain/search/repository/KeywordCountRepository.java delete mode 100644 src/main/java/com/example/eightyage/domain/search/service/v2/KeywordCountFlushService.java diff --git a/src/main/java/com/example/eightyage/domain/product/service/ProductService.java b/src/main/java/com/example/eightyage/domain/product/service/ProductService.java index 9d4e986..d8563a1 100644 --- a/src/main/java/com/example/eightyage/domain/product/service/ProductService.java +++ b/src/main/java/com/example/eightyage/domain/product/service/ProductService.java @@ -98,7 +98,7 @@ public Page getProductsV1(String productName, Category Page productsResponse = productRepository.findProductsOrderByReviewScore(productName, category, pageable); if (StringUtils.hasText(productName) && !productsResponse.isEmpty()) { - searchServiceV1.saveSearchLog(productName); // 로그만 저장 + searchServiceV1.saveSearchLog(productName); // 로그 저장 } return productsResponse; } @@ -112,7 +112,6 @@ public Page getProductsV2(String productName, Category if (StringUtils.hasText(productName) && !productsResponse.isEmpty()) { searchServiceV2.saveSearchLog(productName); // 로그 저장 - searchServiceV2.increaseKeywordCount(productName); // 캐시 작업 } return productsResponse; @@ -126,8 +125,8 @@ public Page getProductsV3(String productName, Category Page productsResponse = productRepository.findProductsOrderByReviewScore(productName, category, pageable); if(StringUtils.hasText(productName) && !productsResponse.isEmpty()){ - searchServiceV3.saveSearchLog(productName); - searchServiceV3.increaseSortedKeywordRank(productName); + searchServiceV3.saveSearchLog(productName); // 로그 저장 + searchServiceV3.increaseSortedKeywordRank(productName); // 캐시 추가 } return productsResponse; } @@ -151,5 +150,4 @@ public Product findProductByIdOrElseThrow(Long productId) { ); } - } diff --git a/src/main/java/com/example/eightyage/domain/search/entity/KeywordCount.java b/src/main/java/com/example/eightyage/domain/search/entity/KeywordCount.java deleted file mode 100644 index 6ceada2..0000000 --- a/src/main/java/com/example/eightyage/domain/search/entity/KeywordCount.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.example.eightyage.domain.search.entity; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Entity -@Getter -@NoArgsConstructor -public class KeywordCount { - - @Id - private String keyword; - private Long count; - - public KeywordCount(String keyword, Long count) { - this.keyword = keyword; - this.count = count; - } - - public void updateCount(Long count) { - this.count = count; - } -} diff --git a/src/main/java/com/example/eightyage/domain/search/repository/KeywordCountRepository.java b/src/main/java/com/example/eightyage/domain/search/repository/KeywordCountRepository.java deleted file mode 100644 index 0341ab8..0000000 --- a/src/main/java/com/example/eightyage/domain/search/repository/KeywordCountRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.example.eightyage.domain.search.repository; - - -import com.example.eightyage.domain.search.entity.KeywordCount; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface KeywordCountRepository extends JpaRepository { -} diff --git a/src/main/java/com/example/eightyage/domain/search/service/v2/KeywordCountFlushService.java b/src/main/java/com/example/eightyage/domain/search/service/v2/KeywordCountFlushService.java deleted file mode 100644 index b7aa890..0000000 --- a/src/main/java/com/example/eightyage/domain/search/service/v2/KeywordCountFlushService.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.example.eightyage.domain.search.service.v2; - -import com.example.eightyage.domain.search.entity.KeywordCount; -import com.example.eightyage.domain.search.repository.KeywordCountRepository; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Set; - -@Service -@RequiredArgsConstructor -@Slf4j -public class KeywordCountFlushService { - - private final CacheManager cacheManager; - private final KeywordCountRepository keywordCountRepository; - private static final String KEYWORD_COUNT_MAP = "keywordCountMap"; - private static final String KEYWORD_KEY_SET = "keywordKeySet"; - - @Transactional - @Scheduled(fixedRate = 5 * 60 * 1000) // 5분마다 실행 - public void flushKeywordCounts() { - Cache countCache = cacheManager.getCache(KEYWORD_COUNT_MAP); - Cache keySetCache = cacheManager.getCache(KEYWORD_KEY_SET); - - if (countCache == null || keySetCache == null) { - log.warn("캐시를 찾을 수 없습니다."); - return; - } - - try { - // 키 목록 가져오기 - Set keywordSet = keySetCache.get("keywords", Set.class); - if (keywordSet == null || keywordSet.isEmpty()) { - log.info("flush 할 키워드가 없습니다."); - return; - } - - int flushed = 0; - - // 반복문을 이용하여 저장하기 - for (String keyword : keywordSet) { - String countStr = countCache.get(keyword, String.class); - if (countStr == null) continue; - - Long count = Long.parseLong(countStr); - if (count == 0L) continue; - - keywordCountRepository.findById(keyword) - .ifPresentOrElse( - exist -> exist.updateCount(exist.getCount() + count), - () -> keywordCountRepository.save(new KeywordCount(keyword, count)) - ); - flushed++; - countCache.evict(keyword); - } - - keySetCache.put("keywords", new java.util.HashSet<>()); - - log.info("{}개의 키워드 플러시 성공", flushed); - - } catch (Exception e) { - log.error("플러시 실패", e); - } - - } - -} diff --git a/src/main/java/com/example/eightyage/domain/search/service/v2/PopularKeywordServiceV2.java b/src/main/java/com/example/eightyage/domain/search/service/v2/PopularKeywordServiceV2.java index 7b0be98..83e2519 100644 --- a/src/main/java/com/example/eightyage/domain/search/service/v2/PopularKeywordServiceV2.java +++ b/src/main/java/com/example/eightyage/domain/search/service/v2/PopularKeywordServiceV2.java @@ -19,11 +19,11 @@ public class PopularKeywordServiceV2 implements PopularKeywordService { private final SearchLogRepository searchLogRepository; private static final int MIN_DAYS = 1; private static final int MAX_DAYS = 365; - + private static final String POPULAR_KEYWORDS = "popularKeywords"; //캐시O 인기 검색어 조회 @Transactional(readOnly = true) - @Cacheable(value = "popularKeywords", key = "#days") + @Cacheable(value = POPULAR_KEYWORDS, key = "#days") public List searchPopularKeywords(int days) { if (days < MIN_DAYS || days > MAX_DAYS) { throw new BadRequestException("조회 일 수는 1~365 사이여야 합니다."); diff --git a/src/main/java/com/example/eightyage/domain/search/service/v2/SearchServiceV2.java b/src/main/java/com/example/eightyage/domain/search/service/v2/SearchServiceV2.java index 05f40f7..333332c 100644 --- a/src/main/java/com/example/eightyage/domain/search/service/v2/SearchServiceV2.java +++ b/src/main/java/com/example/eightyage/domain/search/service/v2/SearchServiceV2.java @@ -3,25 +3,17 @@ import com.example.eightyage.domain.search.entity.SearchLog; import com.example.eightyage.domain.search.repository.SearchLogRepository; import lombok.RequiredArgsConstructor; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import java.util.HashSet; -import java.util.Set; @Service @RequiredArgsConstructor public class SearchServiceV2 { private final SearchLogRepository searchLogRepository; - private final CacheManager cacheManager; - private static final String KEYWORD_COUNT_MAP = "keywordCountMap"; - private static final String KEYWORD_KEY_SET = "keywordKeySet"; - // 검색 키워드를 로그에 저장 @Transactional(propagation = Propagation.REQUIRES_NEW) @@ -30,33 +22,4 @@ public void saveSearchLog(String keyword) { searchLogRepository.save(SearchLog.keywordOf(keyword)); } } - - // 검색 시 키워드 카운트 증가 - @Transactional - public void increaseKeywordCount(String keyword) { - if (!StringUtils.hasText(keyword)) return; - - Cache countCache = cacheManager.getCache(KEYWORD_COUNT_MAP); - - if (countCache != null) { - String countStr = countCache.get(keyword, String.class); - long count = (countStr == null) ? 1L : Long.parseLong(countStr) + 1; - countCache.put(keyword, Long.toString(count)); - } - - updateKeywordSet(keyword); - } - - // 캐시에 키워드 추가 - private void updateKeywordSet(String keyword) { - Cache keySetCache = cacheManager.getCache(KEYWORD_KEY_SET); - if (keySetCache != null) { - Set keywordSet = keySetCache.get("keywords", Set.class); - if (keywordSet == null) { - keywordSet = new HashSet<>(); - } - keywordSet.add(keyword); - keySetCache.put("keywords", keywordSet); - } - } } diff --git a/src/main/java/com/example/eightyage/domain/search/service/v3/SearchServiceV3.java b/src/main/java/com/example/eightyage/domain/search/service/v3/SearchServiceV3.java index c366401..384d11c 100644 --- a/src/main/java/com/example/eightyage/domain/search/service/v3/SearchServiceV3.java +++ b/src/main/java/com/example/eightyage/domain/search/service/v3/SearchServiceV3.java @@ -31,6 +31,6 @@ public void saveSearchLog(String keyword) { // 검색어 점수 증가 public void increaseSortedKeywordRank(String productName) { redisTemplate.opsForZSet().incrementScore(RANKING_KEY, productName, 1); - redisTemplate.expire(RANKING_KEY, Duration.ofMinutes(1)); + redisTemplate.expire(RANKING_KEY, Duration.ofMinutes(5)); } } diff --git a/src/main/java/com/example/eightyage/global/config/CacheConfig.java b/src/main/java/com/example/eightyage/global/config/CacheConfig.java index dc34521..7d3ed20 100644 --- a/src/main/java/com/example/eightyage/global/config/CacheConfig.java +++ b/src/main/java/com/example/eightyage/global/config/CacheConfig.java @@ -28,8 +28,6 @@ public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) // 캐시 별로 TTL 설정 Map configMap = new HashMap<>(); - configMap.put("keywordCountMap", defaultConfig.entryTtl(Duration.ZERO)); - configMap.put("keywordKeySet", defaultConfig.entryTtl(Duration.ZERO)); configMap.put("popularKeywords", defaultConfig.entryTtl(Duration.ofMinutes(5))); return RedisCacheManager.builder(redisConnectionFactory)