Skip to content

[BE] [FIX] 포인트 예외 처리 및 모의트레이딩 재베팅 로직 수정#183

Merged
Yooonjeong merged 7 commits intomainfrom
20260115_#182_포인트_예외_처리_및_모의트레이딩_재베팅_로직_수정
Jan 18, 2026

Hidden character warning

The head ref may contain hidden characters: "20260115_#182_\ud3ec\uc778\ud2b8_\uc608\uc678_\ucc98\ub9ac_\ubc0f_\ubaa8\uc758\ud2b8\ub808\uc774\ub529_\uc7ac\ubca0\ud305_\ub85c\uc9c1_\uc218\uc815"
Merged

[BE] [FIX] 포인트 예외 처리 및 모의트레이딩 재베팅 로직 수정#183
Yooonjeong merged 7 commits intomainfrom
20260115_#182_포인트_예외_처리_및_모의트레이딩_재베팅_로직_수정

Conversation

@Yooonjeong
Copy link
Contributor

@Yooonjeong Yooonjeong commented Jan 15, 2026

Summary by CodeRabbit

릴리즈 노트

  • 새로운 기능

    • 삭제된 베팅 이후 동일한 라운드에 재베팅 가능하도록 개선
    • 활성 라운드 조회 기능 추가
  • 버그 수정

    • 베팅 상태 관리 개선으로 중복 베팅 오류 처리 최적화
    • 예외 처리 및 재시도 로직 강화
  • 보안

    • 사용자 베팅 데이터 엔드포인트에 인증 요구사항 추가
  • 정리

    • 주석 및 불필요한 코드 정리

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

개요

베팅 시스템의 상태 관리를 개선하기 위한 변경 사항입니다. CANCELED 상태를 제거하고, 기존 베팅을 업데이트하는 기능을 추가하며, 저장소 조회 방식을 변경하고, 인증 요구 사항을 강화합니다.

변경 사항

집합 / 파일(들) 변경 요약
베팅 엔티티 변경
backend/src/main/java/org/sejongisc/backend/betting/entity/BetRound.java, backend/src/main/java/org/sejongisc/backend/betting/entity/BetStatus.java, backend/src/main/java/org/sejongisc/backend/betting/entity/UserBet.java
BetRound의 주석 정정, BetStatus에서 CANCELED 상수 제거, UserBet에 updateBet 메서드 추가(베팅 재설정 시 기존 레코드 업데이트 지원)
저장소 및 서비스 계층
backend/src/main/java/org/sejongisc/backend/betting/repository/UserBetRepository.java, backend/src/main/java/org/sejongisc/backend/betting/service/BettingService.java
UserBetRepository에서 existsByRoundAndUserId를 findByRoundAndUserId로 변경, BettingService의 postUserBet이 DELETED 상태인 이전 베팅 재사용 지원, getActiveRoundResponse 메서드 추가
인증 및 스케줄러
backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java, backend/src/main/java/org/sejongisc/backend/betting/service/BettingScheduler.java
SecurityConfig에서 "/api/user-bets/**" 엔드포인트 인증 필수화, BettingScheduler의 미사용 임포트 제거
포인트 서비스
backend/src/main/java/org/sejongisc/backend/point/service/PointHistoryService.java
CustomException 처리를 위한 @Recover 메서드 추가(재시도 불가능한 비즈니스 예외 즉시 전파)

예상 코드 리뷰 난이도

🎯 3 (중간 난이도) | ⏱️ ~25 분

관련 이슈

관련 PR

추천 검토자

  • discipline24
  • msciki7

축시

🐰 옛날 취소 상태는 가고,
다시 베팅할 수 있게 되었네!
지워진 것도 다시 쓸 수 있어,
베팅의 새로운 장이 시작되는 듯 해.
보안도 강화되고, 포인트도 튼튼해,
우리 시스템, 점점 더 똑똑해진다! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경사항의 주요 내용을 정확하게 반영합니다. 포인트 예외 처리와 모의트레이딩 재베팅 로직 수정이 코드 변경사항과 일치합니다.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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: 1

