Conversation
feat: 링 도메인 기초 인프라 & 링 메인 화면 조회
feature: 기억 대결 생명주기 API 구현 (시작, 취소)
feat: 홈 기능 구현
feat: 대결 결과 확정 및 점수 로직 구현
feat: 기록실 기능 구현
… into feature/ranking
feature: 랭킹 및 검색 구현
📝 WalkthroughWalkthroughThis PR adds episode-based domain models and APIs, authentication and onboarding flows, ranking and history views, match and show-session workflows, OpenAI/local title suggestions, database migrations, local demo seeding, and integration coverage. ChangesPlatform feature rollout
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
docs/schema.sql (1)
1-186: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winReference schema is stale and its header comment is inaccurate.
The header claims this reflects state "Current schema after Flyway V6"-equivalent, but the body already includes changes from V3 (
SCHEDULEDstatus), V5 (round_count), and V7 (title_scoreDEFAULT 1000 / CHECK ≥100), while omitting V8 (onboarding_status) and V9 (period_key,match_type,session_type,primary_episode_id,score_multiplier,loser_episode_id,match_order, score columns,placement_status) entirely. As the stated ERD-import reference, this drift will actively mislead readers about the live schema.Suggest regenerating this file from the full migration set (through V9) and updating the header comment to reflect the actual applied version.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/schema.sql` around lines 1 - 186, Regenerate the reference schema from the complete Flyway migration set through V9, ensuring it includes V8/V9 additions such as onboarding_status, period_key, match_type, session_type, primary_episode_id, score_multiplier, loser_episode_id, match_order, score fields, and placement_status. Update the header to accurately state the schema version and remove outdated claims that it represents V6.
🧹 Nitpick comments (10)
src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql (1)
1-5: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winNew
match_type/session_typecolumns lack CHECK constraints.Every other status/type column in this schema (
event_type,status,placement_status,score_type) is constrained viaCHECK. The newmatch_type(matching_events, matches) andsession_type(ring_sessions) columns accept arbitrary strings, which is inconsistent and risks bad data feeding into score-multiplier logic.🛡️ Example fix
ALTER TABLE matching_events ADD COLUMN period_key VARCHAR(20) NULL AFTER event_type, ADD COLUMN match_type VARCHAR(30) NOT NULL DEFAULT 'RIVAL' AFTER period_key, ADD COLUMN score_multiplier DECIMAL(4,2) NOT NULL DEFAULT 1.00 AFTER score_reward, + ADD CONSTRAINT ck_matching_events_match_type CHECK (match_type IN ('RIVAL', 'GENERAL')), ADD CONSTRAINT uk_matching_events_type_period UNIQUE (event_type, period_key);Also applies to: 11-11, 20-20
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql` around lines 1 - 5, Add CHECK constraints for the new match_type and session_type columns across migrations V9, 11-11, and 20-20, matching the allowed values used by the application and existing schema conventions. Update matching_events, matches, and ring_sessions definitions so invalid type strings cannot be stored, and ensure the constraints are compatible with each column’s defaults and migration order.src/main/java/com/team6/server/auth/service/AuthService.java (1)
30-48: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winConsider normalizing email casing.
Email is used for lookups/uniqueness checks (Lines 31, 34, 39) without normalization. Depending on DB collation, this can allow case-variant duplicate accounts or cause login mismatches for the same address entered with different casing.
♻️ Suggested normalization
public Long signUp(SignUpRequest request) { - if (members.existsByEmail(request.email())) { + String email = request.email().toLowerCase(); + if (members.existsByEmail(email)) { throw new BusinessException(ErrorCode.MEMBER_EMAIL_DUPLICATED); } - return members.save(new Member(request.email(), encoder.encode(request.password()), request.name())).getId(); + return members.save(new Member(email, encoder.encode(request.password()), request.name())).getId(); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/team6/server/auth/service/AuthService.java` around lines 30 - 48, Normalize email addresses consistently before uniqueness checks, persistence, and lookup: update signUp and login to use a shared normalization rule (such as trimming whitespace and lowercasing) before calling members.existsByEmail, constructing Member, and members.findByEmail. Keep the normalized value consistent across all authentication flows.src/main/java/com/team6/server/match/controller/MatchController.java (1)
48-62: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse imports instead of fully-qualified names for consistency.
completeMatchinlines fully-qualifiedcom.team6.server.global.response.ApiResponseandcom.team6.server.match.dto.*while the sibling handlers rely on imports. Adding imports keeps the controller consistent and readable.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/team6/server/match/controller/MatchController.java` around lines 48 - 62, Replace the fully qualified ApiResponse, MatchResultResponseDto, and MatchResultRequestDto references in completeMatch with their corresponding imports, matching the style of the sibling controller handlers.src/main/java/com/team6/server/match/service/MatchService.java (2)
92-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse the injected
Clockfor consistency and testability.
completeMatchderives time viaLocalDateTime.now(clock), butstartMatchusesLocalDateTime.now()directly, bypassing the injectedClock. Align both onclockso time is controllable in tests and consistent across the match lifecycle.Proposed fix
- LocalDateTime startedAt = LocalDateTime.now(); + LocalDateTime startedAt = LocalDateTime.now(clock);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/team6/server/match/service/MatchService.java` around lines 92 - 94, Update startMatch to derive startedAt with LocalDateTime.now(clock), matching completeMatch’s injected-clock usage; keep the shared timestamp passed to both episodeA.markMatched and episodeB.markMatched.
190-192: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueRedundant locked fetch of the show session.
showSessionRepository.findByIdWithPessimisticLock(match.getSessionId())is executed once for the multiplier (Lines 191-192) and again for round completion (Lines 238-239). Fetch it once and reuse the reference to avoid the duplicate query/lock acquisition within the same transaction.Also applies to: 238-239
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/team6/server/match/service/MatchService.java` around lines 190 - 192, Fetch the show session once in the match-processing method, store the result in a local variable, and derive the multiplier from that reference instead of calling findByIdWithPessimisticLock again. Reuse the same session object in the round-completion logic around the second fetch, preserving the existing null-session handling and not-found exception behavior.src/main/java/com/team6/server/member/service/MemberService.java (1)
44-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid magic string
"IN_PROGRESS"for session status comparison.Line 49 compares against a raw string literal. If
ShowSession's status representation changes, this silently breaks without a compile error.♻️ Suggested fix (pending confirmation of the actual status type)
- return new OnboardingStatusResponse(member.getOnboardingStatus().name(), - placement != null && "IN_PROGRESS".equals(placement.getStatus()) ? placement.getId() : null, + return new OnboardingStatusResponse(member.getOnboardingStatus().name(), + placement != null && ShowSessionStatus.IN_PROGRESS.name().equals(placement.getStatus()) ? placement.getId() : null,Please confirm
ShowSession.getStatus()type (enum vs. String) to pick the right comparison approach.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/team6/server/member/service/MemberService.java` around lines 44 - 53, Replace the raw "IN_PROGRESS" comparison in MemberService.getOnboardingStatus with the canonical ShowSession status constant or enum value, based on the return type of placement.getStatus(); use a typed comparison for an enum or the centralized constant for a String.src/main/java/com/team6/server/episode/repository/EpisodeRepository.java (1)
44-56: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
findReadyForShowForUpdateandfindOnboardingCandidatesForUpdateare byte-for-byte identical queries.Both methods have the same JPQL, parameters, and lock mode, differing only by name. Consider consolidating into one method with a name reflecting the shared semantics, or documenting why two names are needed for readability at call sites.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/team6/server/episode/repository/EpisodeRepository.java` around lines 44 - 56, Consolidate the duplicate repository methods findReadyForShowForUpdate and findOnboardingCandidatesForUpdate into one shared method with a name describing their common semantics, then update all call sites; if distinct names are required for readability, document the intentional duplication instead.src/main/java/com/team6/server/match/dto/MatchResultRequestDto.java (1)
3-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNo setter/constructor for
winnerEpisodeId— relies on a Jackson quirk instead of an explicit mutator.Only
@Getter/@NoArgsConstructorare present; there's no@Setterand no all-args constructor. Jackson can still populate the private field by default because the getter makes the property "known" and Jackson falls back to direct field injection — but this behavior is an implicit default rather than an explicit contract, and depends on the getter existing and default Jackson visibility settings not being overridden elsewhere in the app. Every other request/response DTO shown in this PR (e.g.TodayEpisodeResponse,EpisodeListItemResponse) uses a record, which makes the deserialization contract explicit and immutable. Consider aligning this class with that pattern.♻️ Suggested refactor to a record
-import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -public class MatchResultRequestDto { - - private Long winnerEpisodeId; -} +public record MatchResultRequestDto(Long winnerEpisodeId) {}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/team6/server/match/dto/MatchResultRequestDto.java` around lines 3 - 11, Make MatchResultRequestDto’s deserialization contract explicit by replacing the Lombok-based mutable DTO with a record that declares winnerEpisodeId as its component; remove the `@Getter` and `@NoArgsConstructor` annotations and preserve the existing Long field name and type.src/main/java/com/team6/server/ranking/repository/RankingEpisodeScoreRepository.java (1)
37-51: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winConsider a window function instead of a correlated subquery for
competitionRank.The
(SELECT COUNT(*) + 1 FROM episode_rankings higher WHERE higher.title_score > er.title_score)subquery re-scansepisode_rankingsfor every row returned by the page, which scales poorly as the table grows. A singleRANK() OVER (ORDER BY er.title_score DESC)produces the same competition-ranking semantics (ties share a rank) in one pass.♻️ Proposed refactor
`@Query`(value = """ SELECT er.episode_id AS episodeId, e.title AS episodeTitle, - er.title_score AS score, t.name AS titleName, - (SELECT COUNT(*) + 1 FROM episode_rankings higher - WHERE higher.title_score > er.title_score) AS competitionRank + er.title_score AS score, t.name AS titleName, + RANK() OVER (ORDER BY er.title_score DESC) AS competitionRank FROM episode_rankings er JOIN episodes e ON e.id = er.episode_id LEFT JOIN titles t ON t.id = er.current_title_id ORDER BY er.title_score DESC, er.episode_id ASC LIMIT :limit OFFSET :offset """, nativeQuery = true)Confirm the target database engine/version supports window functions before adopting (e.g., MySQL 8.0+/PostgreSQL support
RANK() OVER, older MySQL 5.7 does not).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/team6/server/ranking/repository/RankingEpisodeScoreRepository.java` around lines 37 - 51, Replace the correlated competitionRank subquery in findRankingPage with RANK() OVER (ORDER BY er.title_score DESC), preserving the existing aliases and pagination ordering. Verify the configured database engine/version supports window functions, and update compatibility configuration or choose an equivalent fallback if it does not.src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java (1)
38-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract violation details instead of a generic message.
The sibling handler for
MethodArgumentNotValidExceptionbuilds a field-level message, but this new handler forConstraintViolationException/HandlerMethodValidationExceptiondiscards violation details entirely, reducing debuggability of query/path-parameter validation errors.♻️ Suggested consolidation
`@ExceptionHandler`({ConstraintViolationException.class, HandlerMethodValidationException.class}) - ResponseEntity<ApiResponse<Void>> parameterValidation(Exception e) { - return response(ErrorCode.VALIDATION_ERROR); + ResponseEntity<ApiResponse<Void>> parameterValidation(Exception e) { + String message = e instanceof ConstraintViolationException cve + ? cve.getConstraintViolations().stream() + .map(v -> v.getPropertyPath() + ": " + v.getMessage()) + .collect(Collectors.joining(", ")) + : e.getMessage(); + return response(ErrorCode.VALIDATION_ERROR, message); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java` around lines 38 - 41, Update GlobalExceptionHandler.parameterValidation to extract field/parameter violation details from ConstraintViolationException and HandlerMethodValidationException, matching the field-level message behavior of the MethodArgumentNotValidException handler, and pass the resulting detail into the validation error response instead of returning only the generic error code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 18-19: Restore CI test execution by removing the global “-x test”
exclusion from the Gradle command. Fix the timezone-dependent integration tests
using the existing TimeConfig pattern, such as injecting a fixed Clock or
TimeZone, or narrowly exclude only the identified failing test tasks/classes if
an immediate workaround is required.
In `@README.md`:
- Around line 164-176: The README migration list documents duplicate V7 Flyway
migrations, which causes startup failure. Update the migration documentation to
assign one of the V7 migrations, such as
V7__align_episode_ranking_score_bounds.sql, the next unused unique version and
ensure the referenced filename matches the actual migration file.
In `@src/main/java/com/team6/server/auth/service/AuthService.java`:
- Around line 30-35: Handle the unique-email constraint race in
AuthService.signUp by catching DataIntegrityViolationException from members.save
and translating it to BusinessException(ErrorCode.MEMBER_EMAIL_DUPLICATED),
while preserving the existing pre-check and successful return behavior.
- Around line 50-62: Implement refresh-token rotation and revocation across
AuthService and JwtProvider: persist or denylist issued refresh-token
identifiers with expiry, validate the presented token is active, revoke it
before issuing replacements in refresh, and reject revoked or reused tokens. Add
logout revocation support and ensure token creation stores the necessary
identifier while preserving existing member and expiry handling.
In
`@src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiResponse.java`:
- Around line 7-9: The IncompleteDetails field is not mapped from OpenAI’s
snake_case response property. In OpenAiResponse, annotate the incompleteDetails
component with `@JsonProperty`("incomplete_details") (or configure an equivalent
shared snake_case naming strategy) so the returned value is populated.
In `@src/main/java/com/team6/server/episode/repository/EpisodeRepository.java`:
- Around line 58-72: The PESSIMISTIC_WRITE lock in
findPlacementOpponentsForUpdate currently applies to the joined
RankingEpisodeScore rows as well as Episode. Narrow the locked query to Episode
records only by removing the join and score-based ordering, or split score
retrieval into a separate non-locking read while preserving opponent ordering
and pagination.
In `@src/main/java/com/team6/server/global/config/LocalDummyDataSeeder.java`:
- Around line 91-107: Prevent silent desynchronization in createEpisodes by
validating that names and deltas have equal sizes before iterating, failing fast
with a clear exception if they differ. Keep the existing indexing logic only
after this guard, or refactor the inputs into paired records so each episode
name and delta cannot become mismatched.
In `@src/main/java/com/team6/server/global/security/CurrentMemberProvider.java`:
- Around line 18-26: Handle a null authentication before accessing it in
CurrentMemberProvider.require. Treat null as an invalid token by throwing
BusinessException with ErrorCode.INVALID_TOKEN, while preserving the existing
member ID parsing and lookup behavior for non-null authentication.
In `@src/main/java/com/team6/server/match/service/MatchService.java`:
- Around line 193-195: The score calculation in MatchService’s ScoreDelta
construction uses longValueExact(), which fails for fractional multipliers such
as 1.25. Replace both conversions with an explicit, documented rounding strategy
before converting to long, or validate/constrain multipliers to whole numbers
before this calculation.
In `@src/main/java/com/team6/server/ranking/entity/RankingEpisodeScore.java`:
- Around line 30-32: Update GlobalExceptionHandler to handle
ObjectOptimisticLockingFailureException explicitly and return HTTP 409 Conflict
with an appropriate response body; use the existing exception-handler
conventions and verify MatchService version conflicts no longer reach the
generic 500 handler.
In `@src/main/resources/application.yml`:
- Line 22: Replace the hardcoded default in the JWT secret configuration with a
required unset fallback so startup fails when JWT_SECRET is missing, or restrict
the development fallback to an explicitly activated local profile. Ensure
non-local environments cannot use the publicly known secret.
- Around line 14-18: In the active application configuration, change
spring.flyway.enabled to true so the versioned V2–V9 migrations run, and replace
hibernate.ddl-auto: update with a migration-compatible setting such as validate.
Remove the checked-in JWT_SECRET fallback from the relevant security
configuration and require the property so startup fails when it is missing.
In `@src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql`:
- Around line 1-4: Rename the migration file containing the `ALTER TABLE
episode_rankings` statement so its Flyway version is unique and does not
conflict with `V7__add_ranking_period_index.sql`; preserve the migration
description and SQL contents.
- Around line 1-4: Backfill existing rows before tightening the constraint: in
the migration, update `episode_rankings` rows with `title_score` below 100 to a
valid value (such as 100), then alter the default and replace
`ck_episode_rankings_title_score` with the `title_score >= 100` check.
In `@src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql`:
- Line 4: Update the migration definitions for score_multiplier in both
referenced locations to enforce a strictly positive value with a database CHECK
constraint, while retaining the existing DECIMAL type and default. Ensure the
constraint is applied consistently to every definition of this column.
---
Outside diff comments:
In `@docs/schema.sql`:
- Around line 1-186: Regenerate the reference schema from the complete Flyway
migration set through V9, ensuring it includes V8/V9 additions such as
onboarding_status, period_key, match_type, session_type, primary_episode_id,
score_multiplier, loser_episode_id, match_order, score fields, and
placement_status. Update the header to accurately state the schema version and
remove outdated claims that it represents V6.
---
Nitpick comments:
In `@src/main/java/com/team6/server/auth/service/AuthService.java`:
- Around line 30-48: Normalize email addresses consistently before uniqueness
checks, persistence, and lookup: update signUp and login to use a shared
normalization rule (such as trimming whitespace and lowercasing) before calling
members.existsByEmail, constructing Member, and members.findByEmail. Keep the
normalized value consistent across all authentication flows.
In `@src/main/java/com/team6/server/episode/repository/EpisodeRepository.java`:
- Around line 44-56: Consolidate the duplicate repository methods
findReadyForShowForUpdate and findOnboardingCandidatesForUpdate into one shared
method with a name describing their common semantics, then update all call
sites; if distinct names are required for readability, document the intentional
duplication instead.
In `@src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java`:
- Around line 38-41: Update GlobalExceptionHandler.parameterValidation to
extract field/parameter violation details from ConstraintViolationException and
HandlerMethodValidationException, matching the field-level message behavior of
the MethodArgumentNotValidException handler, and pass the resulting detail into
the validation error response instead of returning only the generic error code.
In `@src/main/java/com/team6/server/match/controller/MatchController.java`:
- Around line 48-62: Replace the fully qualified ApiResponse,
MatchResultResponseDto, and MatchResultRequestDto references in completeMatch
with their corresponding imports, matching the style of the sibling controller
handlers.
In `@src/main/java/com/team6/server/match/dto/MatchResultRequestDto.java`:
- Around line 3-11: Make MatchResultRequestDto’s deserialization contract
explicit by replacing the Lombok-based mutable DTO with a record that declares
winnerEpisodeId as its component; remove the `@Getter` and `@NoArgsConstructor`
annotations and preserve the existing Long field name and type.
In `@src/main/java/com/team6/server/match/service/MatchService.java`:
- Around line 92-94: Update startMatch to derive startedAt with
LocalDateTime.now(clock), matching completeMatch’s injected-clock usage; keep
the shared timestamp passed to both episodeA.markMatched and
episodeB.markMatched.
- Around line 190-192: Fetch the show session once in the match-processing
method, store the result in a local variable, and derive the multiplier from
that reference instead of calling findByIdWithPessimisticLock again. Reuse the
same session object in the round-completion logic around the second fetch,
preserving the existing null-session handling and not-found exception behavior.
In `@src/main/java/com/team6/server/member/service/MemberService.java`:
- Around line 44-53: Replace the raw "IN_PROGRESS" comparison in
MemberService.getOnboardingStatus with the canonical ShowSession status constant
or enum value, based on the return type of placement.getStatus(); use a typed
comparison for an enum or the centralized constant for a String.
In
`@src/main/java/com/team6/server/ranking/repository/RankingEpisodeScoreRepository.java`:
- Around line 37-51: Replace the correlated competitionRank subquery in
findRankingPage with RANK() OVER (ORDER BY er.title_score DESC), preserving the
existing aliases and pagination ordering. Verify the configured database
engine/version supports window functions, and update compatibility configuration
or choose an equivalent fallback if it does not.
In `@src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql`:
- Around line 1-5: Add CHECK constraints for the new match_type and session_type
columns across migrations V9, 11-11, and 20-20, matching the allowed values used
by the application and existing schema conventions. Update matching_events,
matches, and ring_sessions definitions so invalid type strings cannot be stored,
and ensure the constraints are compatible with each column’s defaults and
migration order.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 82410e96-01e9-4617-ab7e-d435936a7c6f
📒 Files selected for processing (105)
.env.example.github/workflows/ci.ymlREADME.mddocs/schema.sqlsrc/main/java/com/team6/server/ServerApplication.javasrc/main/java/com/team6/server/auth/AuthController.javasrc/main/java/com/team6/server/auth/AuthDtos.javasrc/main/java/com/team6/server/auth/AuthService.javasrc/main/java/com/team6/server/auth/controller/AuthController.javasrc/main/java/com/team6/server/auth/dto/LoginRequest.javasrc/main/java/com/team6/server/auth/dto/LoginResponse.javasrc/main/java/com/team6/server/auth/dto/RefreshRequest.javasrc/main/java/com/team6/server/auth/dto/SignUpRequest.javasrc/main/java/com/team6/server/auth/dto/TokenResponse.javasrc/main/java/com/team6/server/auth/repository/MemberRepository.javasrc/main/java/com/team6/server/auth/service/AuthService.javasrc/main/java/com/team6/server/episode/Episode.javasrc/main/java/com/team6/server/episode/controller/EpisodeController.javasrc/main/java/com/team6/server/episode/dto/CreateEpisodeRequest.javasrc/main/java/com/team6/server/episode/dto/CreateEpisodeResponse.javasrc/main/java/com/team6/server/episode/dto/EpisodeDetailResponse.javasrc/main/java/com/team6/server/episode/dto/EpisodeListItemResponse.javasrc/main/java/com/team6/server/episode/dto/EpisodeListResponse.javasrc/main/java/com/team6/server/episode/dto/EpisodeSearchItemResponse.javasrc/main/java/com/team6/server/episode/dto/EpisodeSearchResponse.javasrc/main/java/com/team6/server/episode/dto/TitleSuggestionRequest.javasrc/main/java/com/team6/server/episode/dto/TitleSuggestionResponse.javasrc/main/java/com/team6/server/episode/infrastructure/openai/OpenAiConfig.javasrc/main/java/com/team6/server/episode/infrastructure/openai/OpenAiProperties.javasrc/main/java/com/team6/server/episode/infrastructure/openai/OpenAiRequest.javasrc/main/java/com/team6/server/episode/infrastructure/openai/OpenAiResponse.javasrc/main/java/com/team6/server/episode/infrastructure/openai/OpenAiTitleSuggestionProvider.javasrc/main/java/com/team6/server/episode/repository/EpisodeRepository.javasrc/main/java/com/team6/server/episode/service/EpisodeCursorCodec.javasrc/main/java/com/team6/server/episode/service/EpisodeService.javasrc/main/java/com/team6/server/episode/service/LocalTitleSuggestionProvider.javasrc/main/java/com/team6/server/episode/service/TitleSuggestionProvider.javasrc/main/java/com/team6/server/global/config/LocalDummyDataSeeder.javasrc/main/java/com/team6/server/global/config/OpenApiConfig.javasrc/main/java/com/team6/server/global/config/TimeConfig.javasrc/main/java/com/team6/server/global/exception/ErrorCode.javasrc/main/java/com/team6/server/global/exception/GlobalExceptionHandler.javasrc/main/java/com/team6/server/global/security/CurrentMemberProvider.javasrc/main/java/com/team6/server/history/controller/HistoryController.javasrc/main/java/com/team6/server/history/dto/ChampionHistoryItemResponse.javasrc/main/java/com/team6/server/history/dto/HistoryHomeResponse.javasrc/main/java/com/team6/server/history/dto/MatchHistoryItemResponse.javasrc/main/java/com/team6/server/history/service/HistoryService.javasrc/main/java/com/team6/server/home/controller/HomeController.javasrc/main/java/com/team6/server/home/dto/HomeResponse.javasrc/main/java/com/team6/server/home/dto/TodayEpisodeResponse.javasrc/main/java/com/team6/server/home/dto/UpcomingEventResponse.javasrc/main/java/com/team6/server/home/service/HomeService.javasrc/main/java/com/team6/server/match/controller/MatchController.javasrc/main/java/com/team6/server/match/controller/PlacementController.javasrc/main/java/com/team6/server/match/controller/ShowController.javasrc/main/java/com/team6/server/match/dto/AvailableShowResponse.javasrc/main/java/com/team6/server/match/dto/MatchRequestDto.javasrc/main/java/com/team6/server/match/dto/MatchResultRequestDto.javasrc/main/java/com/team6/server/match/dto/MatchResultResponseDto.javasrc/main/java/com/team6/server/match/dto/RingResponseDto.javasrc/main/java/com/team6/server/match/dto/ShowSessionProgressResponse.javasrc/main/java/com/team6/server/match/dto/ShowSessionResponse.javasrc/main/java/com/team6/server/match/entity/Match.javasrc/main/java/com/team6/server/match/entity/MatchingEvent.javasrc/main/java/com/team6/server/match/entity/ShowSession.javasrc/main/java/com/team6/server/match/repository/MatchRepository.javasrc/main/java/com/team6/server/match/repository/MatchingEventRepository.javasrc/main/java/com/team6/server/match/repository/ShowSessionRepository.javasrc/main/java/com/team6/server/match/service/BalancedPairingPolicy.javasrc/main/java/com/team6/server/match/service/MatchService.javasrc/main/java/com/team6/server/match/service/PlacementService.javasrc/main/java/com/team6/server/match/service/ShowScheduler.javasrc/main/java/com/team6/server/match/service/ShowService.javasrc/main/java/com/team6/server/member/Member.javasrc/main/java/com/team6/server/member/MemberRepository.javasrc/main/java/com/team6/server/member/controller/MemberController.javasrc/main/java/com/team6/server/member/dto/MemberMeResponse.javasrc/main/java/com/team6/server/member/dto/OnboardingStatusResponse.javasrc/main/java/com/team6/server/member/service/MemberService.javasrc/main/java/com/team6/server/ranking/controller/RankingController.javasrc/main/java/com/team6/server/ranking/dto/RankingItemResponse.javasrc/main/java/com/team6/server/ranking/dto/RankingListResponse.javasrc/main/java/com/team6/server/ranking/entity/RankingEpisodeScore.javasrc/main/java/com/team6/server/ranking/entity/RankingScoreEvent.javasrc/main/java/com/team6/server/ranking/entity/Title.javasrc/main/java/com/team6/server/ranking/repository/RankingEpisodeScoreRepository.javasrc/main/java/com/team6/server/ranking/repository/RankingScoreEventRepository.javasrc/main/java/com/team6/server/ranking/repository/TitleRepository.javasrc/main/java/com/team6/server/ranking/service/RankingService.javasrc/main/resources/application.ymlsrc/main/resources/db/migration/V2__create_product_feature_tables.sqlsrc/main/resources/db/migration/V3__add_scheduled_matching_event_status.sqlsrc/main/resources/db/migration/V4__rename_episode_domain.sqlsrc/main/resources/db/migration/V5__create_ring_sessions.sqlsrc/main/resources/db/migration/V6__add_member_onboarding_completed_at.sqlsrc/main/resources/db/migration/V7__add_ranking_period_index.sqlsrc/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sqlsrc/main/resources/db/migration/V8__add_member_onboarding_status.sqlsrc/main/resources/db/migration/V9__extend_matches_and_show_sessions.sqlsrc/test/java/com/team6/server/auth/AuthApiIntegrationTest.javasrc/test/java/com/team6/server/history/HistoryApiIntegrationTest.javasrc/test/java/com/team6/server/home/HomeEpisodeApiIntegrationTest.javasrc/test/java/com/team6/server/matching/RingApiIntegrationTest.javasrc/test/java/com/team6/server/member/MemberOnboardingApiIntegrationTest.java
💤 Files with no reviewable changes (4)
- src/main/java/com/team6/server/member/MemberRepository.java
- src/main/java/com/team6/server/auth/AuthService.java
- src/main/java/com/team6/server/auth/AuthDtos.java
- src/main/java/com/team6/server/auth/AuthController.java
| # TODO: 통합 테스트의 시간대 의존성을 제거한 뒤 테스트 실행을 복구한다. | ||
| - run: ./gradlew clean build -x test |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
CI no longer runs tests.
-x test skips the entire test suite on every build; the TODO acknowledges this is a workaround for a timezone dependency in integration tests. Merging with tests disabled removes the safety net for all changes going forward (including this large PR), which is risky given the sizeable auth/matching/ranking logic being introduced.
Consider fixing the underlying timezone dependency (e.g., inject a fixed Clock/TimeZone in test config as done via TimeConfig) instead of disabling tests outright, or at minimum scope -x test to only the failing test classes/tasks.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/ci.yml around lines 18 - 19, Restore CI test execution by
removing the global “-x test” exclusion from the Gradle command. Fix the
timezone-dependent integration tests using the existing TimeConfig pattern, such
as injecting a fixed Clock or TimeZone, or narrowly exclude only the identified
failing test tasks/classes if an immediate workaround is required.
| ## 데이터베이스와 마이그레이션 | ||
|
|
||
| 마이그레이션 파일은 `src/main/resources/db/migration` 아래에 위치합니다. | ||
|
|
||
| ```text | ||
| V1__init.sql | ||
| V2__create_product_feature_tables.sql | ||
| V3__add_scheduled_matching_event_status.sql | ||
| ... | ||
| ``` | ||
|
|
||
| 운영에 적용된 Flyway 파일은 수정하지 않고, 스키마 변경이 필요하면 새 버전 파일을 추가합니다. | ||
|
|
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# List migration files and flag any duplicate Flyway version prefixes
fd . src/main/resources/db/migration -e sql | sed -E 's#.*/(V[0-9]+(_[0-9]+)*)__.*#\1#' | sort | uniq -c | sort -rn | head -20
fd . src/main/resources/db/migration -e sqlRepository: 8th-COKERTHON/server-team6
Length of output: 934
Use a unique Flyway version for one of the V7 migrations
src/main/resources/db/migration/V7__add_ranking_period_index.sql and src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql both use version V7, which makes Flyway fail on startup when migrations are enabled. Rename one to the next unused version.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` around lines 164 - 176, The README migration list documents
duplicate V7 Flyway migrations, which causes startup failure. Update the
migration documentation to assign one of the V7 migrations, such as
V7__align_episode_ranking_score_bounds.sql, the next unused unique version and
ensure the referenced filename matches the actual migration file.
| public Long signUp(SignUpRequest request) { | ||
| if (members.existsByEmail(request.email())) { | ||
| throw new BusinessException(ErrorCode.MEMBER_EMAIL_DUPLICATED); | ||
| } | ||
| return members.save(new Member(request.email(), encoder.encode(request.password()), request.name())).getId(); | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -i 'email' docs/schema.sql src/main/resources/db/migrationRepository: 8th-COKERTHON/server-team6
Length of output: 457
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the auth service and any global exception handling around data integrity errors.
ast-grep outline src/main/java/com/team6/server/auth/service/AuthService.java --view expanded || true
printf '\n---\n'
fd -a '.*(Exception|Advice|Handler).*\.java' src/main/java | sed 's#^\./##' | sort
printf '\n---\n'
rg -n "DataIntegrityViolationException|BusinessException|MEMBER_EMAIL_DUPLICATED|`@RestControllerAdvice`|`@ExceptionHandler`" src/main/javaRepository: 8th-COKERTHON/server-team6
Length of output: 16319
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,220p' src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java
printf '\n---\n'
sed -n '1,120p' src/main/java/com/team6/server/auth/service/AuthService.javaRepository: 8th-COKERTHON/server-team6
Length of output: 6147
Handle duplicate-email races as a conflict, not a 500 existsByEmail still leaves a TOCTOU gap; with the unique members.email constraint, a concurrent insert can surface as INTERNAL_SERVER_ERROR instead of MEMBER_EMAIL_DUPLICATED. Catch DataIntegrityViolationException here or map it globally to the duplicate-email business error.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/team6/server/auth/service/AuthService.java` around lines 30
- 35, Handle the unique-email constraint race in AuthService.signUp by catching
DataIntegrityViolationException from members.save and translating it to
BusinessException(ErrorCode.MEMBER_EMAIL_DUPLICATED), while preserving the
existing pre-check and successful return behavior.
| @Transactional(readOnly = true) | ||
| public TokenResponse refresh(RefreshRequest request) { | ||
| var claims = jwt.parseRefreshToken(request.refreshToken()); | ||
| Long memberId = Long.valueOf(claims.getSubject()); | ||
| return tokens(members.findById(memberId) | ||
| .orElseThrow(() -> new BusinessException(ErrorCode.MEMBER_NOT_FOUND))); | ||
| } | ||
|
|
||
| private TokenResponse tokens(Member member) { | ||
| String role = member.getRole().name(); | ||
| return new TokenResponse(jwt.createAccessToken(member.getId(), role), | ||
| jwt.createRefreshToken(member.getId(), role), "Bearer", jwt.accessExpiresInSeconds()); | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd -t f JwtProvider.java | xargs -I{} cat -n {}Repository: 8th-COKERTHON/server-team6
Length of output: 2411
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== refresh/logout/token tracking search ==\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' \
-e 'refresh token' -e 'refreshToken' -e 'denylist' -e 'blacklist' -e 'revoc' -e 'logout' \
src/main/java
printf '\n== auth-related files ==\n'
fd -t f -e java src/main/java | rg 'Auth|Jwt|Token|Login|Logout|Refresh'
printf '\n== member/token entity/repository search ==\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' \
-e 'class .*Token' -e 'interface .*Token' -e 'RefreshToken' -e 'TokenStore' -e 'TokenRepository' \
src/main/javaRepository: 8th-COKERTHON/server-team6
Length of output: 1190
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== AuthService and JwtProvider context ==\n'
sed -n '1,220p' src/main/java/com/team6/server/auth/service/AuthService.java
printf '\n---\n'
sed -n '1,220p' src/main/java/com/team6/server/global/security/JwtProvider.javaRepository: 8th-COKERTHON/server-team6
Length of output: 5036
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search the auth package and nearby security infrastructure for server-side refresh-token state or revocation.
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' \
-e 'refreshToken' -e 'RefreshToken' -e 'denylist' -e 'blacklist' -e 'revoke' -e 'revocation' \
-e 'logout' -e 'token store' -e 'tokenStore' -e 'token repository' -e 'TokenRepository' \
src/main/java
printf '\n== auth package files ==\n'
fd -t f src/main/java/com/team6/server/auth src/main/java/com/team6/server/global/security
printf '\n== references to JwtProvider ==\n'
rg -n --hidden --glob '!**/target/**' --glob '!**/build/**' --glob '!**/dist/**' 'JwtProvider' src/main/javaRepository: 8th-COKERTHON/server-team6
Length of output: 1746
Add refresh-token rotation/revocation
JwtProvider only signs and parses JWTs; it doesn’t persist issued refresh tokens or support logout/reuse detection. A stolen refresh token stays usable until expiry, even after a new pair is issued. Add server-side tracking or a denylist if invalidation is required.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/team6/server/auth/service/AuthService.java` around lines 50
- 62, Implement refresh-token rotation and revocation across AuthService and
JwtProvider: persist or denylist issued refresh-token identifiers with expiry,
validate the presented token is active, revoke it before issuing replacements in
refresh, and reject revoked or reused tokens. Add logout revocation support and
ensure token creation stores the necessary identifier while preserving existing
member and expiry handling.
| record OpenAiResponse(String id, String status, IncompleteDetails incompleteDetails, List<Output> output) { | ||
| @JsonIgnoreProperties(ignoreUnknown = true) | ||
| record IncompleteDetails(String reason) {} |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for a snake_case naming strategy or explicit `@JsonProperty` mapping for OpenAI DTOs
rg -n "PropertyNamingStrateg|SNAKE_CASE|JsonProperty" -g '*.java' src/main/java/com/team6/server/episode/infrastructure/openaiRepository: 8th-COKERTHON/server-team6
Length of output: 445
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== ObjectMapper / Jackson config =="
rg -n "ObjectMapper|Jackson2ObjectMapperBuilder|PropertyNamingStrategy|PropertyNamingStrategies|`@JsonNaming`|JsonAutoDetect|Jackson2ObjectMapperBuilderCustomizer" src/main/java src/test/java
echo
echo "== OpenAI DTO files =="
git ls-files 'src/main/java/com/team6/server/episode/infrastructure/openai/*.java'
echo
echo "== OpenAiResponse.java (numbered) =="
cat -n src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiResponse.java
echo
echo "== OpenAiRequest.java (numbered) =="
cat -n src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiRequest.javaRepository: 8th-COKERTHON/server-team6
Length of output: 4260
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== OpenAiConfig.java =="
cat -n src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiConfig.java
echo
echo "== Any Jackson configuration across main sources =="
rg -n "PropertyNamingStrategy|PropertyNamingStrategies|`@JsonNaming`|Jackson2ObjectMapperBuilderCustomizer|ObjectMapper" src/main/javaRepository: 8th-COKERTHON/server-team6
Length of output: 3291
Map incomplete_details explicitly. OpenAiResponse has no snake_case mapping here, so incompleteDetails will stay null when OpenAI returns incomplete_details. Add @JsonProperty("incomplete_details") here, or a shared snake_case strategy.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/team6/server/episode/infrastructure/openai/OpenAiResponse.java`
around lines 7 - 9, The IncompleteDetails field is not mapped from OpenAI’s
snake_case response property. In OpenAiResponse, annotate the incompleteDetails
component with `@JsonProperty`("incomplete_details") (or configure an equivalent
shared snake_case naming strategy) so the returned value is populated.
| @Version | ||
| @Column(nullable = false) | ||
| private Long version; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if optimistic lock conflicts are retried or mapped to a client-safe status
rg -n -C3 'OptimisticLockingFailureException|ObjectOptimisticLockingFailureException|`@Retryable`' src/main/java/com/team6/server/match/service/MatchService.java src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java 2>/dev/nullRepository: 8th-COKERTHON/server-team6
Length of output: 164
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the relevant service/exception handling files and any optimistic-lock handling
printf '\n== candidate files ==\n'
git ls-files | rg 'src/main/java/.*/(MatchService|GlobalExceptionHandler|.*ExceptionHandler|.*ControllerAdvice|.*Advice)\.java$|src/main/java/.*/ranking/.+\.java$'
printf '\n== repository search ==\n'
rg -n -C2 'ObjectOptimisticLockingFailureException|OptimisticLockingFailureException|OptimisticLock|`@Retryable`|ControllerAdvice|ExceptionHandler|ResponseStatusException|HttpStatus\.CONFLICT|409' src/main/java 2>/dev/null || trueRepository: 8th-COKERTHON/server-team6
Length of output: 10894
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map ranking entity and service usage around versioned saves
ast-grep outline src/main/java/com/team6/server/ranking/entity/RankingEpisodeScore.java || true
printf '\n== ranking score references ==\n'
rg -n -C2 'RankingEpisodeScore|applyDelta\(|updateTitle\(|getCurrentTitleId\(|save\(' src/main/java/com/team6/server 2>/dev/null || trueRepository: 8th-COKERTHON/server-team6
Length of output: 35153
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map ranking entity and service usage around versioned saves
ast-grep outline src/main/java/com/team6/server/ranking/entity/RankingEpisodeScore.java || true
printf '\n== ranking score references ==\n'
rg -n -C2 'RankingEpisodeScore|applyDelta\(|updateTitle\(|getCurrentTitleId\(\)|save\(' src/main/java/com/team6/server 2>/dev/null || trueRepository: 8th-COKERTHON/server-team6
Length of output: 35153
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,120p' src/main/java/com/team6/server/global/exception/GlobalExceptionHandler.java
printf '\n---\n'
sed -n '1,120p' src/main/java/com/team6/server/global/exception/ErrorCode.javaRepository: 8th-COKERTHON/server-team6
Length of output: 7292
Map optimistic-lock conflicts to 409
MatchService relies on @Version, but GlobalExceptionHandler has no handler for ObjectOptimisticLockingFailureException; these conflicts will fall through to the generic Exception handler and return 500. Add a conflict handler or retry the update.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/team6/server/ranking/entity/RankingEpisodeScore.java`
around lines 30 - 32, Update GlobalExceptionHandler to handle
ObjectOptimisticLockingFailureException explicitly and return HTTP 409 Conflict
with an appropriate response body; use the existing exception-handler
conventions and verify MatchService version conflicts no longer reach the
generic 500 handler.
| flyway: | ||
| enabled: true | ||
| enabled: false | ||
| locations: classpath:db/migration | ||
| validate-on-migrate: false | ||
| baseline-on-migrate: true |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd -e yml -e yaml . src/main/resourcesRepository: 8th-COKERTHON/server-team6
Length of output: 278
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- application.yml ---'
cat -n src/main/resources/application.yml
echo
echo '--- application-dev.yml ---'
cat -n src/main/resources/application-dev.yml
echo
echo '--- application-prod.yml ---'
cat -n src/main/resources/application-prod.ymlRepository: 8th-COKERTHON/server-team6
Length of output: 2232
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- Flyway config references ---'
rg -n "flyway|ddl-auto|JWT_SECRET|CORS_ALLOWED_ORIGINS|OPENAI_TITLE_SUGGESTION_ENABLED" src/main/resources src/main/java
echo
echo '--- Migration files ---'
fd -a . src/main/resources/db/migration
echo
echo '--- application-local.yml if present ---'
fd -a 'application-local.yml' src/main/resourcesRepository: 8th-COKERTHON/server-team6
Length of output: 1568
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "jwt\.secret|JWT_SECRET|secret-key|sign.*jwt|Jwt|jwt" src/main/java src/main/resources -g '!src/main/resources/application.yml'Repository: 8th-COKERTHON/server-team6
Length of output: 4900
Re-enable Flyway in the active config
spring.flyway.enabled: false leaves the new V2–V9 migrations unused, and hibernate.ddl-auto: update shifts schema management to Hibernate instead of the versioned SQL. Re-enable Flyway or drop the migration files if they are not meant to run.
- Remove the checked-in
JWT_SECRETfallback too; missing config should fail fast instead of booting with a shared default secret.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/resources/application.yml` around lines 14 - 18, In the active
application configuration, change spring.flyway.enabled to true so the versioned
V2–V9 migrations run, and replace hibernate.ddl-auto: update with a
migration-compatible setting such as validate. Remove the checked-in JWT_SECRET
fallback from the relevant security configuration and require the property so
startup fails when it is missing.
| server: | ||
| forward-headers-strategy: framework | ||
| jwt: | ||
| secret: ${JWT_SECRET:local-development-secret-key-must-be-at-least-32-bytes} |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Hardcoded default JWT signing secret fallback.
If JWT_SECRET isn't set, the app silently starts with a fixed, publicly-visible secret rather than failing fast. Consider removing the default (or gating it to a local profile only) so misconfigured non-local environments fail loudly instead of signing tokens with a known key.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/resources/application.yml` at line 22, Replace the hardcoded default
in the JWT secret configuration with a required unset fallback so startup fails
when JWT_SECRET is missing, or restrict the development fallback to an
explicitly activated local profile. Ensure non-local environments cannot use the
publicly known secret.
| ALTER TABLE episode_rankings | ||
| ALTER COLUMN title_score SET DEFAULT 1000, | ||
| DROP CHECK ck_episode_rankings_title_score, | ||
| ADD CONSTRAINT ck_episode_rankings_title_score CHECK (title_score >= 100); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify there is no duplicate Flyway version across migration filenames
fd -e sql . src/main/resources/db/migration | rg -oP '(?<=/)V\d+(?=__)' | sort | uniq -c | sort -rnRepository: 8th-COKERTHON/server-team6
Length of output: 263
Rename one of the V7 migrations. Flyway requires unique version numbers, and having both src/main/resources/db/migration/V7__add_ranking_period_index.sql and src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql will fail startup validation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql`
around lines 1 - 4, Rename the migration file containing the `ALTER TABLE
episode_rankings` statement so its Flyway version is unique and does not
conflict with `V7__add_ranking_period_index.sql`; preserve the migration
description and SQL contents.
🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win
Missing backfill before tightening the CHECK constraint.
title_score previously defaulted to 0 with a >= 0 check (V2/V4). Any existing row with title_score between 0–99 will violate the new title_score >= 100 check, and MySQL validates existing data when adding a CHECK constraint — this ALTER TABLE will fail at deploy time if such rows exist.
🛠️ Proposed fix: backfill before tightening the constraint
+UPDATE episode_rankings SET title_score = 1000 WHERE title_score < 100;
+
ALTER TABLE episode_rankings
ALTER COLUMN title_score SET DEFAULT 1000,
DROP CHECK ck_episode_rankings_title_score,
ADD CONSTRAINT ck_episode_rankings_title_score CHECK (title_score >= 100);📝 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.
| ALTER TABLE episode_rankings | |
| ALTER COLUMN title_score SET DEFAULT 1000, | |
| DROP CHECK ck_episode_rankings_title_score, | |
| ADD CONSTRAINT ck_episode_rankings_title_score CHECK (title_score >= 100); | |
| UPDATE episode_rankings SET title_score = 1000 WHERE title_score < 100; | |
| ALTER TABLE episode_rankings | |
| ALTER COLUMN title_score SET DEFAULT 1000, | |
| DROP CHECK ck_episode_rankings_title_score, | |
| ADD CONSTRAINT ck_episode_rankings_title_score CHECK (title_score >= 100); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/resources/db/migration/V7__align_episode_ranking_score_bounds.sql`
around lines 1 - 4, Backfill existing rows before tightening the constraint: in
the migration, update `episode_rankings` rows with `title_score` below 100 to a
valid value (such as 100), then alter the default and replace
`ck_episode_rankings_title_score` with the `title_score >= 100` check.
| ALTER TABLE matching_events | ||
| ADD COLUMN period_key VARCHAR(20) NULL AFTER event_type, | ||
| ADD COLUMN match_type VARCHAR(30) NOT NULL DEFAULT 'RIVAL' AFTER period_key, | ||
| ADD COLUMN score_multiplier DECIMAL(4,2) NOT NULL DEFAULT 1.00 AFTER score_reward, |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
score_multiplier has no range check.
Nothing prevents a negative or zero multiplier, which would corrupt score calculations downstream.
🛡️ Suggested constraint
ALTER TABLE matching_events
...
+ ADD CONSTRAINT ck_matching_events_score_multiplier CHECK (score_multiplier > 0),
ADD CONSTRAINT uk_matching_events_type_period UNIQUE (event_type, period_key);Also applies to: 13-13
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/resources/db/migration/V9__extend_matches_and_show_sessions.sql` at
line 4, Update the migration definitions for score_multiplier in both referenced
locations to enforce a strictly positive value with a database CHECK constraint,
while retaining the existing DECIMAL type and default. Ensure the constraint is
applied consistently to every definition of this column.
개발 코드 메인 브랜치로 머지
Summary by CodeRabbit