Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public class BettingController {
@ApiResponse(responseCode = "404", description = "활성 라운드가 존재하지 않음")
}
)

@GetMapping("/bet-rounds/{scope}")
public ResponseEntity<BetRound> getTodayBetRound(
@Parameter(description = "라운드 범위 (Scope): DAILY 또는 WEEKLY", example = "DAILY")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import java.util.UUID;

public interface BetRoundRepository extends JpaRepository<BetRound, UUID> {
Optional<BetRound> findByStatusTrueAndScope(Scope type);
Optional<BetRound> findTopByStatusTrueAndScopeOrderByOpenAtDesc(Scope type);

List<BetRound> findAllByOrderBySettleAtDesc();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class BettingService {
* 현재 활성화된 베팅 라운드 조회
*/
public Optional<BetRound> getActiveRound(Scope type) {
return betRoundRepository.findByStatusTrueAndScope(type);
return betRoundRepository.findTopByStatusTrueAndScopeOrderByOpenAtDesc(type);
}

/**
Expand All @@ -51,22 +51,31 @@ public List<BetRound> getAllBetRounds() {
* PriceData 기반 무작위 종목 선택 (기존 Stock 대체)
*/
public PriceResponse getPriceData() {
List<PriceData> priceList = priceDataRepository.findAll();
if (priceList.isEmpty()) {
List<PriceData> allData = priceDataRepository.findAll();
if (allData.isEmpty()) {
throw new CustomException(ErrorCode.STOCK_NOT_FOUND);
}

PriceData price = priceList.get(random.nextInt(priceList.size()));
List<String> tickers = allData.stream()
.map(PriceData::getTicker)
.distinct()
.toList();

String randomTicker = tickers.get(random.nextInt(tickers.size()));

PriceData latest = priceDataRepository.findTopByTickerOrderByDateDesc(randomTicker)
.orElseThrow(() -> new CustomException(ErrorCode.STOCK_NOT_FOUND));
Comment on lines +54 to +67
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

성능 문제: 전체 데이터 로드를 피하세요.

현재 구현은 findAll()로 모든 PriceData를 메모리에 로드한 후 distinct ticker를 추출합니다. PriceData 테이블에 수천~수만 개의 레코드가 있는 경우 심각한 성능 저하가 발생할 수 있습니다.

다음과 같이 개선하는 것을 권장합니다:

방법 1: Repository에 distinct ticker 조회 메서드 추가 (권장)

PriceDataRepository에 메서드 추가:

@Query("SELECT DISTINCT p.ticker FROM PriceData p")
List<String> findDistinctTickers();

그 후 서비스 코드 수정:

-    List<PriceData> allData = priceDataRepository.findAll();
-    if (allData.isEmpty()) {
+    List<String> tickers = priceDataRepository.findDistinctTickers();
+    if (tickers.isEmpty()) {
         throw new CustomException(ErrorCode.STOCK_NOT_FOUND);
     }

-    List<String> tickers = allData.stream()
-            .map(PriceData::getTicker)
-            .distinct()
-            .toList();
-
     String randomTicker = tickers.get(random.nextInt(tickers.size()));

방법 2: Native Query 사용

@Query(value = "SELECT DISTINCT ticker FROM price_data", nativeQuery = true)
List<String> findDistinctTickers();
🤖 Prompt for AI Agents
In
backend/src/main/java/org/sejongisc/backend/betting/service/BettingService.java
around lines 54 to 67, avoid loading all PriceData via findAll() — add a
repository method to fetch distinct tickers (either JPQL or native query) and
call that instead, then pick a random ticker from the returned list and query
for the latest PriceData for that ticker; update the service to use the new
repository method (handle empty result with the same CustomException) so only
ticker values are loaded into memory rather than all PriceData rows.


return PriceResponse.builder()
.name(price.getTicker())
.symbol(price.getTicker())
.name(latest.getTicker())
.symbol(latest.getTicker())
.market(MarketType.US)
.previousClosePrice(price.getClosePrice())
.settleClosePrice(price.getAdjustedClose())
.previousClosePrice(latest.getClosePrice())
.settleClosePrice(latest.getAdjustedClose())
.build();
}


/**
* 무료 베팅 가능 여부 (20% 확률)
*/
Expand Down Expand Up @@ -199,7 +208,7 @@ public void settleUserBets() {

for (BetRound round : activeRounds) {
// PriceData를 이용해 시세 조회
Optional<PriceData> priceOpt = priceDataRepository.findFirstByTickerOrderByDateDesc(round.getSymbol());
Optional<PriceData> priceOpt = priceDataRepository.findTopByTickerOrderByDateDesc(round.getSymbol());
if (priceOpt.isEmpty()) continue;

PriceData price = priceOpt.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
public interface PriceDataRepository extends JpaRepository<PriceData, PriceDataId> {
List<PriceData> findByTickerAndDateBetweenOrderByDateAsc(String ticker, LocalDate startDate, LocalDate endDate);
List<PriceData> findByTicker(String ticker);
Optional<PriceData> findFirstByTickerOrderByDateDesc(String ticker);
Optional<PriceData> findTopByTickerOrderByDateDesc(String ticker);
}