Skip to content

fix: 실시간 데이터 반영 완료 - #68

Merged
Kim-Jaehyun0328 merged 1 commit into
devfrom
feat/kafka
Aug 18, 2025
Merged

fix: 실시간 데이터 반영 완료#68
Kim-Jaehyun0328 merged 1 commit into
devfrom
feat/kafka

Conversation

@Kim-Jaehyun0328

@Kim-Jaehyun0328 Kim-Jaehyun0328 commented Aug 18, 2025

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • 버그 수정

    • 실시간 경기 진행/점수 갱신 정확도와 안정성 개선(시작/종료/분/스코어 비교 보강)
    • 선수 데이터 동기화 시 식별자 정합성 강화로 누락·중복 및 삭제 처리 오류 감소
    • 드림팀 여부 기본값을 명확히 적용해 표시 오류 방지
  • 변경

    • 경기 이벤트 알림 발송을 일시 중단
  • 리팩터링

    • 라이브 데이터 구조를 일관된 형태로 통합하여 라이브 화면·통계 갱신의 신뢰성 및 성능 개선

@coderabbitai

coderabbitai Bot commented Aug 18, 2025

Copy link
Copy Markdown

Walkthrough

플레이어 식별자를 code에서 fplId로 전환하고 이에 맞춰 리포지토리/스케줄러 로직을 수정했다. 라이브 피처 흐름은 LiveFixtureDto에서 FplFixture로 교체되었고, 관련 DTO/서비스/엔티티 메서드 시그니처가 업데이트되었다. 일부 필드 매핑과 불리언 컬럼(nullable→false) 및 JSON 프로퍼티명이 조정되었다.

Changes

Cohort / File(s) Change Summary
Player 식별자 전환(fplId 기반)
backendProject/.../player/entity/Player.java, backendProject/.../player/repository/PlayerRepository.java, backendProject/.../global/scheduler/service/DataUpdaterService.java
Player.updatePlayer에서 code 동기화 추가; 리포지토리 메서드들이 code→fplId 기준으로 변경(조회/삭제); 스케줄러가 fplId를 키로 업서트/삭제하고 신규 삽입 saveAll 경로 추가.
라이브 피처 처리 FplFixture 전환
backendProject/.../round/entity/Fixture.java, backendProject/.../global/scheduler/service/LiveDataService.java, backendProject/.../global/staticdata/dto/live/LiveEventDto.java
Fixture.updateLiveFixture가 FplFixture를 수용하도록 시그니처/필드 접근자 교체; LiveDataService 전반이 List로 동작하도록 리팩터; 상태 비교(null-safe)와 맵핑 키를 fplId로 변경; 알림 전송 주석 처리; LiveEventDto.fixtures 타입을 FplFixture로 변경.
라이브 DTO 매핑 조정
backendProject/.../global/staticdata/dto/live/LiveElementDto.java
@JsonProperty가 "element"→"id"로 변경(필드 구조 동일).
통계 엔티티 불리언 비null화
backendProject/.../player/entity/live/PlayerFixtureStat.java
inDreamteam를 Boolean→boolean으로 변경, 기본값 false, 컬럼 name=in_dreamteam, nullable=false, @Builder.Default 추가.

Sequence Diagram(s)

sequenceDiagram
  participant Scheduler as DataUpdaterService
  participant Repo as PlayerRepository
  participant DB as Database

  Scheduler->>Repo: findAllByFplIdIn(fplIds)
  Repo-->>Scheduler: List<Player>
  Scheduler->>Scheduler: 기존 플레이어 맵(키: fplId) 구성
  Scheduler->>Repo: markDeletedByFplIdNotIn(fplIds)
  Scheduler->>DB: 신규 Player 배치 생성(saveAll)
  DB-->>Scheduler: 저장 완료
