Skip to content

[FE] SISC1-30 [FEAT] : 베팅 API 연동#131

Merged
DongEun02 merged 9 commits intomainfrom
SISC1-30-FE-user-bets
Nov 24, 2025
Merged

[FE] SISC1-30 [FEAT] : 베팅 API 연동#131
DongEun02 merged 9 commits intomainfrom
SISC1-30-FE-user-bets

Conversation

@DongEun02
Copy link
Contributor

@DongEun02 DongEun02 commented Nov 22, 2025

1) 작업한 이슈번호

SISC1-30

2) 변경 요약 (What & Why)

  • 무엇을 변경했는지: 베팅, 베팅 이력 API 연동
  • 변경했는지(문제/목표):

3) 스크린샷/동영상 (UI 변경 시)

전/후 비교, 반응형(모바일/데스크톱) 캡쳐

  • Before:
  • After:
image image

4) 상세 변경사항 (전부 다)

  • 라우팅/페이지:
  • 컴포넌트:
  • 상태관리:
  • API 호출:
  • 스타일:
  • 기타:

5) 참고사항

베팅 가능 기간이 아닌 경우에 베팅 시 409 에러를 받아 베팅 기간이 아니라는 알림 출력 예정

Summary by CodeRabbit

  • New Features

    • 베팅 참여/취소 API 연동 및 충돌(중복) 시 사용자 경고 추가
    • 베팅 이력 실시간 조회 및 UI 연동 강화 — 기간별 이력 표시 개선
    • 베팅 상태 동기화로 현재 베팅 여부 UI에 정확히 반영
  • Style

    • 헤더 메뉴 버튼 레이아웃 안정화(버튼 바 전체 폭 유지)
    • 베팅 이력 페이지네이션 중앙 정렬 적용
  • Bug Fixes

    • 이력 항목의 포인트·날짜·상승/하락 표기 및 카운트 표시 오류 수정

✏️ Tip: You can customize this high-level summary in your review settings.

@DongEun02 DongEun02 requested a review from gxuoo as a code owner November 22, 2025 14:19
@coderabbitai
Copy link

coderabbitai bot commented Nov 22, 2025

Walkthrough

베팅 컴포넌트를 DailyBetting에서 Betting으로 리네이밍하고, 사용자 베팅 히스토리 로드와 /api/user-bets에 대한 POST/DELETE 호출(에러 및 409 처리 포함)을 추가했으며, 히스토리 필드 매핑과 일부 CSS(헤더·히스토리 페이징) 정렬/스타일을 조정했습니다.

Changes

