-
Notifications
You must be signed in to change notification settings - Fork 1
Feat/#108 외부 부스 랭킹에서 제거 #109
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
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 | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,44 @@ | ||||||||||||||||||||||||||||||||||
| package kr.co.knuserver.application.booth; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import java.util.Map; | ||||||||||||||||||||||||||||||||||
| import java.util.Optional; | ||||||||||||||||||||||||||||||||||
| import java.util.Set; | ||||||||||||||||||||||||||||||||||
| import java.util.stream.Collectors; | ||||||||||||||||||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||||||||||||||||||
| import org.springframework.data.redis.core.ZSetOperations; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @Slf4j | ||||||||||||||||||||||||||||||||||
| public class BoothRanking { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private final Map<Long, Long> likeCountMap; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public BoothRanking(Set<ZSetOperations.TypedTuple<String>> tuples) { | ||||||||||||||||||||||||||||||||||
| this.likeCountMap = tuples.stream() | ||||||||||||||||||||||||||||||||||
| .filter(t -> t.getValue() != null) | ||||||||||||||||||||||||||||||||||
| .flatMap(t -> parseBoothId(t.getValue()) | ||||||||||||||||||||||||||||||||||
| .map(id -> Map.entry(id, t.getScore() == null ? 0L : t.getScore().longValue())) | ||||||||||||||||||||||||||||||||||
| .stream()) | ||||||||||||||||||||||||||||||||||
| .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+21
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. 🧩 Analysis chain🏁 Script executed: cat -n src/main/java/kr/co/knuserver/application/booth/BoothRanking.javaRepository: knu-80/knu-server Length of output: 1691 🏁 Script executed: rg "parseBoothId" -A 10 -B 2 src/main/java/kr/co/knuserver/application/booth/Repository: knu-80/knu-server Length of output: 2040 🏁 Script executed: # parseBoothId 메서드의 파싱 로직 정확성을 재확인
# "1"과 "01" 모두 같은 Long 값으로 파싱되는지 검증
python3 << 'EOF'
# Java Long.parseLong("1") vs Long.parseLong("01")과 동일한 동작
result1 = int("1")
result2 = int("01")
print(f"int('1') = {result1}")
print(f"int('01') = {result2}")
print(f"Are they equal? {result1 == result2}")
EOFRepository: knu-80/knu-server Length of output: 108 Line 21의 Redis member가 "1"과 "01"처럼 파싱 후 동일한 Long 값(1L)이 되는 경우, 수정 제안- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+ .collect(Collectors.toMap(
+ Map.Entry::getKey,
+ Map.Entry::getValue,
+ Long::max
+ ));📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| private Optional<Long> parseBoothId(String value) { | ||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||
| return Optional.of(Long.parseLong(value)); | ||||||||||||||||||||||||||||||||||
| } catch (NumberFormatException e) { | ||||||||||||||||||||||||||||||||||
| log.error("[BoothRanking] Redis member 파싱 실패 value={}", value, e); | ||||||||||||||||||||||||||||||||||
| return Optional.empty(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public boolean isEmpty() { | ||||||||||||||||||||||||||||||||||
| return likeCountMap.isEmpty(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public Set<Long> boothIds() { | ||||||||||||||||||||||||||||||||||
| return likeCountMap.keySet(); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| public long getLikeCount(Long boothId) { | ||||||||||||||||||||||||||||||||||
| return likeCountMap.getOrDefault(boothId, 0L); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
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.
랭킹 조회에서 외부/비활성 부스가 다시 노출될 수 있습니다.
Line 86의
findAllById는 필터가 없어 Redis에 남아 있는 EXTERNAL_SUPPORT/비활성 부스를 그대로 응답에 포함시킬 수 있습니다. PR 목표와 충돌합니다.🔧 제안 수정안
🤖 Prompt for AI Agents
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.
76번줄 boothLikeService.getRanking(); 에서 이미 필터링 되는데 왜 포함되나요?
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.
🧩 Analysis chain
🏁 Script executed:
Length of output: 9879
@wlgns12370, 좋은 지적입니다! 다시 확인해 보겠습니다.getRanking()구현을 보면 실제로는 필터링이 없습니다:워밍업과
like()차단으로 EXTERNAL_SUPPORT 부스가 Redis에 들어가지 않는다는 점은 맞습니다. 그런데 다른 진입 경로가 있습니다.BoothLikeService.getLikeCount()를 확인해 보세요:그리고
BoothQueryService.getBooth()는 모든 부스에 대해 이를 호출합니다:결과적으로 다음 경로가 만들어집니다:
따라서 원래 제안한 수정이 여전히 유효합니다. 수정 방법은 두 가지입니다:
방법 1:
getLikeCount()에서 EXTERNAL_SUPPORT 부스가 ZSet에 등록되지 않도록 방어방법 2 (더 단순):
getBoothRanking()에서 필터링된 쿼리 사용 (기존 제안)방법 1은 근본 원인을 해결하고, 방법 2는 랭킹 응답에서 방어적으로 필터링합니다. 두 방법을 함께 적용하는 것이 가장 안전합니다.
🧠 Learnings used
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.
아니 애초에 warm up에서 넣어가는거 아니야?
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.
🧩 Analysis chain
좋은 질문입니다. 코드를 다시 확인해보겠습니다.
🏁 Script executed:
Length of output: 9273
@wlgns12370, 맞습니다 — 워밍업이 주된 삽입 경로이고, 이번 PR에서 EXTERNAL_SUPPORT를 올바르게 제외했습니다. 그런데 두 번째 삽입 경로가getLikeCount()에 존재합니다:그리고 이 메서드는
getBooth()에서 모든 부스 ID에 대해 호출됩니다:따라서 실제 흐름은 다음과 같습니다:
getLikeCount()에 division 체크를 추가하는 것이 가장 근본적인 수정입니다:🧠 Learnings used