Loading
sequenceDiagram
  participant Live as LiveDataService
  participant API as FPL API
  participant Fix as Fixture(엔티티)

  Live->>API: fetchLiveFixtures() -> List<FplFixture>
  Live->>Live: hasNoActiveFixtures(fixtures)?
  alt 변경사항 있음
    Live->>Fix: updateLiveFixture(FplFixture)
  end
  Live->>API: fetchLiveData()
  Live->>Live: processPlayerEvents(liveData, fixtures)<br/>(activeFixtureIds by fplId)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • jaehyeok-code
  • hwichoi0317
  • roode1017

Poem

토끼는 귀 쫑긋, fplId를 품고 뛰네 🐇
라이브 경기, FplFixture 길 따라가고,
꿈팀 표식은 또렷이 false로 시작해,
코드와 맵은 새 키로 다시 짜였지.
딸칵! 저장, 딸칵! 업데이트—
오늘도 데이터 밭에 당근 로그가 열렸네. 🥕

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/kafka

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Kim-Jaehyun0328
Kim-Jaehyun0328 merged commit 3077a51 into dev Aug 18, 2025
1 check was pending
@Kim-Jaehyun0328
Kim-Jaehyun0328 deleted the feat/kafka branch August 18, 2025 02:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🔭 Outside diff range comments (5)
backendProject/src/main/java/likelion/mlb/backendProject/domain/player/entity/Player.java (1)

140-149: 선수 사진 URL의 opta_code ‘p’ 접두어 누락 및 news 컬럼 null 안전성 보완 필요

  • 사진 URL 구성에서 p 접두어가 누락되어 있어 이미지 404 가능성이 큽니다. 코드 상단 주석(“opta_code는 p + code”)과도 불일치합니다.
  • news 컬럼은 DB에서 nullable=false인데, 외부 API가 null을 줄 경우 제약 위반 위험이 있습니다. 빈 문자열로 폴백하세요.

아래처럼 수정 제안드립니다.

-        String picUri = "https://resources.premierleague.com/premierleague25/photos/players/110x140/"
-                + element.getCode() + ".png";
+        String picUri = "https://resources.premierleague.com/premierleague25/photos/players/110x140/p"
+                + element.getCode() + ".png";
         this.pic = picUri;
         this.code = element.getCode();
         this.status = element.getStatus();
         this.fplId = element.getFplId();
-        this.news = element.getNews();
+        this.news = element.getNews() != null ? element.getNews() : "";
         this.teamCode = element.getTeamCode();
         this.chanceOfPlayingNextRound = element.isChanceOfPlayingNextRound();
         this.chanceOfPlayingThisRound = element.isChanceOfPlayingThisRound();

추가로, 동일한 URL 규칙을 사용하는 playerBuilder(FplElement, ...)의 라인 117-124에도 동일한 접두어(p) 적용 검토를 권장합니다.

backendProject/src/main/java/likelion/mlb/backendProject/domain/player/entity/live/PlayerFixtureStat.java (1)

63-118: NPE 위험: Integer 필드 비교에서 오토언박싱 사용 금지

this.minutes != element.getStats().getMinutes()와 같은 비교는 null이 들어오면 NPE가 발생합니다. 라이브 데이터 스트림에 null이 흔해 치명적입니다. Objects.equals(a, b)로 비교 후 변경 시 대입하는 패턴으로 전환하세요.

예시 수정안(패턴 적용):

