Skip to content

feat: 모의지원 최대 지망 수를 동적으로 관리하도록#746

Merged
whqtker merged 24 commits into
developfrom
feat/733-dynamic-choice-count
Jun 15, 2026
Merged

feat: 모의지원 최대 지망 수를 동적으로 관리하도록#746
whqtker merged 24 commits into
developfrom
feat/733-dynamic-choice-count

Conversation

@whqtker

@whqtker whqtker commented Jun 9, 2026

Copy link
Copy Markdown
Member

관련 이슈

작업 내용

1. 엔티티 구조 변경

  • Application 엔티티에서 firstChoice / secondChoice / thirdChoiceUnivApplyInfoId 컬럼을 제거했습니다.
  • @Embeddable 클래스 ApplicationChoice 를 추가했습니다. (잘 몰랐는데 @ElementCollection 매핑 대상 클래스는 @Embeddable이 있어야 한다고 하더라고요 ..)
  • application_choice@ElementCollection으로 매핑했습니다.
@Query("""
       SELECT DISTINCT a
       FROM Application a
       JOIN a.choices c
       WHERE c.univApplyInfoId IN :univApplyInfoIds
           AND a.verifyStatus = :status
           AND a.termId = :termId
           AND a.isDelete = false
       """)
List<Application> findAllByUnivApplyInfoIds(
        @Param("univApplyInfoIds") List<Long> univApplyInfoIds,
        @Param("status") VerifyStatus status,
        @Param("termId") long termId);
  • Application을 조회하는 과정에서, ApplicationChoice 컬렉션을 로드하기 위해 추가 쿼리가 발생할 수 있습니다(N+1 문제). 이를 방지하기 위해 @BatchSize(size = 100)를 적용했습니다.
  • HomeUniversity 엔티티에 maxChoiceCount 컬럼을 추가했습니다.
  • 모의지원 시 최대 지망 수를 초과 시 처리도 구현되었습니다.

2. DTO 구조 변경

  • 어드민에서 국내 대학 정보 추가 시 요청 DTO에 maxChoiceCount 또한 받도록 수정했습니다.
  • 모의지원 관련 DTO에서 3지망 고정 필드를 List<> 타입으로 변경했습니다.

3. 검증 로직 변경

  • 기존의 2지망이 있어야 2지망이 있어야 한다 와 같은 검증을 변경했습니다. (Validator)

4. 스키마 관련

  • 1, 2, 3지망에 대한 인덱스를 삭제했습니다.
  • 추후 데이터 마이그레이션 후 컬럼 또한 삭제할 예정입니다.

5. 테스트 코드 변경

특이 사항

리뷰 요구사항 (선택)

작업 범위가 꽤 큽니다. 엣지 케이스 위주로 봐주시면 감사하겠습니다 !

@whqtker whqtker self-assigned this Jun 9, 2026
@whqtker whqtker requested review from Gyuhyeok99 and wibaek as code owners June 9, 2026 08:49
@whqtker whqtker added the 기능 label Jun 9, 2026
@whqtker whqtker added the 최종 리뷰 최소 1명 필수 label Jun 9, 2026
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a33ca37c-34d6-407f-aa6a-d2e455531739

📥 Commits

Reviewing files that changed from the base of the PR and between bf5cbfa and 4d6b3b5.

📒 Files selected for processing (5)
  • src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java
  • src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java
  • src/main/java/com/example/solidconnection/university/domain/HomeUniversity.java
  • src/test/java/com/example/solidconnection/application/service/ApplicationSubmissionServiceTest.java
  • src/test/java/com/example/solidconnection/university/fixture/HomeUniversityFixture.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/com/example/solidconnection/university/domain/HomeUniversity.java
  • src/test/java/com/example/solidconnection/application/service/ApplicationSubmissionServiceTest.java
  • src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java