Cohort / File(s) 변경 요약
헤더 스타일
frontend/src/components/Header.module.css
.menuButtonflex-shrink: 0 추가 및 .menuButton span { width: 100% } 추가; .header 스타일 블록 뒤에 빈 줄 삽입
베팅 컴포넌트 (리네임 & API 통합)
frontend/src/components/stockgame/Betting.jsx
컴포넌트명 DailyBettingBetting; userBets 상태 추가 및 period 기반 getDailyBetHistory/getWeeklyBetHistory 로드; RISE/FALL 베팅에 대한 POST /api/user-bets 호출과 DELETE /api/user-bets/{id} 추가; async 핸들러, try/catch, 409 처리 및 UI 동기화(effect) 도입; 로딩 가드 유지
베팅 히스토리 UI 매핑
frontend/src/components/stockgame/BettingHistory.jsx
item.collectitem.correct, item.pointsitem.earnedPoints 등 필드명 교체; item.round.* 사용을 item.*(예: roundTitle, symbol, settleClosePrice, previousClosePrice)로 변경; resultOption 기반 베팅 카운트/레이블 및 아이콘/텍스트 조정
베팅 히스토리 스타일
frontend/src/components/stockgame/BettingHistory.module.css
페이징의 margin-left: 230px 제거(주석으로 보관) 및 justify-content: center로 중앙 정렬 변경
유틸리티 필터 및 응답 처리
frontend/src/utils/bettingHistory.js
API 응답에서 res.data 반환 명시; 오류 시 null 반환; getDailyBetHistory/getWeeklyBetHistory가 `item.roundTitle.includes('DAILY'

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant UI as Betting.jsx
    participant API as /api/user-bets
    participant Hist as bettingHistory.js / BettingHistory.jsx

    rect rgb(240,249,255)
    User->>UI: 베팅 버튼 클릭 (RISE/FALL)
    activate UI
    UI->>API: POST /api/user-bets (async)
    activate API
    API-->>UI: 응답 (성공 / 409 / 오류)
    deactivate API
    UI->>UI: userBets 업데이트, 에러/알림 처리
    UI-->>User: 상태/알림 표시
    deactivate UI
    end

    rect rgb(255,247,240)
    User->>UI: 히스토리 조회 (period)
    UI->>Hist: getDailyBetHistory / getWeeklyBetHistory
    activate Hist
    Hist-->>UI: 필터된 히스토리 (roundTitle.includes)
    deactivate Hist
    UI-->>User: BettingHistory 렌더링 (earnedPoints, resultOption 등)
    end

    rect rgb(247,255,240)
    User->>UI: 베팅 취소 요청
    activate UI
    UI->>API: DELETE /api/user-bets/{userBetId}
    activate API
    API-->>UI: 삭제 응답
    deactivate API
    UI->>UI: userBets 동기화
    UI-->>User: 취소 결과 표시
    deactivate UI
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • 집중 검토 항목:
    • Betting.jsx의 POST/DELETE 비동기 흐름, 409 처리 및 사용자 알림 로직
    • userBets와 UI 동기화(effect) 경계 조건(동시성, 중복 업데이트)
    • BettingHistory.jsx의 필드 매핑이 실제 API 응답 스펙과 일치하는지 (nullable/형식)
    • bettingHistory.jsroundTitle.includes 필터의 케이스/포맷 민감도

Possibly related PRs

Suggested reviewers

  • gxuoo

Poem

🐰 밤밭을 뛰어와 키보드 톡톡
이름은 Betting, 기록은 반짝반짝 쌓여
POST로 심음, DELETE로 뽑아도 괜찮아
earnedPoints는 반짝, roundTitle은 노래해
당근 들고 축하의 깡총춤을! 🥕🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 베팅 API 연동이라는 주요 변경사항을 명확하게 설명하며, 실제 변경 내용(Betting.jsx의 API 통합, BettingHistory.jsx의 데이터 구조 업데이트 등)과 일치합니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch SISC1-30-FE-user-bets

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82134a1 and 5ad9eae.

📒 Files selected for processing (1)
  • frontend/src/utils/bettingHistory.js (2 hunks)
🔇 Additional comments (1)
frontend/src/utils/bettingHistory.js (1)

6-6: 주석 개선이 적절합니다.

백엔드에서 정렬을 처리한다는 점을 명확히 한 주석 수정이 이전 리뷰 피드백을 잘 반영했습니다.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/stockgame/BettingHistory.jsx (1)

57-59: 0으로 나누기 예외 처리 누락

prev(이전 가격)가 0일 경우 Infinity 또는 NaN이 반환되어 UI에 잘못된 값이 표시될 수 있습니다.

다음과 같이 수정하세요:

   const getPercentChange = (prev, next) => {
+    if (prev === 0) return '0.00';
     return (((next - prev) / prev) * 100).toFixed(2);
   };
🧹 Nitpick comments (1)
frontend/src/components/stockgame/BettingHistory.jsx (1)

102-106: 데이터 필드 존재 여부 확인 권장

item.upBetCountitem.downBetCount에 접근할 때 해당 필드가 존재하지 않으면 undefined가 표시될 수 있습니다. 백엔드 API가 항상 이 필드를 반환한다고 보장되면 문제없지만, 방어적 코딩을 위해 기본값 처리를 권장합니다.

다음과 같이 수정하는 것을 고려하세요:

               <span>
                 {item.resultOption === 'RISE'
-                  ? item.upBetCount
-                  : item.downBetCount}
+                  ? (item.upBetCount ?? 0)
+                  : (item.downBetCount ?? 0)}
                 명
               </span>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab1004c and 2daa0bd.

📒 Files selected for processing (5)
  • frontend/src/components/Header.module.css (2 hunks)
  • frontend/src/components/stockgame/Betting.jsx (4 hunks)
  • frontend/src/components/stockgame/BettingHistory.jsx (2 hunks)
  • frontend/src/components/stockgame/BettingHistory.module.css (1 hunks)
  • frontend/src/utils/bettingHistory.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
frontend/src/components/stockgame/Betting.jsx (2)
frontend/src/utils/bettingHistory.js (7)
  • data (14-14)
  • data (20-20)
  • res (5-5)
  • getDailyBetHistory (13-17)
  • getDailyBetHistory (13-17)
  • getWeeklyBetHistory (19-23)
  • getWeeklyBetHistory (19-23)
frontend/src/utils/bettingInfo.js (6)
  • res (5-5)
  • res (15-15)
  • dailyBet (3-11)
  • dailyBet (3-11)
  • weeklyBet (13-21)
  • weeklyBet (13-21)
frontend/src/components/stockgame/BettingHistory.jsx (1)
frontend/src/components/stockgame/StockInfoItem.jsx (1)
  • StockInfoItem (3-10)
frontend/src/utils/bettingHistory.js (2)
frontend/src/components/stockgame/Betting.jsx (1)
  • data (14-14)
frontend/src/components/stockgame/BettingHistory.jsx (1)
  • data (12-12)
🔇 Additional comments (7)
frontend/src/components/Header.module.css (2)

15-15: 깔끔한 코드 포맷팅

스타일 블록 사이의 공백 추가로 가독성을 개선했습니다.


41-41: 반응형 레이아웃 개선 - 좋은 CSS 수정

.menuButtonflex-shrink: 0을 추가하여 고정 크기(22px × 16px)를 유지하도록 하고, .menuButton spanwidth: 100%를 적용하여 라인들이 버튼 전체 너비를 일관되게 채우도록 개선했습니다. 이는 베팅 UI 통합에 따른 레이아웃 변경에서 헤더 버튼의 반응형 동작을 안정화하는 좋은 개선입니다.

Also applies to: 46-46

frontend/src/components/stockgame/BettingHistory.module.css (1)

132-133: 페이지네이션 중앙 정렬 변경 승인

페이지네이션을 중앙 정렬로 변경한 것은 적절한 UI 개선입니다.

frontend/src/components/stockgame/Betting.jsx (2)

53-58: 하드코딩된 베팅 파라미터 확인 필요

stakePoints: 0isFree: true가 하드코딩되어 있습니다. 향후 유료 베팅이나 포인트 베팅 기능을 추가할 계획이 있다면 이를 props나 state로 관리하는 것이 좋습니다.

현재 요구사항에서 모든 베팅이 무료이고 포인트가 0인 것이 맞는지 확인해주세요. 향후 확장 가능성이 있다면 다음과 같이 리팩토링을 고려하세요:

const onClickUpBet = async (stakePoints = 0, isFree = true) => {
  // ...
  const res = await api.post('/api/user-bets', {
    roundId: data.betRoundId,
    option: 'RISE',
    stakePoints,
    isFree,
  });
  // ...
};

35-46: 베팅 상태 동기화 로직 승인

datauserBets의 변경을 감지하여 베팅 상태를 자동으로 동기화하는 로직이 잘 구현되었습니다. null 체크와 betRoundId 비교를 통해 안전하게 처리하고 있습니다.

frontend/src/components/stockgame/BettingHistory.jsx (2)

72-78: 데이터 구조 변경 반영 완료

item.collect에서 item.correct로의 변경과 결과 아이콘 렌더링이 올바르게 구현되었습니다.


79-93: API 응답 데이터 구조 변경 반영 완료

중첩된 item.round.* 구조에서 플랫한 item.* 구조로 변경이 잘 적용되었습니다. roundTitle, symbol, settleClosePrice, previousClosePrice 필드가 올바르게 사용되고 있습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (5)
frontend/src/utils/bettingHistory.js (2)

17-17: 문자열 부분 매칭 방식의 필터링 개선 필요

includes('DAILY')는 "DAILY"가 포함된 모든 문자열과 매칭됩니다. 예를 들어 "MY_DAILY_SPECIAL" 같은 roundTitle도 잘못 매칭될 수 있습니다.

이전 리뷰에서 제안한 대로 다음과 같이 수정하는 것을 권장합니다:

-  return data.filter((item) => item.roundTitle.includes('DAILY'));
+  return data.filter((item) => item.roundTitle === 'DAILY' || item.roundTitle.startsWith('DAILY_'));

23-23: 문자열 부분 매칭 방식의 필터링 개선 필요

Line 17과 동일한 이슈입니다. includes('WEEKLY')는 "WEEKLY"가 포함된 모든 문자열과 매칭되므로 더 정확한 매칭 방식이 필요합니다.

다음과 같이 수정하는 것을 권장합니다:

-  return data.filter((item) => item.roundTitle.includes('WEEKLY'));
+  return data.filter((item) => item.roundTitle === 'WEEKLY' || item.roundTitle.startsWith('WEEKLY_'));
frontend/src/components/stockgame/Betting.jsx (3)

59-83: Axios 에러 객체 접근 방식을 수정해야 합니다.

Line 75에서 error.status로 접근하고 있지만, Axios는 HTTP 상태 코드를 error.response.status에 저장합니다. 현재 코드는 409 에러를 제대로 감지하지 못합니다.

다음과 같이 수정하세요:

       } catch (error) {
         // 409 처리
-        if (error.status === 409) {
+        if (error.response?.status === 409) {
           alert('베팅 가능 시간이 아닙니다.');
           return;
         }
         alert('오류가 발생했습니다. 다시 시도해주세요.');
         return null;
       }

85-109: Axios 에러 객체 접근 방식을 수정해야 합니다.

Line 101에서 Line 75와 동일한 문제가 있습니다. error.status 대신 error.response?.status를 사용해야 합니다.

다음과 같이 수정하세요:

       } catch (error) {
         // 409 처리
-        if (error.status === 409) {
+        if (error.response?.status === 409) {
           alert('베팅 가능 시간이 아닙니다.');
           return;
         }
         alert('오류가 발생했습니다. 다시 시도해주세요.');
         return null;
       }

111-121: 사용자에게 원시 에러 메시지 노출을 피하세요.

Line 118에서 error.message를 직접 표시하고 있습니다. 이는 기술적인 세부사항을 노출하여 사용자 경험을 해칠 수 있습니다.

다음과 같이 수정하는 것을 권장합니다:

       } catch (error) {
-        alert(error.message);
+        console.error(error);
+        alert('베팅 취소 중 오류가 발생했습니다. 다시 시도해주세요.');
       }
🧹 Nitpick comments (2)
frontend/src/utils/bettingHistory.js (1)

6-7: 원본 배열 변경을 피하기 위해 비파괴적 방식 사용을 권장합니다.

reverse()는 원본 배열을 변경(mutate)합니다. res.data가 다른 곳에서도 참조될 가능성이 있다면 예상치 못한 부작용이 발생할 수 있습니다.

다음과 같이 수정하는 것을 권장합니다:

-    // 최신순으로 정렬
-    return res.data.reverse();
+    // 최신순으로 정렬
+    return [...res.data].reverse();

또는 React 19를 사용 중이므로 toReversed()를 사용할 수도 있습니다:

-    return res.data.reverse();
+    return res.data.toReversed();
frontend/src/components/stockgame/Betting.jsx (1)

59-109: 상승/하락 베팅 핸들러의 코드 중복을 제거하는 것을 고려하세요.

onClickUpBetonClickDownBet은 거의 동일한 로직을 가지고 있으며, option 값과 메시지만 다릅니다. 공통 함수로 리팩토링하면 유지보수성이 향상됩니다.

다음과 같이 리팩토링할 수 있습니다:

const handleBet = async (option, direction) => {
  if (isBetting !== 'none') {
    alert('이미 베팅하셨습니다.');
  } else if (confirm(`${direction}에 베팅하시겠습니까?`)) {
    try {
      const res = await api.post('/api/user-bets', {
        roundId: data.betRoundId,
        option,
        stakePoints: 0,
        isFree: true,
      });
      await update();
      alert('베팅이 완료되었습니다.');
      return res;
    } catch (error) {
      if (error.response?.status === 409) {
        alert('베팅 가능 시간이 아닙니다.');
        return;
      }
      alert('오류가 발생했습니다. 다시 시도해주세요.');
      return null;
    }
  }
};

const onClickUpBet = () => handleBet('RISE', '상승');
const onClickDownBet = () => handleBet('FALL', '하락');
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58a21dd and 9c05559.

📒 Files selected for processing (2)
  • frontend/src/components/stockgame/Betting.jsx (4 hunks)
  • frontend/src/utils/bettingHistory.js (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/components/stockgame/Betting.jsx (3)
frontend/src/utils/bettingHistory.js (7)
  • data (15-15)
  • data (21-21)
  • res (5-5)
  • getDailyBetHistory (14-18)
  • getDailyBetHistory (14-18)
  • getWeeklyBetHistory (20-24)
  • getWeeklyBetHistory (20-24)
frontend/src/components/stockgame/BettingHistory.jsx (2)
  • data (12-12)
  • error (15-15)
frontend/src/utils/bettingInfo.js (6)
  • res (5-5)
  • res (15-15)
  • dailyBet (3-11)
  • dailyBet (3-11)
  • weeklyBet (13-21)
  • weeklyBet (13-21)
frontend/src/utils/bettingHistory.js (1)
frontend/src/utils/bettingInfo.js (2)
  • res (5-5)
  • res (15-15)
🔇 Additional comments (3)
frontend/src/components/stockgame/Betting.jsx (3)

22-23: 배열 안전성 검사가 올바르게 추가되었습니다.

이전 리뷰에서 지적된 배열 접근 시 안전성 문제가 적절히 해결되었습니다. history && history.length > 0 조건으로 빈 배열 접근을 방지하고 있습니다.

Also applies to: 27-28


34-46: 베팅 상태 동기화 로직이 잘 구현되었습니다.

datauserBets의 변경을 감지하여 isBetting 상태를 동기화하는 로직이 명확하게 구현되었습니다. null 체크와 betRoundId 비교를 통해 현재 라운드의 베팅 여부를 정확히 판단하고 있습니다.


48-57: 데이터 갱신 로직을 별도 함수로 분리한 것이 좋은 개선입니다.

update() 함수로 데이터와 베팅 히스토리 갱신 로직을 중앙화하여 코드 중복을 제거했습니다. 이전 리뷰에서 요청한 데이터 동기화 문제도 해결되었습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
frontend/src/utils/bettingHistory.js (2)

17-17: 문자열 부분 매칭과 null 안전성 문제

이전 리뷰에서 지적된 .includes('DAILY') 사용 이슈가 여전히 해결되지 않았습니다. 또한 item.roundTitlenull 또는 undefined일 경우 런타임 에러가 발생할 수 있습니다.

다음과 같이 수정하여 두 문제를 모두 해결하세요:

-  return data.filter((item) => item.roundTitle.includes('DAILY'));
+  return data.filter((item) => item.roundTitle?.startsWith('DAILY_') || item.roundTitle === 'DAILY');

23-23: 문자열 부분 매칭과 null 안전성 문제

Line 17과 동일한 이슈입니다. 이전 리뷰에서 지적된 .includes('WEEKLY') 사용 이슈가 여전히 해결되지 않았으며, item.roundTitle에 대한 null 체크도 누락되었습니다.

다음과 같이 수정하세요:

-  return data.filter((item) => item.roundTitle.includes('WEEKLY'));
+  return data.filter((item) => item.roundTitle?.startsWith('WEEKLY_') || item.roundTitle === 'WEEKLY');
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c05559 and 82134a1.

📒 Files selected for processing (1)
  • frontend/src/utils/bettingHistory.js (2 hunks)

Copy link
Contributor

@gxuoo gxuoo left a comment

Choose a reason for hiding this comment

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

잘 돌아가네요 고생하셨습니다! 👍👍

@DongEun02 DongEun02 merged commit 46b33ee into main Nov 24, 2025
1 check passed
@DongEun02 DongEun02 deleted the SISC1-30-FE-user-bets branch November 24, 2025 06:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants