-
Notifications
You must be signed in to change notification settings - Fork 0
SCRUM 236 : 최근 검색어 기능 리팩토링 #100
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
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -29,18 +29,24 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.team_nebula.nebula.global.apipayload.exception.GeneralException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.context.ApplicationEventPublisher; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.data.redis.core.RedisTemplate; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.scheduling.annotation.Async; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Service; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.transaction.annotation.Transactional; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.multipart.MultipartFile; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.Duration; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.LocalDateTime; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.OffsetDateTime; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.Optional; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.UUID; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.stream.Collectors; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import static org.springframework.data.neo4j.core.ReactiveNeo4jClient.log; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+48
to
+49
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. 🛠️ Refactor suggestion Incorrect logger import and usage; use a proper class logger.
Within this file’s changed lines, remove the static import: -import static org.springframework.data.neo4j.core.ReactiveNeo4jClient.log;And update error calls in the methods (see diffs on those ranges). 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Service | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Transactional | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -59,6 +65,9 @@ public class StarCommandServiceImpl implements StarCommandService { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final FaviconRepository faviconRepository; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final ApplicationEventPublisher eventPublisher; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final RedisTemplate<String, Object> redisTemplate; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private static final int MAX_RECENT_SEARCHES = 10; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public CreateStarResponseDTO createFirstStar(Long userId, MultipartFile htmlFile, String title, String siteUrl){ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -314,5 +323,32 @@ public CreateStarResponseDTO createStar(Long userId, CreateStarRequestDTO reques | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Async | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void saveRecentSearches(Long userId, String keyword){ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String key = "recent_search:" + userId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String normalizedKeyword = keyword.trim().toLowerCase(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 중복 제거 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redisTemplate.opsForList().remove(key, 0, normalizedKeyword); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redisTemplate.opsForList().leftPush(key, normalizedKeyword); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redisTemplate.opsForList().trim(key, 0, MAX_RECENT_SEARCHES - 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redisTemplate.expire(key, Duration.ofDays(30)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.error(e, "Failed to save recent search"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+326
to
+342
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. 🛠️ Refactor suggestion Harden save logic: null/blank guard, locale-safe lowercasing, correct logging, and avoid unnecessary DB transactions.
Proposed changes: @Override
@Async
public void saveRecentSearches(Long userId, String keyword){
try {
- String key = "recent_search:" + userId;
- String normalizedKeyword = keyword.trim().toLowerCase();
+ if (keyword == null) return;
+ String trimmed = keyword.trim();
+ if (trimmed.isEmpty()) return;
+ String key = "recent_search:" + userId;
+ String normalizedKeyword = trimmed.toLowerCase(java.util.Locale.ROOT);
// 중복 제거
redisTemplate.opsForList().remove(key, 0, normalizedKeyword);
redisTemplate.opsForList().leftPush(key, normalizedKeyword);
redisTemplate.opsForList().trim(key, 0, MAX_RECENT_SEARCHES - 1);
redisTemplate.expire(key, java.time.Duration.ofDays(30));
}
catch (Exception e) {
- log.error(e, "Failed to save recent search");
+ log.error("Failed to save recent search", e);
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void deleteRecentSearch(Long userId, String keyword) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String key = "recent_search:" + userId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redisTemplate.opsForList().remove(key, 0, keyword.trim().toLowerCase()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.error(e, "Failed to delete recent search"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+326
to
+352
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. 💡 Verification agent 🧩 Analysis chainOptional: prevent JPA transaction for Redis-only methods and add @slf4j. Outside these changed lines, consider:
Example (outside selected lines): import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Propagation;
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class StarCommandServiceImpl implements StarCommandService {
@Override
@Async
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveRecentSearches(Long userId, String keyword) { ... }
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void deleteRecentSearch(Long userId, String keyword) { ... }
}Check that async execution is enabled and key usage is consistent: 🏁 Script executed: #!/bin/bash
echo "Search for @EnableAsync (required for @Async to work via proxies):"
rg -n '@EnableAsync'
echo ""
echo "Check consistent Redis key usage for recent searches:"
rg -n --pretty 'recent_search:'Length of output: 604 Add @slf4j and mark Redis‐only methods as non‐transactional To avoid opening unnecessary JPA transactions for Redis operations and enable logging in this class: • Annotate Suggested diff in @@
-import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class StarCommandServiceImpl implements StarCommandService {
@Override
@Async
- public void saveRecentSearches(Long userId, String keyword){
+ @Transactional(propagation = Propagation.NOT_SUPPORTED)
+ public void saveRecentSearches(Long userId, String keyword) {
try {
String key = "recent_search:" + userId;
String normalizedKeyword = keyword.trim().toLowerCase();
@@
@Override
- public void deleteRecentSearch(Long userId, String keyword) {
+ @Transactional(propagation = Propagation.NOT_SUPPORTED)
+ public void deleteRecentSearch(Long userId, String keyword) {
try {
String key = "recent_search:" + userId;
redisTemplate.opsForList().remove(key, 0, keyword.trim().toLowerCase());📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Comment on lines
+344
to
+352
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. 🛠️ Refactor suggestion Normalize with Locale.ROOT and fix logging; mirror save behavior. Ensure delete uses the same normalization and correct logging: @Override
public void deleteRecentSearch(Long userId, String keyword) {
try {
String key = "recent_search:" + userId;
- redisTemplate.opsForList().remove(key, 0, keyword.trim().toLowerCase());
+ if (keyword == null) return;
+ String trimmed = keyword.trim();
+ if (trimmed.isEmpty()) return;
+ redisTemplate.opsForList().remove(key, 0, trimmed.toLowerCase(java.util.Locale.ROOT));
} catch (Exception e) {
- log.error(e, "Failed to delete recent search");
+ log.error("Failed to delete recent search", e);
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,11 +5,11 @@ | |||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import com.team_nebula.nebula.domain.star.dto.response.*; | ||||||||||||||||||||||||
| import com.team_nebula.nebula.domain.star.repository.StarNeo4jRepositoryCustom; | ||||||||||||||||||||||||
| import com.team_nebula.nebula.domain.star.search.document.StarSearchDocument; | ||||||||||||||||||||||||
| import com.team_nebula.nebula.domain.star.search.dto.response.SearchResultResponseDTO; | ||||||||||||||||||||||||
| import com.team_nebula.nebula.domain.star.search.dto.response.SearchStarResponseDTO; | ||||||||||||||||||||||||
| import com.team_nebula.nebula.domain.star.search.service.ElasticsearchService; | ||||||||||||||||||||||||
| import org.springframework.cache.annotation.Cacheable; | ||||||||||||||||||||||||
| import org.springframework.data.redis.core.RedisTemplate; | ||||||||||||||||||||||||
| import org.springframework.stereotype.Service; | ||||||||||||||||||||||||
| import org.springframework.transaction.annotation.Transactional; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
@@ -21,6 +21,8 @@ | |||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import static org.springframework.data.neo4j.core.ReactiveNeo4jClient.log; | ||||||||||||||||||||||||
|
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. Fix logger: invalid static import and wrong error(...) signature
Apply this diff and add Lombok logger: - import static org.springframework.data.neo4j.core.ReactiveNeo4jClient.log;
+ import lombok.extern.slf4j.Slf4j;- log.error(e, "Failed to get recent searches");
+ log.error("Failed to get recent searches for userId={}", userId, e);Outside this hunk, annotate the class: // add above class declaration
@Slf4jAlso applies to: 199-199 🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Service | ||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||
| @Transactional(readOnly = true) | ||||||||||||||||||||||||
|
|
@@ -31,6 +33,9 @@ public class StarQueryServiceImpl implements StarQueryService { | |||||||||||||||||||||||
| private final StarNeo4jRepositoryCustom starNeo4jRepositoryCustom; | ||||||||||||||||||||||||
| private final ElasticsearchService elasticsearchService; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| private final RedisTemplate<String, Object> redisTemplate; | ||||||||||||||||||||||||
| private static final int MAX_RECENT_SEARCHES = 10; | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| // 스타 + 링크 전체 조회 | ||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||
| public GetStarListResponseDTO getStarList(Long userId) { | ||||||||||||||||||||||||
|
|
@@ -164,8 +169,6 @@ public SearchResultResponseDTO searchStarsV2(String keyword, Long userId, int pa | |||||||||||||||||||||||
| .build(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||
| public List<String> getAutoComplete(String query, Long userId, int size) { | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
@@ -181,4 +184,20 @@ public List<String> getAutoComplete(String query, Long userId, int size) { | |||||||||||||||||||||||
| return elasticsearchService.getAutoComplete(query, userId, size); | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||
| @Cacheable(value = "recent_searches", key = "#userId") | ||||||||||||||||||||||||
|
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. 💡 Verification agent 🧩 Analysis chainAvoid stale results: @Cacheable without eviction on writes
Run this to check for cache eviction on write paths and cache TTL config: 🏁 Script executed: #!/bin/bash
# Find recent searches write operations and verify cache eviction
rg -n --hidden --glob '!**/node_modules/**' '@CacheEvict\([^)]*value\s*=\s*"recent_searches"' -A 2
rg -n --hidden --glob '!**/node_modules/**' 'saveRecentSearch|saveRecentSearches|deleteRecentSearch' -A 3
# Look for CacheManager/RedisCacheManager TTL config
rg -n --hidden --glob '!**/node_modules/**' 'RedisCacheManager|CacheManager|cacheNames|ttl|timeToLive' -A 3
# Check for any other usage of "recent_searches" to avoid key/cachename collisions
rg -n --hidden --glob '!**/node_modules/**' '"recent_searches"'Length of output: 6160 Ensure cache invalidation and correct TTL for
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| public List<String> getRecentSearches(Long userId) { | ||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||
| String key = "recent_searches"+userId.toString(); | ||||||||||||||||||||||||
| List<Object> searches = redisTemplate.opsForList().range(key, 0, MAX_RECENT_SEARCHES - 1); | ||||||||||||||||||||||||
|
Comment on lines
+190
to
+193
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. 🛠️ Refactor suggestion Validate userId and improve Redis key format
public List<String> getRecentSearches(Long userId) {
- try {
- String key = "recent_searches"+userId.toString();
+ if (userId == null) {
+ throw new GeneralException(ErrorStatus._USER_NOT_FOUND);
+ }
+ try {
+ final String key = String.format("star:recent-searches:%d", userId);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| return searches.stream() | ||||||||||||||||||||||||
| .map(Object::toString) | ||||||||||||||||||||||||
| .collect(Collectors.toList()); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| catch (Exception e) { | ||||||||||||||||||||||||
| log.error(e, "Failed to get recent searches"); | ||||||||||||||||||||||||
| return Collections.emptyList(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
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.
💡 Verification agent
🧩 Analysis chain
Avoid saving blank queries; reconsider side-effect in GET.
qto prevent storing empty recent searches.Suggested guard:
Confirm there are no other code paths persisting blank queries:
🏁 Script executed:
Length of output: 240
🏁 Script executed:
Length of output: 1778
Guard blank search queries & reconsider side-effect in GET
We searched the entire codebase and found only one direct call to
saveRecentSearchesin a GET handler (StarV2Controller). To prevent empty keywords from being stored—and to keep this endpoint free of side-effects—please apply the following patch:• File:
src/main/java/com/team_nebula/nebula/domain/star/api/StarV2Controller.javaAlso consider emitting an asynchronous domain event for “recent search” recording instead of performing this side-effect directly within a GET handler.
📝 Committable suggestion
🤖 Prompt for AI Agents