🎯 Walkthrough

  1. 홈대학 최대 지망 수 정의: HomeUniversity에 DEFAULT_MAX_CHOICE_COUNT 상수(기본값 3)와 maxChoiceCount 필드를 추가했으며, update 메서드가 name과 maxChoiceCount를 함께 처리합니다.

  2. 관리자 협정대학 관리 확장: AdminHomeUniversityCreateRequest, AdminHomeUniversityUpdateRequest, AdminHomeUniversityResponse가 maxChoiceCount를 요청·응답·저장 구조에 포함하고, 서비스 로직도 생성/수정 시 해당 값을 반영합니다.

  3. 지원서 선택 도메인 모델 전환: Application 엔티티의 고정 3개 선택 필드(firstChoiceUnivApplyInfoId 등)를 List<ApplicationChoice> choices 컬렉션으로 통합하고, ApplicationChoice는 choiceOrder와 univApplyInfoId를 담는 JPA 임베더블입니다.

  4. 데이터베이스 스키마 변경: V50 마이그레이션에서 home_university에 max_choice_count 컬럼을 추가(NOT NULL DEFAULT 3, CHECK >= 1)하고, 새로운 application_choice 테이블(복합 기본키: application_id, choice_order)을 생성했습니다.

  5. 선택 요청 계약 단순화: UnivApplyInfoChoiceRequest가 3개의 개별 필드 대신 @JsonProperty("choices")로 매핑되는 List<Long> univApplyInfoIds 단일 필드를 받도록 변경되었습니다.

  6. 선택 검증 규칙 개선: ValidUnivApplyInfoChoiceValidator가 목록의 빈값·NULL 항목·중복을 검사하도록 재구성되었으며, 에러 코드에 INVALID_UNIV_APPLY_INFO_CHOICE(빈/NULL)와 CHOICE_COUNT_EXCEEDS_LIMIT(개수 초과)가 추가되었습니다.

  7. 지원서 제출 흐름 재구성: ApplicationSubmissionService.apply()가 resolveMaxChoiceCount로 홈대학 기준 상한 결정, validateChoiceCount로 검증, buildChoices로 IntStream 기반 ApplicationChoice 생성, 일괄 UnivApplyInfo 조회를 순차적으로 수행합니다.

  8. 조회 쿼리와 분류 로직 갱신: ApplicationRepository.findAllByUnivApplyInfoIds가 choices 컬렉션을 JOIN하여 필터링하고, ApplicationQueryService는 가변 maxChoiceCount 범위(1..maxChoiceCount)로 순회하며 order별 분류 맵을 구성합니다.

  9. 응답 스키마 리스트 통합: ApplicationsResponse, UnivApplyInfoResponse, AdminUnivApplyInfoResponse가 3개의 개별 필드 대신 단일 List<T> choices 필드를 선언하고, 관련 팩토리·매핑 로직이 choiceOrder 정렬 기반으로 재작성되었습니다.

  10. SiteUser 조회 최적화: SiteUserFilterRepositoryImpl.fetchUnivApplyInfo가 최신 Application의 choices를 choiceOrder로 정렬하여 univApplyInfoId를 수집하고, univApplyInfo 이름 매핑으로 UnivApplyInfoResponse를 생성합니다.

  11. 픽스처·테스트 일괄 갱신: ApplicationFixture, ApplicationFixtureBuilder, HomeUniversityFixture, HomeUniversityFixtureBuilder가 리스트 기반 선택 구성으로 통일되었고, 모든 관련 테스트 케이스(AdminHomeUniversityServiceTest, ApplicationSubmissionServiceTest, ApplicationQueryServiceTest, ValidUnivApplyInfoChoiceValidatorTest)가 List.of(...) 생성 방식으로 정리되었습니다.

🎯 Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

📋 Suggested Reviewers

  • wibaek
  • lsy1307
  • JAEHEE25
  • Hexeong
  • sukangpunch
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 변경의 핵심을 명확하게 요약하고 있습니다. '모의지원 최대 지망 수를 동적으로 관리'는 이 PR의 주요 목표를 직관적으로 전달합니다.
Description check ✅ Passed PR 설명이 구조화된 템플릿을 따르고 있으며, 관련 이슈, 5가지 주요 작업 내용 및 리뷰 요구사항을 명확히 제시하고 있습니다.
Linked Issues check ✅ Passed PR의 코드 변경사항들이 이슈 #744의 핵심 요구사항인 '국내 대학별 최대 지망 수 동적 관리'를 충실히 구현하고 있습니다. 엔티티 구조 변경, DTO 수정, 검증 로직 개선, 스키마 마이그레이션 등 모든 변경이 해당 목표에 정렬되어 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 동적 지망 수 관리 기능 구현에 직접적으로 관련되어 있으며, 범위를 벗어난 독립적인 기능이나 리팩토링은 관찰되지 않습니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/733-dynamic-choice-count

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 074b5408cb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/main/resources/db/migration/V50__dynamic_choice_count.sql

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

Caution

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

⚠️ Outside diff range comments (1)
src/test/java/com/example/solidconnection/admin/university/service/AdminHomeUniversityServiceTest.java (1)

182-191: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

2) \t같은 이름 수정 성공 케이스에 maxChoiceCount 검증이 빠져 있습니다

요청에 maxChoiceCount를 넣었는데 성공 검증은 이름만 확인하고 있어, 수정 매핑 누락 회귀를 놓칠 수 있습니다. 응답(또는 DB 재조회)에서 maxChoiceCount도 함께 단언해 주세요.

🤖 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/test/java/com/example/solidconnection/admin/university/service/AdminHomeUniversityServiceTest.java`
around lines 182 - 191, The test method 같은_이름으로_수정하면_성공한다 in
AdminHomeUniversityServiceTest is only asserting the name was updated but
ignores maxChoiceCount; update the assertion to also verify maxChoiceCount was
changed (either assert response.maxChoiceCount() equals the request value 3 or
reload the entity from the repository and assert
homeUniversity.getMaxChoiceCount() == 3) so the AdminHomeUniversityUpdateRequest
-> adminHomeUniversityService.updateHomeUniversity mapping for maxChoiceCount is
covered.
🧹 Nitpick comments (1)
src/main/java/com/example/solidconnection/application/domain/ApplicationChoice.java (1)

7-24: ⚡ Quick win

equals()hashCode() 구현을 권장합니다.

  1. 현재 상태

    • @Embeddable 클래스에 equals()/hashCode()가 없습니다.
  2. 잠재적 문제

    • Hibernate는 @ElementCollection의 변경 감지 시 equals()를 사용합니다.
    • 기본 Object identity 비교로 인해 의도치 않은 dirty checking 동작이 발생할 수 있습니다.
    • 컬렉션 내 요소 비교 및 제거 시 예상과 다르게 동작할 수 있습니다.
♻️ equals/hashCode 구현 제안
 `@Embeddable`
 `@Getter`
 public class ApplicationChoice {

     `@Column`(name = "choice_order", nullable = false)
     private int choiceOrder;

     `@Column`(name = "univ_apply_info_id", nullable = false)
     private long univApplyInfoId;

     protected ApplicationChoice() {
     }

     public ApplicationChoice(int choiceOrder, long univApplyInfoId) {
         this.choiceOrder = choiceOrder;
         this.univApplyInfoId = univApplyInfoId;
     }
+
+    `@Override`
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ApplicationChoice that = (ApplicationChoice) o;
+        return choiceOrder == that.choiceOrder && univApplyInfoId == that.univApplyInfoId;
+    }
+
+    `@Override`
+    public int hashCode() {
+        return Objects.hash(choiceOrder, univApplyInfoId);
+    }
 }

java.util.Objects import 추가가 필요합니다.

🤖 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/example/solidconnection/application/domain/ApplicationChoice.java`
around lines 7 - 24, Implement proper equality for the embeddable
ApplicationChoice by adding equals(Object) and hashCode() that base equality on
the fields choiceOrder and univApplyInfoId (use java.util.Objects for null-safe
comparison / hashing); update ApplicationChoice to include these methods so
Hibernate’s ElementCollection dirty-checking and collection operations work
correctly—add the import java.util.Objects and implement equals to check
instance/type then compare choiceOrder and univApplyInfoId, and implement
hashCode to return Objects.hash(choiceOrder, univApplyInfoId).
🤖 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
`@src/main/java/com/example/solidconnection/application/dto/UnivApplyInfoResponse.java`:
- Around line 17-20: The mapping for choiceNames in UnivApplyInfoResponse can
insert nulls because nameById.get(choice.getUnivApplyInfoId()) may return null;
update the logic that builds choiceNames (the stream using
ApplicationChoice::getChoiceOrder and ApplicationChoice::getUnivApplyInfoId) to
defensively handle missing keys—either filter out choices with no name, replace
nulls with a sensible default (e.g., empty string or "Unknown"), or throw a
clear exception when data invariants are violated; ensure the chosen behavior is
applied consistently and documented in UnivApplyInfoResponse construction.

In
`@src/main/java/com/example/solidconnection/siteuser/repository/custom/SiteUserFilterRepositoryImpl.java`:
- Around line 350-358: The current collect into Map<Long,String> using
tuples.stream().collect(Collectors.toMap(...)) will NPE if
t.get(univApplyInfo.koreanName) is null and may produce nulls in the resulting
choiceNames when nameById lacks an entry; update the collection logic to
defensively handle null values by supplying a non-null value or merge function
to Collectors.toMap (use t.get(univApplyInfo.koreanName) == null ? "" :
thatValue or similar) and then when building choiceNames from univApplyInfoIds
map missing IDs to a safe default (e.g., empty string) or filter them out so
choiceNames contains no nulls; reference the tuples stream, univApplyInfo.id,
univApplyInfo.koreanName, nameById map and univApplyInfoIds when applying the
fix.

In
`@src/main/java/com/example/solidconnection/university/domain/HomeUniversity.java`:
- Around line 28-34: HomeUniversity의 maxChoiceCount가 0 또는 음수가 들어갈 수 있어 도메인 검증을
추가해야 합니다; HomeUniversity 클래스의 생성자와 update(String name, int maxChoiceCount) 메서드에서
maxChoiceCount <= 0일 경우 IllegalArgumentException(또는 도메인 전용 예외)을 던지도록 검증을 추가하고,
메시지는 "maxChoiceCount must be >= 1"처럼 명확히 표현하세요; 또한 필드 선언부나 JPA 엔티티
제약(annotation)만으로는 부족하므로 비즈니스 요구(최소 1)를 반영하는 검증 로직을 생성자와 update 메서드에 중복 없이 호출되는
private validateMaxChoiceCount(int) 같은 헬퍼 메서드로 분리해 재사용하세요.

In `@src/main/resources/db/migration/V50__dynamic_choice_count.sql`:
- Around line 4-12: Add a foreign key constraint on
application_choice.univ_apply_info_id referencing univ_apply_info(id) to enforce
referential integrity (e.g., name it fk_app_choice_univ_apply_info) and also
create an index on application_choice.univ_apply_info_id to improve lookup
performance; update the CREATE TABLE or add ALTER TABLE / CREATE INDEX
statements that reference the application_choice table and the univ_apply_info
id column accordingly.

In
`@src/test/java/com/example/solidconnection/application/fixture/ApplicationFixtureBuilder.java`:
- Around line 66-73: The ApplicationFixtureBuilder currently constructs
Application with a hard-coded max choice count of 1, which prevents tests from
exercising multi-choice scenarios; update the builder to accept and store a
configurable maxChoiceCount parameter (exposed on the builder's
API/constructor), replace the literal 1 in the Application(...) call with that
stored maxChoiceCount, and update any builder creation sites/tests to pass the
desired maxChoiceCount so fixtures can create 2+ choice applications when needed
(reference ApplicationFixtureBuilder and the Application(...) constructor call
and the maxChoiceCount builder field).

In
`@src/test/java/com/example/solidconnection/application/service/ApplicationQueryServiceTest.java`:
- Around line 127-131: The test in ApplicationQueryServiceTest currently uses
assertThat(response.choices().get(0)).containsAll(...), which only checks for
presence and can miss extra/unexpected entries; update the assertion to assert
strict equality regardless of order by replacing containsAll with
containsExactlyInAnyOrder so that ApplicantsResponse.of(...) entries must match
exactly (use response.choices().get(0) and the same ApplicantsResponse.of(...)
expected values).

In
`@src/test/java/com/example/solidconnection/university/fixture/HomeUniversityFixtureBuilder.java`:
- Around line 31-33: The create() method in HomeUniversityFixtureBuilder
currently reuses an existing HomeUniversity found by name via
homeUniversityRepository.findByName(name) without verifying maxChoiceCount,
causing tests requesting a different maxChoiceCount to get the wrong fixture;
update create() so that after finding an existing HomeUniversity you compare its
getMaxChoiceCount() to the builder's maxChoiceCount and if they differ set the
new value on the entity and persist it (e.g., call
homeUniversityRepository.save(existing) or an update method), otherwise return
the existing entity; reference HomeUniversityFixtureBuilder.create,
homeUniversityRepository.findByName, HomeUniversity.getMaxChoiceCount(), and
homeUniversityRepository.save() when making the change.

---

Outside diff comments:
In
`@src/test/java/com/example/solidconnection/admin/university/service/AdminHomeUniversityServiceTest.java`:
- Around line 182-191: The test method 같은_이름으로_수정하면_성공한다 in
AdminHomeUniversityServiceTest is only asserting the name was updated but
ignores maxChoiceCount; update the assertion to also verify maxChoiceCount was
changed (either assert response.maxChoiceCount() equals the request value 3 or
reload the entity from the repository and assert
homeUniversity.getMaxChoiceCount() == 3) so the AdminHomeUniversityUpdateRequest
-> adminHomeUniversityService.updateHomeUniversity mapping for maxChoiceCount is
covered.

---

Nitpick comments:
In
`@src/main/java/com/example/solidconnection/application/domain/ApplicationChoice.java`:
- Around line 7-24: Implement proper equality for the embeddable
ApplicationChoice by adding equals(Object) and hashCode() that base equality on
the fields choiceOrder and univApplyInfoId (use java.util.Objects for null-safe
comparison / hashing); update ApplicationChoice to include these methods so
Hibernate’s ElementCollection dirty-checking and collection operations work
correctly—add the import java.util.Objects and implement equals to check
instance/type then compare choiceOrder and univApplyInfoId, and implement
hashCode to return Objects.hash(choiceOrder, univApplyInfoId).
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9303249c-c45a-4c36-8c1e-356aff3eeb78

📥 Commits

Reviewing files that changed from the base of the PR and between 022bde6 and 074b540.