🤖 Fix all issues with AI agents
In
`@backend/src/main/java/org/sejongisc/backend/point/service/PointHistoryService.java`:
- Around line 86-94: The recover(CustomException e) `@Recover` method in
PointHistoryService will never be invoked because the `@Retryable` is configured
with include = {OptimisticLockingFailureException.class}, so CustomException is
not a retryable exception; either remove this dead recover(CustomException e)
method or replace/add a `@Recover` method that matches the retryable exception
(e.g., recover(OptimisticLockingFailureException e) or recover(Exception e) if
you want a catch-all) so that Spring Retry will call it after retries are
exhausted; if the intent is to let CustomException propagate immediately, simply
delete recover(CustomException e).
🧹 Nitpick comments (2)
backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java (1)

109-116: 인증 엔드포인트 추가는 적절합니다.

/api/user-bets/** 패턴이 인증 필수 엔드포인트로 추가되어 베팅 관련 API가 보호됩니다.

다만, Line 116의 .anyRequest().permitAll()이 활성화되어 있어 명시적으로 지정되지 않은 모든 엔드포인트가 공개됩니다. 향후 새로운 API 추가 시 인증 설정을 누락할 수 있으니, 운영 환경에서는 .anyRequest().authenticated()로 변경하고 공개 API만 명시적으로 permitAll()하는 방식을 권장합니다.

backend/src/main/java/org/sejongisc/backend/betting/entity/UserBet.java (1)

78-86: 재베팅 로직이 적절하게 구현되었습니다.

updateBet 메서드가 베팅 옵션, 포인트, 무료 여부를 업데이트하고 상태를 초기화하는 로직이 잘 구현되어 있습니다.

방어적 코딩 관점에서, 현재 상태가 DELETED일 때만 재베팅이 가능하도록 엔티티 레벨에서 검증을 추가하면 서비스 레이어의 버그로 인한 의도치 않은 상태 변경을 방지할 수 있습니다.

♻️ 상태 검증 추가 제안 (선택사항)
 // 재베팅 처리
 public void updateBet(BetOption option, Integer stakePoints, boolean isFree) {
+    if (this.betStatus != BetStatus.DELETED) {
+        throw new IllegalStateException("재베팅은 DELETED 상태에서만 가능합니다.");
+    }
     this.option = option;
     this.stakePoints = stakePoints;
     this.isFree = isFree;
     this.betStatus = BetStatus.ACTIVE;
     this.isCorrect = false;
     this.payoutPoints = null;
 }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccee132 and 92918b7.

📒 Files selected for processing (8)
  • backend/src/main/java/org/sejongisc/backend/betting/entity/BetRound.java
  • backend/src/main/java/org/sejongisc/backend/betting/entity/BetStatus.java
  • backend/src/main/java/org/sejongisc/backend/betting/entity/UserBet.java
  • backend/src/main/java/org/sejongisc/backend/betting/repository/UserBetRepository.java
  • backend/src/main/java/org/sejongisc/backend/betting/service/BettingScheduler.java
  • backend/src/main/java/org/sejongisc/backend/betting/service/BettingService.java
  • backend/src/main/java/org/sejongisc/backend/common/auth/config/SecurityConfig.java
  • backend/src/main/java/org/sejongisc/backend/point/service/PointHistoryService.java
💤 Files with no reviewable changes (2)
  • backend/src/main/java/org/sejongisc/backend/betting/entity/BetStatus.java
  • backend/src/main/java/org/sejongisc/backend/betting/service/BettingScheduler.java
🔇 Additional comments (8)
backend/src/main/java/org/sejongisc/backend/betting/entity/BetRound.java (1)

129-134: LGTM!

주석의 오타가 제거되었습니다. 기능적 변경 없이 코드 정리만 수행되었습니다.

backend/src/main/java/org/sejongisc/backend/betting/repository/UserBetRepository.java (1)

12-12: LGTM!

기존 existsByRoundAndUserId 대신 findByRoundAndUserId를 사용하여 재베팅 로직에서 기존 베팅을 조회하고 업데이트할 수 있도록 변경되었습니다. Spring Data JPA 네이밍 컨벤션을 올바르게 따르고 있습니다.

backend/src/main/java/org/sejongisc/backend/betting/entity/UserBet.java (1)

73-76: LGTM!

cancel() 메서드의 주석이 추가되어 가독성이 향상되었습니다.

backend/src/main/java/org/sejongisc/backend/betting/service/BettingService.java (5)

163-168: 재베팅 시 통계 일관성 검증

재베팅 경로(기존 DELETED 상태 베팅이 있는 경우)에서도 통계가 증가합니다. cancelUserBet 메서드에서는 취소 시 통계를 차감하지만, DELETED 상태로 변경되는 경로에서도 동일하게 통계가 차감되는지 확인이 필요합니다.

만약 DELETED 상태 전환 시 통계가 차감되지 않았다면, 재베팅 시 중복으로 통계가 증가할 수 있습니다.


202-207: 예외 처리 구조 적절함

DataIntegrityViolationException 처리를 통해 동시 베팅 요청에 대한 DB 레벨 보호가 구현되어 있습니다. @Transactional 어노테이션이 있으므로 예외 발생 시 통계 증가와 포인트 차감도 함께 롤백됩니다.


209-213: LGTM!

getActiveRoundResponse 메서드는 Optional.map을 사용하여 엔티티를 DTO로 깔끔하게 변환합니다. Controller에서 LazyInitializationException을 방지하기 위한 적절한 접근입니다.


185-200: 재베팅 시 updateBet 메서드가 올바르게 상태를 ACTIVE로 설정하고 있습니다.

UserBet.updateBet() 메서드(라인 79-86)에서 this.betStatus = BetStatus.ACTIVE;로 명시적으로 상태를 설정하며, isCorrect를 false로 초기화하고 payoutPoints를 null로 초기화하여 재베팅에 필요한 모든 상태 변경을 정확하게 처리하고 있습니다.


149-155: 상태 검증 결과: 코드가 올바르게 구현되었습니다

BetStatus 열거형에는 ACTIVE, DELETED, CLOSED만 존재하며, cancel() 메서드는 상태를 정확히 BetStatus.DELETED로 설정합니다. 주석(line 141)에 명시된 "취소 이력이 있는 베팅도 재배팅 가능"이라는 의도와 코드(line 153)의 BetStatus.DELETED 확인이 일치하고 있습니다. updateBet() 메서드에서 상태를 BetStatus.ACTIVE로 변경하여 재베팅이 정상적으로 처리되므로, 현재 구현에는 문제가 없습니다.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@discipline24 discipline24 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다~ 토끼 리뷰만 손보면 될거같네요.
PR 올린 후 백엔드 카톡방에 코드리뷰 요청해주세요~

@Yooonjeong
Copy link
Contributor Author

@discipline24 넹~

토끼 리뷰는 디버깅 해보고 찾아봤을 때 실제 동작과 다른 것 같아서 반영 안 했습니다! 확인 한번만 부탁드려용
include에 없는 Exception 발생 시 재시도만 안 할 뿐 인터셉터에 의해 @Recover 메서드가 실행되어야 하는데 해당 메서드가 없어서 500 에러가 발생하던 이슈를 해결하기 위해 추가한 내용입니다

아래에 관련 내용 첨부할게용


@Retryable 애스팩트가 붙은 메서드

  • 해당 메서드를 포함하는 클래스는 프록시 객체가 빈으로 등록됨
  • createPointHistory 메서드 호출 시 비즈니스 로직을 실행하다가 에러 발생 시
    • include에 정의된 에러는 정의된 재시도 횟수만큼 재시도, 횟수를 넘어가면 @Recover 메서드를 찾아 실행
    • 정의되지 않은 에러는 즉시 @Recover 메서드 찾아 실행
      → 이때 @Recover 메서드가 정의되어 있지 않으면 실제 에러 대신 Cannot locate recovery method 500 에러 반환
  • 실제 비즈니스 로직 에러를 명확히 반환하려면 CustomException 발생 시에도 실행할 @Recover 메서드를 정의해야 함

+) @RetryableexcludeCustomException을 포함해도 동일한 500 에러가 발생하는 이유

  • exclude는 재시도 여부만 판단하는 것
  • @Retryable이 적용된 메서드는 이미 인터셉터 내부에서 실행되므로, 재시도 대상이 아닌 예외가 발생하더라도 @Recover 메서드를 탐색함

@cksdid202
Copy link
Contributor

cksdid202 commented Jan 16, 2026

오 역시. 고생하셨습니다 @Recover 메서드는 필요한 게 맞는 것 같아요!

@Yooonjeong Yooonjeong merged commit bc67e9d into main Jan 18, 2026
1 check passed
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.

[기능개선][BE] 포인트 예외 처리 및 모의트레이딩 재베팅 로직 수정

3 participants