Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import kr.co.knuserver.presentation.booth.dto.BoothInfoResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothListResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothRankingResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothTop3ResponseDto;
import org.springframework.data.redis.core.ZSetOperations;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
Expand Down Expand Up @@ -96,7 +97,31 @@ public List<BoothRankingResponseDto> getBoothRanking() {
.toList();
}


public List<BoothTop3ResponseDto> getTop3BoothRanking() {
Set<ZSetOperations.TypedTuple<String>> rawRanking = boothLikeService.getRanking();
if (rawRanking == null || rawRanking.isEmpty()) {
return List.of();
}

BoothRanking boothRanking = new BoothRanking(rawRanking);
if (boothRanking.isEmpty()) {
return List.of();
}

List<Booth> booths = boothRepository.findAllById(boothRanking.boothIds());

return booths.stream()
.map(booth -> new BoothTop3ResponseDto(
booth.getId(),
booth.getName(),
boothRanking.getLikeCount(booth.getId())
))
.sorted(Comparator.comparingLong(BoothTop3ResponseDto::likeCount).reversed()
.thenComparingLong(BoothTop3ResponseDto::boothId))
.limit(3)
.toList();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Top3 API가 전체 랭킹/전체 부스를 읽어 확장 시 병목이 됩니다.

Line [101]에서 전체 랭킹을 가져오고, Line [111]에서 전체 ID를 DB 조회한 뒤 마지막에 3개만 자르고 있습니다. Top3 전용 API라서 요청당 비용이 불필요하게 큽니다.

⚡ 제안 수정안 (Top N만 조회하도록 경로 축소)
-        Set<ZSetOperations.TypedTuple<String>> rawRanking = boothLikeService.getRanking();
+        // 예: Redis에서 상위 후보만 조회 (여유 버퍼 포함)
+        Set<ZSetOperations.TypedTuple<String>> rawRanking = boothLikeService.getTopRanking(10);
// BoothLikeService 쪽 예시 (별도 파일 변경)
public Set<ZSetOperations.TypedTuple<String>> getTopRanking(int limit) {
    try {
        return redisTemplate.opsForZSet().reverseRangeWithScores(RANKING_KEY, 0, limit - 1);
    } catch (DataAccessException e) {
        log.warn("[Ranking] Redis 조회 실패, 빈 셋 반환", e);
        return Collections.emptySet();
    }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/kr/co/knuserver/application/booth/BoothQueryService.java`
around lines 101 - 122, The Top3 endpoint currently fetches the entire ranking
and all booth rows, causing unnecessary load; change BoothQueryService to call a
new limited Redis fetch (use boothLikeService.getTopRanking(3) instead of
getRanking()), construct BoothRanking from that limited set, collect only the
top N booth IDs, query the DB for only those IDs (use a repository method that
takes a List of IDs, e.g., boothRepository.findAllByIdIn or findByIdIn), then
map those results back to BoothTop3ResponseDto using
BoothRanking.getLikeCount(...) and preserve ordering by sorting according to the
limited ranking (or by using the ranking order) before returning the top 3.
Ensure any helper BoothRanking constructor/usage supports the reduced set and
keep mapping via BoothTop3ResponseDto(booth.getId(), booth.getName(),
boothRanking.getLikeCount(booth.getId())).

}

public CursorPaginationResponse<BoothListResponseDto> searchBoothsByKeyword2(String keyword, Long lastId, int size) {
Pageable pageable = PageRequest.of(0, size + 1);
List<Booth> booths;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import kr.co.knuserver.presentation.booth.dto.BoothInfoResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothListResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothRankingResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothTop3ResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothUpdateRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -85,4 +86,11 @@ public ResponseEntity<ApiResponse<List<BoothRankingResponseDto>>> getBoothRankin
List<BoothRankingResponseDto> result = boothQueryService.getBoothRanking();
return ResponseEntity.ok(ApiResponse.success(result));
}

@Override
@GetMapping("/ranking/top3")
public ResponseEntity<ApiResponse<List<BoothTop3ResponseDto>>> getTop3BoothRanking() {
List<BoothTop3ResponseDto> result = boothQueryService.getTop3BoothRanking();
return ResponseEntity.ok(ApiResponse.success(result));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import kr.co.knuserver.presentation.booth.dto.BoothCountResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothInfoResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothListResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothTop3ResponseDto;
import kr.co.knuserver.presentation.booth.dto.BoothUpdateRequestDto;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -60,4 +61,10 @@ ResponseEntity<kr.co.knuserver.global.exception.ApiResponse<CursorPaginationResp
@Parameter(description = "페이지 크기 (기본값: 10)")
@RequestParam(name = "size", required = false, defaultValue = "10") int size
);

@Operation(summary = "가두모집 부스 좋아요 TOP 3 조회", description = "좋아요 수 기준 상위 3개 부스를 반환합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "TOP 3 랭킹 조회 성공")
})
ResponseEntity<kr.co.knuserver.global.exception.ApiResponse<List<BoothTop3ResponseDto>>> getTop3BoothRanking();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kr.co.knuserver.presentation.booth.dto;

public record BoothTop3ResponseDto(
Long boothId,
String boothName,
long likeCount
) {}
Loading