📒 Files selected for processing (27)
  • src/main/java/com/example/solidconnection/admin/dto/UnivApplyInfoResponse.java
  • src/main/java/com/example/solidconnection/admin/university/dto/AdminHomeUniversityCreateRequest.java
  • src/main/java/com/example/solidconnection/admin/university/dto/AdminHomeUniversityResponse.java
  • src/main/java/com/example/solidconnection/admin/university/dto/AdminHomeUniversityUpdateRequest.java
  • src/main/java/com/example/solidconnection/admin/university/service/AdminHomeUniversityService.java
  • src/main/java/com/example/solidconnection/application/domain/Application.java
  • src/main/java/com/example/solidconnection/application/domain/ApplicationChoice.java
  • src/main/java/com/example/solidconnection/application/dto/ApplicationsResponse.java
  • src/main/java/com/example/solidconnection/application/dto/UnivApplyInfoChoiceRequest.java
  • src/main/java/com/example/solidconnection/application/dto/UnivApplyInfoResponse.java
  • src/main/java/com/example/solidconnection/application/repository/ApplicationRepository.java
  • src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java
  • src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java
  • src/main/java/com/example/solidconnection/siteuser/repository/custom/SiteUserFilterRepositoryImpl.java
  • src/main/java/com/example/solidconnection/university/domain/HomeUniversity.java
  • src/main/java/com/example/solidconnection/university/dto/validation/ValidUnivApplyInfoChoiceValidator.java
  • src/main/resources/db/migration/V50__dynamic_choice_count.sql
  • src/test/java/com/example/solidconnection/admin/service/AdminUserServiceTest.java
  • src/test/java/com/example/solidconnection/admin/university/service/AdminHomeUniversityServiceTest.java
  • src/test/java/com/example/solidconnection/application/fixture/ApplicationFixture.java
  • src/test/java/com/example/solidconnection/application/fixture/ApplicationFixtureBuilder.java
  • src/test/java/com/example/solidconnection/application/service/ApplicationQueryServiceTest.java
  • src/test/java/com/example/solidconnection/application/service/ApplicationSubmissionServiceTest.java
  • src/test/java/com/example/solidconnection/university/dto/validation/ValidUnivApplyInfoChoiceValidatorTest.java
  • src/test/java/com/example/solidconnection/university/fixture/HomeUniversityFixture.java
  • src/test/java/com/example/solidconnection/university/fixture/HomeUniversityFixtureBuilder.java

Comment thread src/main/resources/db/migration/V50__dynamic_choice_count.sql
@whqtker whqtker changed the title feat: 최대 지망 수를 동적으로 관리하도록 feat: 모의지원 최대 지망 수를 동적으로 관리하도록 Jun 9, 2026
whqtker added 5 commits June 10, 2026 09:29
- 저장 전에 검증하여 잘못된 id가 DB에 삽입되는 경우 방지
- 중복 조회 제거
- 최대 1지망 지원 가능
- 응답의 개수를 정확히 판별하기 위해

@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.

Caution

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

⚠️ Outside diff range comments (2)
src/main/resources/db/migration/V50__dynamic_choice_count.sql (2)

5-17: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

2. 동일 지원서의 중복 지망을 DB가 막지 못합니다.

현재 PK가 (application_id, choice_order)라서 같은 univ_apply_info_id를 다른 순번으로 여러 번 넣을 수 있습니다. 이번 변경에서 중복 지망을 invalid로 보는 계약이라면, 스키마에도 (application_id, univ_apply_info_id) 유니크 제약을 같이 두는 편이 안전합니다.

🤖 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/V50__dynamic_choice_count.sql` around lines 5
- 17, The schema allows the same univ_apply_info_id to be added multiple times
for one application because the PK is (application_id, choice_order); add a
unique constraint on (application_id, univ_apply_info_id) to prevent duplicate
choices. Modify the application_choice table definition to include a UNIQUE
constraint (or create a unique index) named e.g. ux_app_choice_app_univ
(referencing table application_choice and columns application_id and
univ_apply_info_id) so the DB enforces uniqueness at insert/update.

5-20: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

1. 기존 지원서의 지망 데이터 이관 단계가 빠져 있습니다.

Application 저장 모델을 application_choice로 전환하는데, 이 마이그레이션에는 기존 first/second/third 지망 값을 새 테이블로 옮기는 단계가 없습니다. 지금 상태로 배포하면 기존 지원서는 choices가 비어 있는 것으로 읽혀서 조회/분류 결과가 깨질 가능성이 큽니다. 최소한 백필 SQL을 같은 릴리스에 포함하거나, 엔티티 전환 전후를 분리한 2단계 배포로 가야 합니다.

🤖 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/V50__dynamic_choice_count.sql` around lines 5
- 20, The migration creates a new application_choice table but omits backfilling
existing first/second/third choice columns, so existing Application rows will
lose choices; add steps to migrate data from
application.first_choice_university_info_for_apply_id,
application.second_choice_university_info_for_apply_id and
application.third_choice_university_info_for_apply_id into application_choice
(INSERT INTO application_choice (application_id, choice_order,
univ_apply_info_id) SELECT id, 1/2/3, <column> FROM application WHERE <column>
IS NOT NULL) before dropping or changing those columns, or split this into two
migrations (1: add table and backfill, 2: remove old columns) to ensure no data
loss and consistent reads; reference the application_choice table and the
columns
first_choice_university_info_for_apply_id/second_choice_university_info_for_apply_id/third_choice_university_info_for_apply_id
and the application primary key to implement the backfill and ordering.
🤖 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.