@@
-        // 모든 스탯 필드 업데이트 체크
-        if (this.minutes != element.getStats().getMinutes()) {
-            this.minutes = element.getStats().getMinutes();
-            hasChanges = true;
-        }
+        // 모든 스탯 필드 업데이트 체크 (null-세이프)
+        var s = element.getStats();
+        if (!java.util.Objects.equals(this.minutes, s.getMinutes())) {
+            this.minutes = s.getMinutes();
+            hasChanges = true;
+        }
-        if (this.goalsScored != element.getStats().getGoalsScored()) {
-            this.goalsScored = element.getStats().getGoalsScored();
+        if (!java.util.Objects.equals(this.goalsScored, s.getGoalsScored())) {
+            this.goalsScored = s.getGoalsScored();
             hasChanges = true;
         }
-        if (this.assists != element.getStats().getAssists()) {
-            this.assists = element.getStats().getAssists();
+        if (!java.util.Objects.equals(this.assists, s.getAssists())) {
+            this.assists = s.getAssists();
             hasChanges = true;
         }
-        if (this.cleanSheets != element.getStats().getCleanSheets()) {
-            this.cleanSheets = element.getStats().getCleanSheets();
+        if (!java.util.Objects.equals(this.cleanSheets, s.getCleanSheets())) {
+            this.cleanSheets = s.getCleanSheets();
             hasChanges = true;
         }
-        if (this.goalsConceded != element.getStats().getGoalsConceded()) {
-            this.goalsConceded = element.getStats().getGoalsConceded();
+        if (!java.util.Objects.equals(this.goalsConceded, s.getGoalsConceded())) {
+            this.goalsConceded = s.getGoalsConceded();
             hasChanges = true;
         }
-        if (this.ownGoals != element.getStats().getOwnGoals()) {
-            this.ownGoals = element.getStats().getOwnGoals();
+        if (!java.util.Objects.equals(this.ownGoals, s.getOwnGoals())) {
+            this.ownGoals = s.getOwnGoals();
             hasChanges = true;
         }
-        if (this.penaltiesSaved != element.getStats().getPenaltiesSaved()) {
-            this.penaltiesSaved = element.getStats().getPenaltiesSaved();
+        if (!java.util.Objects.equals(this.penaltiesSaved, s.getPenaltiesSaved())) {
+            this.penaltiesSaved = s.getPenaltiesSaved();
             hasChanges = true;
         }
-        if (this.penaltiesMissed != element.getStats().getPenaltiesMissed()) {
-            this.penaltiesMissed = element.getStats().getPenaltiesMissed();
+        if (!java.util.Objects.equals(this.penaltiesMissed, s.getPenaltiesMissed())) {
+            this.penaltiesMissed = s.getPenaltiesMissed();
             hasChanges = true;
         }
-        if (this.yellowCards != element.getStats().getYellowCards()) {
-            this.yellowCards = element.getStats().getYellowCards();
+        if (!java.util.Objects.equals(this.yellowCards, s.getYellowCards())) {
+            this.yellowCards = s.getYellowCards();
             hasChanges = true;
         }
-        if (this.redCards != element.getStats().getRedCards()) {
-            this.redCards = element.getStats().getRedCards();
+        if (!java.util.Objects.equals(this.redCards, s.getRedCards())) {
+            this.redCards = s.getRedCards();
             hasChanges = true;
         }
-        if (this.saves != element.getStats().getSaves()) {
-            this.saves = element.getStats().getSaves();
+        if (!java.util.Objects.equals(this.saves, s.getSaves())) {
+            this.saves = s.getSaves();
             hasChanges = true;
         }
-        if (this.bonus != element.getStats().getBonus()) {
-            this.bonus = element.getStats().getBonus();
+        if (!java.util.Objects.equals(this.bonus, s.getBonus())) {
+            this.bonus = s.getBonus();
             hasChanges = true;
         }
-        if (this.totalPoints != element.getStats().getTotalPoints()) {
-            this.totalPoints = element.getStats().getTotalPoints();
+        if (!java.util.Objects.equals(this.totalPoints, s.getTotalPoints())) {
+            this.totalPoints = s.getTotalPoints();
             hasChanges = true;
         }

참고: import java.util.Objects;가 필요합니다.

backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveEventDto.java (1)

3-16: LiveFixtureDto 레거시 참조 제거 및 fixtures JSON 역직렬화 확인

아래 파일들에서 여전히 LiveFixtureDto를 참조하고 있으므로, FplFixture로 교체해주세요:

  • backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/LiveDataService.java
  • backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/LiveDataScheduler.java
  • backendProject/src/main/java/likelion/mlb/backendProject/domain/round/entity/Fixture.java

예시 수정(diff):

-import likelion.mlb.backendProject.global.staticdata.dto.live.LiveFixtureDto;
+import likelion.mlb.backendProject.global.staticdata.dto.fixture.FplFixture;

또한, 외부 JSON의 fixtures 배열이 FplFixture의 필드(team, score, minutes, kickoff_time)와 정확히 매핑되는지 최종 점검 부탁드립니다.

backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/LiveDataService.java (2)

271-274: NPE 가능성: Fixture.minutes가 null일 때 언박싱

stat.getFixture().getMinutes()가 Integer일 경우 null 언박싱으로 NPE 발생합니다. 기본값 처리로 방어하세요.

-        int currentMatchMinute = stat.getFixture().getMinutes();
+        Integer minuteObj = stat.getFixture().getMinutes();
+        int currentMatchMinute = minuteObj == null ? 0 : minuteObj;

296-299: NPE 가능성(초기 이벤트): Fixture.minutes null 처리 필요

초기 이벤트 생성 루틴도 동일한 NPE 위험이 있습니다. 동일한 방식을 적용하세요.

-        int currentMatchMinute = stat.getFixture().getMinutes();
+        Integer minuteObj = stat.getFixture().getMinutes();
+        int currentMatchMinute = minuteObj == null ? 0 : minuteObj;
🧹 Nitpick comments (6)
backendProject/src/main/java/likelion/mlb/backendProject/domain/player/entity/Player.java (1)

36-38: fplId 컬럼 nullable=true 유지가 의도인지 확인 필요

PR 전반이 fplId 중심으로 정규화되는 흐름인데, 엔티티에서는 @Column(nullable = true)로 되어 있습니다. 신규/갱신 로직에서 fplId가 항상 채워지는 것이 전제라면, 스키마를 NOT NULL(+ UNIQUE 인덱스)로 강화하는 방안 검토 바랍니다. 마이그레이션 영향도가 있으니 단계적 적용을 추천합니다.

backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/DataUpdaterService.java (1)

107-111: 중복 스트림 변환 대신 레포 헬퍼 사용으로 단순화 제안

existingMap 구성은 레포지토리의 findAllByFplIdInAsMap을 사용하면 가독성과 비용을 줄일 수 있습니다.

적용 예:

-        List<Player> existingPlayers = playerRepository.findAllByFplIdIn(allFplIds);
-        Map<Integer, Player> existingMap = existingPlayers.stream()
-                .collect(Collectors.toMap(Player::getFplId, Function.identity()));
+        Map<Integer, Player> existingMap = playerRepository.findAllByFplIdInAsMap(allFplIds);
backendProject/src/main/java/likelion/mlb/backendProject/domain/player/repository/PlayerRepository.java (2)

25-25: 파라미터 명칭 정합성 (가독성)

codes 대신 fplIds가 의도를 더 잘 드러냅니다. 구현엔 영향 없지만 통일을 권장합니다.

-    List<Player> findAllByFplIdIn(List<Integer> codes);
+    List<Player> findAllByFplIdIn(List<Integer> fplIds);

41-43: 주석 처리된 죽은 코드 정리 제안

주석으로 남긴 대안 쿼리는 혼란만 가중시킵니다. 삭제를 권장합니다.

-//    @Query("SELECT p FROM Player p WHERE p.fplId IN :ids")
-//    List<Player> findAllByFplIdIn(@Param("ids") List<Integer> ids);
backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/LiveDataService.java (2)

122-129: 주석 처리된 과거 비교 로직 제거 제안

이전 비교 구현 주석은 유지할 이유가 없어 보입니다. 현재 hasFixtureChanged가 더 안전합니다. 주석 제거로 가독성 개선을 권장합니다.


208-213: 메서드 매개변수 명 명확화 (가독성)

getActiveFixtureIds(List<FplFixture> liveData)의 매개변수명이 liveData로 되어 있어 혼동됩니다. fixtures로 변경을 제안합니다.

-    private List<Integer> getActiveFixtureIds(List<FplFixture> liveData) {
-        return liveData.stream()
+    private List<Integer> getActiveFixtureIds(List<FplFixture> fixtures) {
+        return fixtures.stream()
                 .filter(fixture -> fixture.isStarted() && !fixture.isFinished())
                 .map(FplFixture::getFplId)
                 .toList();
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2879e68 and f475ae2.

📒 Files selected for processing (8)
  • backendProject/src/main/java/likelion/mlb/backendProject/domain/player/entity/Player.java (1 hunks)
  • backendProject/src/main/java/likelion/mlb/backendProject/domain/player/entity/live/PlayerFixtureStat.java (1 hunks)
  • backendProject/src/main/java/likelion/mlb/backendProject/domain/player/repository/PlayerRepository.java (1 hunks)
  • backendProject/src/main/java/likelion/mlb/backendProject/domain/round/entity/Fixture.java (1 hunks)
  • backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/DataUpdaterService.java (1 hunks)
  • backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/LiveDataService.java (7 hunks)
  • backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveElementDto.java (1 hunks)
  • backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveEventDto.java (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveEventDto.java (2)
backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/fixture/FplFixture.java (1)
  • Getter (9-39)
backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveFixtureDto.java (1)
  • Getter (6-31)
backendProject/src/main/java/likelion/mlb/backendProject/domain/player/entity/Player.java (2)
backendProject/src/main/java/likelion/mlb/backendProject/domain/team/entity/Team.java (1)
  • updateStats (89-99)
backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/bootstrap/FplElement.java (1)
  • Getter (7-49)
backendProject/src/main/java/likelion/mlb/backendProject/domain/player/entity/live/PlayerFixtureStat.java (2)
backendProject/src/main/java/likelion/mlb/backendProject/domain/team/entity/Team.java (1)
  • Entity (16-100)
backendProject/src/main/java/likelion/mlb/backendProject/domain/player/repository/PlayerFixtureStatRepository.java (2)
  • Repository (14-32)
  • EntityGraph (25-31)
backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveElementDto.java (1)
backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveFixtureDto.java (1)
  • Getter (6-31)
backendProject/src/main/java/likelion/mlb/backendProject/domain/round/entity/Fixture.java (2)
backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/fixture/FplFixture.java (1)
  • Getter (9-39)
backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveFixtureDto.java (1)
  • Getter (6-31)
backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/LiveDataService.java (2)
backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/fixture/FplFixture.java (1)
  • Getter (9-39)
backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveFixtureDto.java (1)
  • Getter (6-31)
backendProject/src/main/java/likelion/mlb/backendProject/domain/player/repository/PlayerRepository.java (1)
backendProject/src/main/java/likelion/mlb/backendProject/domain/player/repository/PlayerFixtureStatRepository.java (1)
  • Repository (14-32)
🔇 Additional comments (12)
backendProject/src/main/java/likelion/mlb/backendProject/domain/player/entity/Player.java (1)

143-143: code 동기화 추가는 방향성 맞습니다

실시간/정기 업데이트 시 code를 최신 FPL 데이터와 동기화하는 것은 일관성 측면에서 타당합니다.

backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveElementDto.java (2)

13-14: playerId의 JsonProperty를 "element" → "id"로 변경: 맵핑 일치 👍

실제 FPL live elements의 식별자가 "id"로 오는 형태와 정합적입니다. 파이프라인 나머지 구간에서도 해당 변경에 맞춰 참조가 업데이트되었는지만 확인하면 됩니다.


13-14: 호환성 체크 권장: 기존 소스에서 "element" 키 사용 여부

이 변경으로 과거 응답 포맷("element")을 기대하던 디시리얼라이즈가 깨지지 않는지 확인 필요합니다. 다중 소스(예: 캐시/히스토릭 파일)에서 들어올 가능성이 있다면 fallback 전략을 고려하세요.

backendProject/src/main/java/likelion/mlb/backendProject/domain/player/entity/live/PlayerFixtureStat.java (2)

41-43: inDreamteam primitive + non-null 컬럼 전환 좋습니다

엔티티/DB 제약 일관성이 좋아졌습니다. 기본값 false도 적절합니다.


41-43: 스키마 마이그레이션 점검 필요

DB에 기존 null 값이 존재한다면 컬럼을 NOT NULL로 바꾸는 과정에서 에러가 납니다. DDL 변경 전에 UPDATE ... SET in_dreamteam = false WHERE in_dreamteam IS NULL 같은 정합성 보정이 필요합니다.

backendProject/src/main/java/likelion/mlb/backendProject/global/staticdata/dto/live/LiveEventDto.java (1)

3-16: fixtures를 FplFixture로 교체한 점 일관성 좋습니다

라이브 파이프라인 전반에서 FplFixture로 통일하려는 PR 방향성과 부합합니다.

backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/DataUpdaterService.java (2)

103-106: FplId 전환 반영 👍

DTO 수집 키를 fplId로 전환한 부분 문제 없습니다. 전체 흐름과 레포지토리 시그니처 변경과도 일관적입니다.


120-128: 업서트 경로 일관성 확보 완료

기존/신규 선수 분기에서 fplId 기준 매핑 및 빌더 사용 흐름 깔끔합니다. 도메인 불변조건만 유지되면 문제 없어 보입니다.

backendProject/src/main/java/likelion/mlb/backendProject/domain/player/repository/PlayerRepository.java (2)

26-32: fplId 기준 단건 조회 쿼리 전환 LGTM

JOIN FETCH p.team 포함한 fplId 비교로의 전환 적절합니다.


44-47: 헬퍼 메서드로 Map 반환 제공 적절

다건 조회 후 Map 변환을 공용화한 접근 좋습니다. 서비스에서 적극 사용 권장합니다.

backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/LiveDataService.java (2)

102-117: 라이브 경기 업데이트 흐름 적절

fplId 기반 매핑, 변경 감지 후 업데이트, 상세 로그 모두 적절합니다. 성능 측면에서도 배치 조회(Map) 접근이 좋습니다.


129-143: null-safe 변경 감지 구현 좋습니다

minutes 기본값 처리와 Objects.equals 기반 비교로 NPE 가능성을 제거했습니다. 이 방식 유지에 동의합니다.

Comment on lines +36 to +37
@Query("UPDATE Player p SET p.status = 'x' WHERE p.fplId NOT IN :fplIds")
void markDeletedByFplIdNotIn(@Param("fplIds") List<Integer> fplIds);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

빈 컬렉션 처리 없는 NOT IN 업데이트 쿼리 — 안전장치 필요

p.fplId NOT IN :fplIds에서 :fplIds가 빈 경우 방언에 따라 전행 업데이트 위험 또는 쿼리 오류가 발생합니다. 서비스 레벨에서 빈 리스트 가드를 강제하세요. (DataUpdaterService에 가드 추가 제안 드렸습니다)

레거시 코드가 남아 있을 수 있으니, 오래된 메서드 호출이 없는지 확인해 주세요:


🏁 Script executed:

#!/bin/bash
# 이전 코드 기반 메서드 호출 잔존 여부 점검
rg -n -C2 -g '!**/build/**' -g '!**/out/**' -S -tjava $'findAllByCodeIn|markDeletedByCodeNotIn'

Length of output: 96


기존 스크립트가 잘못된 메서드명을 검색해 빈 결과를 반환했습니다. 실제 메서드명(markDeletedByFplIdNotIn, findAllByFplIdIn)에 대해 호출 잔존 여부를 재확인합니다.


🏁 Script executed:

#!/bin/bash
# fplId 기반 메서드 호출 잔존 여부 점검
rg -n -C2 -g '!**/build/**' -g '!**/out/**' -S -tjava 'markDeletedByFplIdNotIn|findAllByFplIdIn'

Length of output: 9751


빈 리스트에 의한 NOT IN 업데이트 쿼리 안전장치 추가 필요

PlayerRepository.markDeletedByFplIdNotIn(List<Integer> fplIds)는 DataUpdaterService에서 단 한 곳(…/DataUpdaterService.java:113)에서만 호출되고 있습니다.
:fplIds가 빈 컬렉션일 때 방언에 따라 전체 행이 삭제되거나 쿼리 오류가 발생할 수 있으므로, 호출 직전에 빈 리스트인 경우 조기 종료 로직을 추가해주세요.

  • 파일:
    ‑ backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/DataUpdaterService.java
    ‑ 라인: 113 (playerRepository.markDeletedByFplIdNotIn(allFplIds);)
  • 제안 코드 스니펫:
    if (allFplIds.isEmpty()) {
        // 삭제 대상 없음
        return;
    }
    playerRepository.markDeletedByFplIdNotIn(allFplIds);
🤖 Prompt for AI Agents
In
backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/DataUpdaterService.java
around line 113, the call to playerRepository.markDeletedByFplIdNotIn(allFplIds)
must be guarded against an empty collection because some SQL dialects treat NOT
IN with an empty list as dangerous (affecting all rows or throwing); add an
early return when allFplIds.isEmpty() (i.e., if empty, log or comment “삭제 대상 없음”
and return) so the repository method is only invoked with a non-empty list.

Comment on lines +117 to 123
public void updateLiveFixture(FplFixture dto) {
this.started = dto.isStarted();
this.finished = dto.isFinished();
this.homeTeamScore = dto.getHomeTeamScore();
this.awayTeamScore = dto.getAwayTeamScore();
this.minutes = dto.getMinutes();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

minutes 컬럼(nullable=false)에 null 대입 가능성 — 방어 로직 필요

외부 응답에서 minutes가 null일 수 있습니다. 현재 그대로 대입하면 JPA flush 시 제약 위반 위험이 있습니다. 이전 값을 유지하거나 0으로 폴백하세요. 이전 값 유지가 안전합니다.

     public void updateLiveFixture(FplFixture dto) {
         this.started = dto.isStarted();
         this.finished = dto.isFinished();
         this.homeTeamScore = dto.getHomeTeamScore();
         this.awayTeamScore = dto.getAwayTeamScore();
-        this.minutes = dto.getMinutes();
+        this.minutes = dto.getMinutes() != null ? dto.getMinutes() : this.minutes;
     }
🤖 Prompt for AI Agents
In
backendProject/src/main/java/likelion/mlb/backendProject/domain/round/entity/Fixture.java
around lines 117 to 123, the updateLiveFixture method assigns dto.getMinutes()
directly but the minutes column is non-nullable and external responses may
return null; change the assignment to guard against null by only overwriting
this.minutes when dto.getMinutes() != null (otherwise preserve the existing
value), so the entity never gets a null minutes value (alternatively fall back
to 0 if you prefer explicit default).


// 3) 삭제 처리 (DB에 남아있으나 allCodes 에 없는 선수들)
playerRepository.markDeletedByCodeNotIn(allCodes);
playerRepository.markDeletedByFplIdNotIn(allFplIds);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

빈 목록 전달 시 대량 업데이트 위험 (NOT IN 빈 컬렉션)

markDeletedByFplIdNotIn(allFplIds) 호출 시, allFplIds가 빈 리스트면 JPA 구현체/DB 방언에 따라 전행 업데이트가 발생하거나 쿼리 오류가 날 수 있습니다. 외부 API 장애 등으로 elements가 비어올 경우를 방어해야 합니다.

서비스 레벨에서 빈 리스트 가드 추가를 권장합니다.

-        playerRepository.markDeletedByFplIdNotIn(allFplIds);
+        if (!allFplIds.isEmpty()) {
+            playerRepository.markDeletedByFplIdNotIn(allFplIds);
+        } else {
+            log.warn("elements가 비어있어 삭제 마킹을 건너뜁니다. (외부 API 점검/오류 가능성)");
+        }

또는 레포지토리 측에 별도 안전 쿼리 메서드 추가가 가능합니다. 필요 시 패치 제안 드리겠습니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
playerRepository.markDeletedByFplIdNotIn(allFplIds);
// 빈 목록 전달 시 전행 업데이트/오류 방어
if (!allFplIds.isEmpty()) {
playerRepository.markDeletedByFplIdNotIn(allFplIds);
} else {
log.warn("elements가 비어있어 삭제 마킹을 건너뜁니다. (외부 API 점검/오류 가능성)");
}
🤖 Prompt for AI Agents
In
backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/DataUpdaterService.java
around line 113, calling playerRepository.markDeletedByFplIdNotIn(allFplIds)
without checking for an empty collection can trigger a full-table update or SQL
error when allFplIds is empty; add a guard that checks if allFplIds is null or
empty and skip the repository call (optionally log a warning) or implement/use a
repository method that safely ignores empty input (e.g.,
markDeletedByFplIdNotInIfNotEmpty) so we never pass an empty collection to a NOT
IN query.

Comment on lines +83 to 87
private boolean hasNoActiveFixtures(List<FplFixture> fixtures) {
if (fixtures == null || fixtures.isEmpty()) return true;
return fixtures.stream().noneMatch(f ->
Boolean.TRUE.equals(f.isStarted()) || (f.getMinutes() != null && f.getMinutes() > 0));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

종료된 경기까지 ‘활성’으로 오판 가능 — 활성 판단 로직 불일치

hasNoActiveFixturesstarted == true만으로 활성로 간주합니다. 종료(finished == true)인 경기도 started는 true라 계속 활성로 인식됩니다. 아래와 같이 finished 배제를 포함해 getActiveFixtureIds와 기준을 일치시키세요.

-    private boolean hasNoActiveFixtures(List<FplFixture> fixtures) {
-        if (fixtures == null || fixtures.isEmpty()) return true;
-        return fixtures.stream().noneMatch(f ->
-                Boolean.TRUE.equals(f.isStarted()) || (f.getMinutes() != null && f.getMinutes() > 0));
-    }
+    private boolean hasNoActiveFixtures(List<FplFixture> fixtures) {
+        if (fixtures == null || fixtures.isEmpty()) return true;
+        return fixtures.stream().noneMatch(f ->
+            (f.isStarted() || (f.getMinutes() != null && f.getMinutes() > 0))
+            && !f.isFinished()
+        );
+    }

또는 더 단순하게 return getActiveFixtureIds(fixtures).isEmpty();로 통일해도 됩니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private boolean hasNoActiveFixtures(List<FplFixture> fixtures) {
if (fixtures == null || fixtures.isEmpty()) return true;
return fixtures.stream().noneMatch(f ->
Boolean.TRUE.equals(f.isStarted()) || (f.getMinutes() != null && f.getMinutes() > 0));
}
private boolean hasNoActiveFixtures(List<FplFixture> fixtures) {
if (fixtures == null || fixtures.isEmpty()) return true;
return fixtures.stream().noneMatch(f ->
(f.isStarted() || (f.getMinutes() != null && f.getMinutes() > 0))
&& !f.isFinished()
);
}
🤖 Prompt for AI Agents
In
backendProject/src/main/java/likelion/mlb/backendProject/global/scheduler/service/LiveDataService.java
around lines 83 to 87, the hasNoActiveFixtures method treats fixtures with
started == true as active even if finished == true, causing inconsistency with
getActiveFixtureIds; update the logic to exclude fixtures where finished == true
(mirror the active criteria used in getActiveFixtureIds) or replace the method
body with a call to getActiveFixtureIds(fixtures).isEmpty() to ensure both use
the same active-definition.

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.

1 participant