Outside diff comments:
In `@src/main/resources/db/migration/V50__dynamic_choice_count.sql`:
- Around line 5-17: The schema allows the same univ_apply_info_id to be added
multiple times for one application because the PK is (application_id,
choice_order); add a unique constraint on (application_id, univ_apply_info_id)
to prevent duplicate choices. Modify the application_choice table definition to
include a UNIQUE constraint (or create a unique index) named e.g.
ux_app_choice_app_univ (referencing table application_choice and columns
application_id and univ_apply_info_id) so the DB enforces uniqueness at
insert/update.
- Around line 5-20: The migration creates a new application_choice table but
omits backfilling existing first/second/third choice columns, so existing
Application rows will lose choices; add steps to migrate data from
application.first_choice_university_info_for_apply_id,
application.second_choice_university_info_for_apply_id and
application.third_choice_university_info_for_apply_id into application_choice
(INSERT INTO application_choice (application_id, choice_order,
univ_apply_info_id) SELECT id, 1/2/3, <column> FROM application WHERE <column>
IS NOT NULL) before dropping or changing those columns, or split this into two
migrations (1: add table and backfill, 2: remove old columns) to ensure no data
loss and consistent reads; reference the application_choice table and the
columns
first_choice_university_info_for_apply_id/second_choice_university_info_for_apply_id/third_choice_university_info_for_apply_id
and the application primary key to implement the backfill and ordering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 818d8abc-33fd-4589-8a06-f66ff8b8d3e8

📥 Commits

Reviewing files that changed from the base of the PR and between 074b540 and bf5cbfa.

📒 Files selected for processing (6)
  • src/main/java/com/example/solidconnection/application/domain/Application.java
  • src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java
  • src/main/resources/data.sql
  • src/main/resources/db/migration/V50__dynamic_choice_count.sql
  • src/test/java/com/example/solidconnection/application/service/ApplicationQueryServiceTest.java
  • src/test/java/com/example/solidconnection/application/service/ApplicationSubmissionServiceTest.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/test/java/com/example/solidconnection/application/service/ApplicationSubmissionServiceTest.java
  • src/test/java/com/example/solidconnection/application/service/ApplicationQueryServiceTest.java
  • src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java
  • src/main/java/com/example/solidconnection/application/domain/Application.java

@sukangpunch sukangpunch left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

전반적으로 고정 3지망 구조에서 동적 지망 수로 잘 전환되었습니다. 아래 몇 가지 개선 포인트를 남깁니다.

@whqtker whqtker merged commit 1bb075c into develop Jun 15, 2026
3 of 4 checks passed
whqtker added a commit that referenced this pull request Jun 18, 2026
* feat: 모의지원 최대 지망 수를 동적으로 관리하도록 (#746)

* chore: application_choice 테이블 추가 및 지망 수 컬럼 추가하는 스크립트 작성

* feat: ApplicationChoice 클래스 추가, 최대 지망 수 필드 추가

* chore: n지망 컬럼 제약 조건 변경

- 컬럼 복수 유지에 대한 임시 조치

* feat: DTO 및 검증 로직 변경

* feat: JPQL 쿼리 변경

* feat: 서비스 메서드 변경

* feat: 어드민 국내 대학 삽입 시 최대 지망 수 관련 DTO에 제약조건 추가

* feat: 대학 검색 응답 필드 변경

* feat: 최대 지망 수 포함하도록 서비스 메서드 변경

* feat: QueryDSL 조인 -> 동적 선택으로 변경

* test: 테스트 픽스처 및 테스트 변경

* refactor: 컨벤션에 맞게 로직 변경

- private 메서드 위치
- Wrapper 타입
- 메서드 레퍼런스 방식

* refactor: 지망 리스트에서 null인 경우 검증 추가

* refactor: 초기 상태에서도 maxChoiceCount만큼의 리스트 크기가 생성되도록

* refactor: 불필요한 검증 제거

* refactor: BatchSize로 N+1 문제 해결

* test: 불필요한 Nested 제거

* chore: mock데이터 수정

* refactor: 유효한 id 검증을 서비스에 추가

- 저장 전에 검증하여 잘못된 id가 DB에 삽입되는 경우 방지
- 중복 조회 제거

* chore: CHECK 제약조건 추가

- 최대 1지망 지원 가능

* chore: FK, 인덱스 추가

* test: containsExactlyInAnyOrder로 변경

- 응답의 개수를 정확히 판별하기 위해

* refactor: 상수 도메인에서 중앙 관리하도록

* test: 최대 지망 대학임을 바로 알 수 있도록 메서드명 변경

* feat: 컬럼 매핑 alias enum class 작성

* feat: 마크다운 표 파서 클래스 추가

* feat: DTO 작성

* refactor: deprecated 컬럼 제거

* feat: 서비스 계층 로직 작성

* feat: 지원 대학 정보 삽입 관련 로직 추가

* feat: 컨트롤러 작성

* fix: 중간 빈 셀이 있는 경우 정상적으로 저장이 되지 않던 문제 수정

* chore: 어드민 관련 계정 추가 외

- admin@test.email
- Admin@1234
- 로컬 어드민 웹에 대한 접근 허용

* feat: 학기 추가, 조회, 현재 학기 설정 관련 어드민 API 추가

* refactor: 현재 학기 설정 관련 무결성 제약조건 위반하지 않도록 Modifying 쿼리 사용

* refactor: 누락 필드 임시 추가

- 추후 제거 예정

* feat: host_university 테이블 컬럼 또한 입력으로 받도록

- host_university가 없는 경우 추가해줘야 한다.

* fix: 컬럼명 오타 수정

* feat: 세부 레코드 삽입 실패 이유까지 전달하도록

* feat: 셀 단위 타입·열거형·길이 제약 검증 추가

* test: enum 변환 실패 시 행 실패 동작으로 테스트 수정

* test: 셀 단위 타입·길이 검증 테스트 추가

* fix: 마크다운 파서에서 이스케이프된 파이프 문자 처리

* refactor: 지원 대학 삽입 응답에서 각 셀 검증 제거

* test: 각 셀 검증 제거에 따른 테스트 수정

* refactor: 학기 이름 관련 변수명 수정

- label -> name

* refactor: 불명확한 변수명 수정

- 활성화할 학기에 대한 변수 이름을 termToActivate로 수정한다.

* refactor: 부분 실패 대신 전체 실패

* chore: 국가 코드 추가

* refactor: 비어있는 키-값에 대한 검증 추가

* refactor: 컬럼 길이 제한 수정

- 영문 대학명: 200자
- 기숙사, 어학 세부 조건: 2000자

* refactor: 코드래빗 리뷰 반영

* refactor: 컨벤션 위반 항목 전면 수정

* chore: 두 스크립트 하나로 병합

* feat: 지원 대학 추가 시 캐시 무효화

* chore: 목데이터 수정

* chore: 어드민 기능 테스트 관련 목데이터 추가
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

기능 최종 리뷰 최소 1명 필수

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 국내 대학별로 최대 지망 수를 동적으로 관리한다.

2 participants