Skip to content

Conversation

@Gyuhyeok99
Copy link
Contributor

관련 이슈

작업 내용

변경 사항이 상당합니다.. 작업의 히스토리는 그동안 현재 학기를 yml로 관리해서 학기가 바뀔 때마다 yml 수정해서 secret에 pr날리고 서브모듈 업데이트해서 또 pr을 날리는데 이걸 dev -> release -> master까지 해줘야했습니다.

학기 하나 수정하자고 너무 불필요한 작업들이 많은 거 같아 join을 한 번 더 하더라도 테이블로 관리하자고 이야기가 나왔어서 미루고 미루다 이번에 작업했습니다!

이게 반영되면 추후 관리자 api로 학기 변경하는 기능도 개발하도록 하겠습니다!

특이 사항

flyway 스크립트를 아직 작성하지 못했습니다..
과정이

  1. Term 테이블 생성
  2. 기존에 사용하던 term 값들을 Term 테이블에 INSERT
  3. 현재 학기 설정 (가장 최근 학기를 current로)
  4. 각 테이블에 term_id 컬럼 추가 (임시로 nullable) (application, mentor, university_info_for_apply)
  5. 기존 term(String) 값을 term_id(Long)로 매핑
  6. term_id를 NOT NULL로 변경 (application, mentor, university_info_for_apply)
  7. application 내 기존 인덱스 삭제
  8. application 내 새 인덱스 생성 (term -> term_id)
  9. 기존 term 컬럼 삭제
  10. term_id Foreign Key 추가 (application, mentor, university_info_for_apply)

인데 이 7-8번 과정을 어떻게 해야하는지 잘 모르겠네요.. 그냥 삭제하려하니까 fk 문제도 걸리더라구요..
이걸 어떻게 성공적으로 마이그레이션할지 잘 고민해야할 거 같습니다

리뷰 요구사항 (선택)

변경사항이 상당한 노가다였어서.. 실수한 부분이 존재할 수 있긴 합니다..! 저도 다시 검토해보겠습니다!

@Gyuhyeok99 Gyuhyeok99 self-assigned this Oct 8, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 8, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

  1. Term 엔티티 도입. 새로운 Term 엔티티와 TermRepository, 현재 학기 조회 메서드가 추가되었습니다.
  2. 모델 필드 변경. Application, Mentor, UnivApplyInfo 등에서 term:String → termId:long으로 필드 및 DB 컬럼이 변경되었습니다.
  3. 리포지토리/서비스 변경. 여러 쿼리와 메서드 시그니처가 termId 기반으로 업데이트되고 서비스들은 TermRepository를 통해 현재 학기를 조회하도록 전환되었습니다.
  4. DTO/팩토리 변경. 여러 Mentor/UnivApplyInfo 관련 DTO 팩토리 메서드가 termName(문자열) 인자를 받도록 시그니처가 변경되었습니다.
  5. 테스트 인프라 변경. TermFixture, TermFixtureBuilder, TermRepositoryForTest 등 테스트 픽스처 추가 및 모든 테스트에서 termId/termName 사용으로 전환되었습니다.
  6. DB 마이그레이션 추가. term 테이블 생성, 기존 term 값 마이그레이션, term_id 컬럼 추가 및 FK/인덱스 재구성(V36__migrate_term_to_table.sql) 및 data.sql 업데이트가 포함됩니다.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 분

  • 데이터베이스 마이그레이션 스크립트 (V36__migrate_term_to_table.sql)의 로직, 동적 FK 드롭/생성, 데이터 이관 및 롤백/중복 처리 검증
  • 서비스 계층 전환(특히 UnivApplyInfoQueryService, UnivApplyInfoRecommendService, ApplicationQuery/Submission)에서 현재 학기 조회 실패 흐름 및 캐싱/트랜잭션 영향 확인
  • 리포지토리 쿼리 및 JPQL/네이티브 쿼리의 파라미터/인덱스 변경(문자열→숫자)으로 인한 성능·정확성 검토
  • DTO 팩토리 시그니처 변경에 따른 호출처(멘토/유니브 관련 서비스와 배치 쿼리) 전체 전파 확인
  • 테스트 픽스처 추가 및 기존 테스트들의 수정 일관성(TermFixture 초기화 순서, TermRepositoryForTest 동작) 확인

Suggested reviewers

  • wibaek
  • Hexeong
  • JAEHEE25
  • lsy1307

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목 '학기를 테이블로 관리하도록 변경'은 이 PR의 핵심 변경사항을 명확하게 전달합니다. 수정 사항이 상당하지만, 제목은 주요 목적(yml/secret 기반 학기 관리 → DB 테이블 기반 관리)을 간결하고 정확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명이 필수 항목들을 충실히 포함하고 있습니다. 관련 이슈(#418)를 명시했고, 변경 배경과 작업 내용을 상세히 설명했으며, 마이그레이션 절차 10단계를 문서화했습니다. Flyway 스크립트 미작성과 인덱스 처리 관련 우려사항도 특이 사항으로 명확히 기록했고, 리뷰 요구사항을 표시했습니다. 다만 NONE 상태의 마이그레이션 스크립트 완성도 개선 여지가 있습니다.
Linked Issues check ✅ Passed 이 PR은 #418에서 요구하는 세 가지 주요 목표를 충족합니다: (1) Term 테이블 도입 및 관련 테이블들(application, mentor, university_info_for_apply)에 termId 추가, (2) 기존 String term에서 long termId로의 마이그레이션, (3) 마이그레이션 절차 문서화. 코드 변경사항이 이 세 목표와 일치하며, 추후 관리자 API 개발 기반도 마련되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 #418의 목표와 직접 연관되어 있습니다. Term 테이블 생성, termId 적용, 데이터 마이그레이션, DTO/Service 업데이트, 테스트 픽스처 수정 등이 모두 학기 테이블화 리팩터링의 범위 내에 있으며 불필요한 추가 변경은 발견되지 않습니다.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f0caf0 and 8a4719b.

📒 Files selected for processing (2)
  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepository.java (1 hunks)
  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java (7 hunks)

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.

@Gyuhyeok99 Gyuhyeok99 marked this pull request as ready for review October 8, 2025 10:02
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (4)
src/main/java/com/example/solidconnection/term/repository/TermRepository.java (1)

1-10: 변경사항 확인 및 제안 사항

  1. 서비스 레이어 Optional 처리
     findByIsCurrentTrue().orElseThrow(...)로 CURRENT_TERM_NOT_FOUND 예외가 올바르게 발생
  2. 현재 학기 유일성 보장 검토
     • 테이블에 isCurrent=true에 대한 UNIQUE 제약조건 또는 partial index 추가
     • Term 전환 시 기존 current 값을 false로 업데이트하는 트랜잭션 로직 고려
     • Flyway 마이그레이션 스크립트에 반영
src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java (1)

53-54: 현재 학기 조회 로직의 중복을 줄이는 것을 고려해보세요.

세 개의 메서드(getApplicants, getApplicantsByUserApplications, validateSiteUserCanViewApplicants)에서 동일한 패턴으로 현재 학기를 조회하고 있습니다. 에러 처리가 일관되어 좋지만, private 헬퍼 메서드로 추출하면 코드 중복을 제거하고 유지보수성을 높일 수 있습니다.

다음과 같이 헬퍼 메서드를 추가하는 것을 고려해보세요:

+    private Term getCurrentTerm() {
+        return termRepository.findByIsCurrentTrue()
+                .orElseThrow(() -> new CustomException(CURRENT_TERM_NOT_FOUND));
+    }

그런 다음 각 메서드에서 다음과 같이 사용:

-        Term term = termRepository.findByIsCurrentTrue()
-                .orElseThrow(() -> new CustomException(CURRENT_TERM_NOT_FOUND));
+        Term term = getCurrentTerm();

Also applies to: 70-71, 140-141

src/main/java/com/example/solidconnection/term/domain/Term.java (1)

34-35: 현재 학기 유일성 검증을 고려해보세요.

isCurrent는 비즈니스 로직상 하나의 Term만 true여야 합니다. 현재는 애플리케이션 레벨에서만 관리되므로, 실수로 여러 Term이 current가 되는 것을 방지하기 어렵습니다.

다음 중 하나를 고려해보세요:

옵션 1: 데이터베이스 레벨 제약 (PostgreSQL 등)

CREATE UNIQUE INDEX uk_term_is_current ON term (is_current) WHERE is_current = true;

옵션 2: 서비스 레벨 트랜잭션 관리
현재 학기를 변경할 때 이전 current를 false로 설정하는 로직을 서비스 메서드에서 트랜잭션으로 보장:

@Transactional
public void setCurrentTerm(Long termId) {
    termRepository.findByIsCurrentTrue()
        .ifPresent(Term::setAsNotCurrent);
    Term newCurrentTerm = termRepository.findById(termId)
        .orElseThrow(...);
    newCurrentTerm.setAsCurrent();
}
src/main/java/com/example/solidconnection/mentor/repository/MentorBatchQueryRepository.java (1)

81-92: 2. getTermIdToNameMap 메서드가 일관된 패턴으로 구현되었으나, 데이터 무결성 검증을 고려해 보세요.

새로운 메서드가 기존 getMentorIdToXxxMap 패턴과 일관되게 구현되었고, 빈 리스트에 대한 early return도 적절합니다. 다만, 기존 메서드들(getMentorIdToSiteUserMap, getMentorIdToUniversityMap)은 데이터 무결성 문제 발생 시 DATA_INTEGRITY_VIOLATION 예외를 던지는 반면, 이 메서드는 존재하지 않는 termId가 있어도 조용히 해당 매핑을 누락시킵니다.

데이터 일관성 관점에서, mentor.termId에 해당하는 Term이 없다면 무결성 문제로 간주하여 예외를 던지는 것을 고려해 보세요.

다음과 같은 검증 로직 추가를 고려해 보세요:

 public Map<Long, String> getTermIdToNameMap(List<Mentor> mentors) {
     if (mentors.isEmpty()) {
         return Collections.emptyMap();
     }

     Set<Long> termIds = mentors.stream()
             .map(Mentor::getTermId)
             .collect(Collectors.toSet());

-    return termRepository.findAllById(termIds).stream()
-            .collect(Collectors.toMap(Term::getId, Term::getName));
+    List<Term> terms = termRepository.findAllById(termIds);
+    Map<Long, String> termIdToNameMap = terms.stream()
+            .collect(Collectors.toMap(Term::getId, Term::getName));
+    
+    // 데이터 무결성 검증
+    for (Long termId : termIds) {
+        if (!termIdToNameMap.containsKey(termId)) {
+            throw new CustomException(DATA_INTEGRITY_VIOLATION, 
+                "mentor.term_id에 해당하는 term이 존재하지 않음");
+        }
+    }
+    
+    return termIdToNameMap;
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 878c05c and b896dc0.

📒 Files selected for processing (49)
  • src/main/java/com/example/solidconnection/application/domain/Application.java (6 hunks)
  • src/main/java/com/example/solidconnection/application/repository/ApplicationRepository.java (1 hunks)
  • src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java (7 hunks)
  • src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java (4 hunks)
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1 hunks)
  • src/main/java/com/example/solidconnection/mentor/domain/Mentor.java (1 hunks)
  • src/main/java/com/example/solidconnection/mentor/dto/MatchedMentorResponse.java (1 hunks)
  • src/main/java/com/example/solidconnection/mentor/dto/MentorDetailResponse.java (1 hunks)
  • src/main/java/com/example/solidconnection/mentor/dto/MentorMyPageResponse.java (1 hunks)
  • src/main/java/com/example/solidconnection/mentor/dto/MentorPreviewResponse.java (1 hunks)
  • src/main/java/com/example/solidconnection/mentor/repository/MentorBatchQueryRepository.java (3 hunks)
  • src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java (3 hunks)
  • src/main/java/com/example/solidconnection/mentor/service/MentorQueryService.java (5 hunks)
  • src/main/java/com/example/solidconnection/mentor/service/MentoringQueryService.java (2 hunks)
  • src/main/java/com/example/solidconnection/term/domain/Term.java (1 hunks)
  • src/main/java/com/example/solidconnection/term/repository/TermRepository.java (1 hunks)
  • src/main/java/com/example/solidconnection/university/controller/UnivApplyInfoController.java (1 hunks)
  • src/main/java/com/example/solidconnection/university/domain/UnivApplyInfo.java (1 hunks)
  • src/main/java/com/example/solidconnection/university/dto/UnivApplyInfoDetailResponse.java (1 hunks)
  • src/main/java/com/example/solidconnection/university/dto/UnivApplyInfoPreviewResponse.java (2 hunks)
  • src/main/java/com/example/solidconnection/university/repository/UnivApplyInfoRepository.java (2 hunks)
  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepository.java (1 hunks)
  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java (5 hunks)
  • src/main/java/com/example/solidconnection/university/service/GeneralUnivApplyInfoRecommendService.java (2 hunks)
  • src/main/java/com/example/solidconnection/university/service/LikedUnivApplyInfoService.java (2 hunks)
  • src/main/java/com/example/solidconnection/university/service/UnivApplyInfoQueryService.java (3 hunks)
  • src/main/java/com/example/solidconnection/university/service/UnivApplyInfoRecommendService.java (5 hunks)
  • src/main/resources/data.sql (2 hunks)
  • src/main/resources/secret (1 hunks)
  • src/test/java/com/example/solidconnection/application/fixture/ApplicationFixture.java (2 hunks)
  • src/test/java/com/example/solidconnection/application/fixture/ApplicationFixtureBuilder.java (3 hunks)
  • src/test/java/com/example/solidconnection/application/service/ApplicationQueryServiceTest.java (22 hunks)
  • src/test/java/com/example/solidconnection/application/service/ApplicationSubmissionServiceTest.java (3 hunks)
  • src/test/java/com/example/solidconnection/mentor/fixture/MentorFixture.java (2 hunks)
  • src/test/java/com/example/solidconnection/mentor/fixture/MentorFixtureBuilder.java (3 hunks)
  • src/test/java/com/example/solidconnection/mentor/service/MentorMyPageServiceTest.java (3 hunks)
  • src/test/java/com/example/solidconnection/mentor/service/MentorQueryServiceTest.java (2 hunks)
  • src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java (4 hunks)
  • src/test/java/com/example/solidconnection/term/fixture/TermFixture.java (1 hunks)
  • src/test/java/com/example/solidconnection/term/fixture/TermFixtureBuilder.java (1 hunks)
  • src/test/java/com/example/solidconnection/term/repository/TermRepositoryForTest.java (1 hunks)
  • src/test/java/com/example/solidconnection/university/fixture/UnivApplyInfoFixture.java (1 hunks)
  • src/test/java/com/example/solidconnection/university/fixture/UnivApplyInfoFixtureBuilder.java (2 hunks)
  • src/test/java/com/example/solidconnection/university/repository/LikedUnivApplyInfoRepositoryTest.java (4 hunks)
  • src/test/java/com/example/solidconnection/university/service/GeneralUnivApplyInfoRecommendServiceTest.java (3 hunks)
  • src/test/java/com/example/solidconnection/university/service/LikedUnivApplyInfoServiceTest.java (2 hunks)
  • src/test/java/com/example/solidconnection/university/service/UnivApplyInfoQueryServiceTest.java (8 hunks)
  • src/test/java/com/example/solidconnection/university/service/UnivApplyInfoRecommendServiceTest.java (8 hunks)
  • src/test/resources/application.yml (0 hunks)
💤 Files with no reviewable changes (1)
  • src/test/resources/application.yml
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-26T05:07:54.817Z
Learnt from: nayonsoso
PR: solid-connection/solid-connect-server#486
File: src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java:0-0
Timestamp: 2025-08-26T05:07:54.817Z
Learning: 대학지원정보 텍스트 검색에서 region과 country 간의 INNER JOIN은 데이터 무결성을 위한 의도된 설계이다. country.region_code가 region 테이블에 존재하지 않는 경우는 잘못된 데이터 상태로 간주하여 검색 결과에서 제외한다.

Applied to files:

  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java
📚 Learning: 2025-08-26T05:00:56.556Z
Learnt from: nayonsoso
PR: solid-connection/solid-connect-server#486
File: src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java:166-178
Timestamp: 2025-08-26T05:00:56.556Z
Learning: UnivApplyInfo의 텍스트 검색 기능에서는 University.koreanName이 아닌 UnivApplyInfo.koreanName을 검색 대상으로 사용하는 것이 의도된 설계이다.

Applied to files:

  • src/test/java/com/example/solidconnection/university/service/UnivApplyInfoQueryServiceTest.java
🧬 Code graph analysis (4)
src/test/java/com/example/solidconnection/term/fixture/TermFixtureBuilder.java (1)
src/test/java/com/example/solidconnection/term/fixture/TermFixture.java (1)
  • TestComponent (7-26)
src/test/java/com/example/solidconnection/term/fixture/TermFixture.java (1)
src/test/java/com/example/solidconnection/term/fixture/TermFixtureBuilder.java (1)
  • TestComponent (8-35)
src/test/java/com/example/solidconnection/university/repository/LikedUnivApplyInfoRepositoryTest.java (1)
src/test/java/com/example/solidconnection/university/service/UnivApplyInfoQueryServiceTest.java (4)
  • Nested (58-99)
  • Nested (101-163)
  • Nested (165-291)
  • Nested (185-244)
src/test/java/com/example/solidconnection/university/service/UnivApplyInfoQueryServiceTest.java (1)
src/test/java/com/example/solidconnection/university/repository/LikedUnivApplyInfoRepositoryTest.java (1)
  • Nested (43-93)
🔇 Additional comments (41)
src/main/resources/secret (1)

1-1: 서브모듈 커밋 업데이트 확인 완료!
새 SHA 반영 확인했습니다. 추가적인 조치 없이도 머지해도 괜찮아 보여요.

src/test/java/com/example/solidconnection/university/repository/LikedUnivApplyInfoRepositoryTest.java (2)

8-41: LGTM! 학기 컨텍스트 설정이 올바르게 적용되었습니다.

변경사항:

  1. TermFixture를 주입받아 테스트 시작 시 현재 학기를 초기화
  2. 모든 테스트가 동일한 학기 컨텍스트를 공유하도록 설정
  3. @beforeeach에서 "2025-2" 학기를 현재 학기로 설정

이 패턴은 다른 테스트 파일들과 일관성이 있어 좋습니다.


50-83: LGTM! 픽스처 호출이 termId 기반으로 올바르게 업데이트되었습니다.

모든 UnivApplyInfoFixture 호출에서 term.getId()를 전달하여 학기 ID 기반 데이터 흐름과 일치하도록 수정되었습니다.

src/main/java/com/example/solidconnection/university/dto/UnivApplyInfoPreviewResponse.java (1)

19-28: LGTM! DTO 팩토리 메서드가 termName을 외부에서 받도록 올바르게 수정되었습니다.

변경사항:

  1. 메서드 시그니처에 String termName 파라미터 추가
  2. univApplyInfo.getTerm() 대신 전달받은 termName 사용
  3. 관심사 분리 - 학기 이름 조회 책임을 서비스 레이어로 이동

이 패턴은 UnivApplyInfo 엔티티가 더 이상 term 문자열을 직접 보유하지 않는 새로운 구조와 일치합니다.

src/test/java/com/example/solidconnection/mentor/service/MentorQueryServiceTest.java (1)

21-63: LGTM! 테스트 설정이 다른 테스트 파일들과 일관된 패턴을 따릅니다.

변경사항:

  1. TermFixture 의존성 주입
  2. setUp 메서드에서 "2025-2" 학기를 현재 학기로 초기화
  3. 모든 멘토 테스트가 동일한 학기 컨텍스트에서 실행

코드베이스 전반에 걸쳐 일관된 학기 설정 패턴이 적용되어 좋습니다.

src/test/java/com/example/solidconnection/mentor/service/MentorMyPageServiceTest.java (1)

22-66: LGTM! 멘토 마이페이지 테스트에 학기 컨텍스트가 올바르게 추가되었습니다.

변경사항은 다른 테스트 파일들과 동일한 패턴을 따르며, setUp 메서드에서 TermFixture를 통해 현재 학기를 초기화합니다.

src/main/java/com/example/solidconnection/university/controller/UnivApplyInfoController.java (2)

102-102: 동일한 패턴이 텍스트 검색 메서드에도 일관되게 적용되었습니다.

searchUnivApplyInfoByText 메서드에서도 term 파라미터가 제거되어 일관성이 유지됩니다.


94-94: LGTM! 컨트롤러 책임이 서비스 레이어로 깔끔히 이동되었습니다.

변경사항:
1. @value로 주입받던 term 필드 제거
2. 서비스 메서드 호출 시 term 파라미터 제거
3. UnivApplyInfoQueryService 내부에서 TermRepository.findByIsCurrentTrue()로 현재 학기 조회 로직 캡슐화

searchUnivApplyInfoByFilter와 searchUnivApplyInfoByText 메서드에서 TermRepository.findByIsCurrentTrue()를 통해 현재 학기를 정상적으로 조회하고 있습니다.

src/test/java/com/example/solidconnection/university/service/GeneralUnivApplyInfoRecommendServiceTest.java (2)

8-46: LGTM! 추천 서비스 테스트가 termId 기반으로 올바르게 마이그레이션되었습니다.

변경사항:

  1. TermFixture를 통한 현재 학기 초기화
  2. 모든 UnivApplyInfoFixture 호출에 term.getId() 전달
  3. 10개의 대학 지원 정보 픽스처가 모두 동일한 학기 ID를 사용하도록 설정

setUp 메서드가 테스트에 필요한 모든 데이터를 일관된 학기 컨텍스트 내에서 준비합니다.


58-59: LGTM! 검증 로직이 새로운 필드명과 일치하도록 업데이트되었습니다.

extracting("term") 대신 extracting("termId")를 사용하고, term.getId()와 비교하도록 수정되어 엔티티 구조 변경사항을 정확히 반영합니다.

src/main/java/com/example/solidconnection/university/domain/UnivApplyInfo.java (1)

35-36: UnivApplyInfo termId 할당 검증 및 FK 제약조건 추적 요청

  1. termId 할당 검증
    • main 코드에서 UnivApplyInfo 인스턴스 생성(생성자/세터/DTO 매핑 등) 모든 경로에서 termId가 명시적으로 설정되는지 rg -n 'new UnivApplyInfo\(', rg -n 'setTermId' 등으로 확인해 주세요.
  2. FK 제약조건 추적
    • PR 목표에 언급된 term_id FK 제약 추가가 별도 이슈/PR로 관리되고 있는지 확인 후 관련 링크 또는 이슈 번호를 남겨 주세요.
src/test/java/com/example/solidconnection/mentor/fixture/MentorFixtureBuilder.java (1)

20-20: 깔끔한 타입 변경이에요!

테스트 픽스처 빌더가 term (String)에서 termId (long)로 전환되었어요. 변경 사항:

  1. 필드 타입 변경: private String termprivate long termId
  2. 빌더 메서드 시그니처 변경: term(String)termId(long)
  3. Mentor 생성 시 termId 전달

일관되고 올바르게 리팩터링되었습니다.

Also applies to: 56-59, 70-70

src/test/java/com/example/solidconnection/term/repository/TermRepositoryForTest.java (1)

1-10: 테스트용 리포지토리 추가가 적절해요!

TermRepositoryForTest는 테스트 컨텍스트에서 학기 이름으로 Term 엔티티를 조회할 수 있게 해줍니다.

변경 내용:

  1. JpaRepository<Term, Long> 확장
  2. findByName(String name) 쿼리 메서드 선언
  3. 테스트 픽스처가 학기 이름으로 Term을 찾을 수 있도록 지원

테스트와 프로덕션 리포지토리를 명확하게 분리한 좋은 설계입니다.

src/main/java/com/example/solidconnection/mentor/dto/MentorMyPageResponse.java (1)

22-22: DTO 팩토리 메서드 시그니처 변경이 올바릅니다!

MentorMyPageResponse.of() 메서드가 termName 파라미터를 추가로 받도록 변경되었어요.

변경 사항:

  1. 메서드 시그니처: of(Mentor, SiteUser, University)of(Mentor, SiteUser, University, String termName)
  2. 응답 생성 시 termName 파라미터 사용

이제 서비스 레이어에서 termId로부터 termName을 조회하여 전달해야 합니다. 다른 DTO들과 일관된 패턴이에요.

Also applies to: 29-29

src/test/java/com/example/solidconnection/university/service/LikedUnivApplyInfoServiceTest.java (1)

13-14: 테스트 컨텍스트에 학기 설정 추가가 적절해요!

테스트에 Term 컨텍스트가 추가되어 termId 기반 픽스처를 사용할 수 있게 되었습니다.

변경 내용:

  1. TermFixture 의존성 주입 추가 (lines 44-45)
  2. Term 필드 추가 (line 48)
  3. setUp()에서 현재 학기 초기화: termFixture.현재_학기("2025-2") (line 53)
  4. 모든 UnivApplyInfo 픽스처 호출에 term.getId() 전달 (lines 55, 61-62)

테스트 데이터가 일관되게 termId 컨텍스트를 사용하도록 잘 구성되었습니다.

Also applies to: 44-48, 53-53, 55-55, 61-62

src/test/java/com/example/solidconnection/siteuser/service/MyPageServiceTest.java (1)

40-41: 마이페이지 테스트도 학기 컨텍스트로 일관되게 업데이트되었어요!

MyPageServiceTest가 Term 기반 테스트 데이터를 사용하도록 변경되었습니다.

주요 변경 사항:

  1. TermFixture 의존성 주입 추가 (lines 94-95)
  2. Term 필드 추가 (line 104)
  3. setUp()에서 현재 학기 초기화 (line 109)
  4. 멘토 및 좋아요 관련 픽스처에 term.getId() 전달 (lines 141, 163-165)

다른 서비스 테스트들과 동일한 패턴을 따르고 있어 일관성이 좋습니다.

Also applies to: 94-95, 104-104, 109-109, 141-141, 163-165

src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java (1)

23-24: 학기 조회 로직이 잘 구현되었어요!

ApplicationSubmissionService가 현재 학기를 TermRepository에서 조회하여 사용하도록 변경되었습니다.

올바른 변경 사항:

  1. TermRepository 의존성 추가 (lines 23-24, 40)
  2. 현재 학기 조회: termRepository.findByIsCurrentTrue() (line 51)
  3. 기존 지원서 조회 시 termId 사용 (line 58)
  4. 새 지원서 생성 시 term.getId() 전달 (line 71)

String 기반에서 Long 기반 termId로의 전환이 잘 이루어졌습니다.

Also applies to: 40-40, 58-58, 71-71

src/main/resources/data.sql (2)

44-44: 학기 테이블 초기 데이터 추가가 적절해요!

term 테이블에 첫 학기 데이터가 삽입되었습니다.

INSERT INTO term (name, is_current) VALUES ('2024-1', true);

'2024-1' 학기가 현재 학기(is_current = true)로 설정되었어요.

확인 필요:

  • 현재 하나의 학기만 삽입되어 있는데, 이전 학기나 향후 학기 데이터도 필요한지 검토해주세요
  • 프로덕션 환경에서는 여러 학기 데이터가 필요할 수 있어요

149-224: 대학 지원 정보의 학기 참조가 올바르게 변경되었어요!

university_info_for_apply 테이블의 학기 참조가 String에서 FK로 전환되었습니다.

변경 내용:

  1. 컬럼명 변경: termterm_id (line 149)
  2. 모든 INSERT 문에서 학기 값 변경:
    • 이전: ('2024-1', 1, ...) (String)
    • 현재: (1, 1, ...) (FK to term table)
  3. 총 24개의 대학 지원 정보 레코드가 모두 term_id = 1 참조

데이터 마이그레이션이 일관되게 적용되었습니다.

참고:

  • PR 목표에서 언급했듯이 Flyway 스크립트는 아직 작성되지 않았어요
  • 프로덕션 마이그레이션 시 기존 데이터의 term String 값을 term_id로 매핑하는 스크립트가 필요합니다
  • FK 제약조건 추가도 Flyway 스크립트에 포함되어야 해요
src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java (2)

4-4: 새로운 에러 코드 추가가 적절합니다.

현재 학기를 찾지 못했을 때 명확한 에러를 던지도록 CURRENT_TERM_NOT_FOUND를 추가한 점이 좋습니다.


60-60: term.getId() 사용이 올바릅니다.

String term에서 Long termId로의 전환이 일관되게 적용되었습니다. 리포지토리 호출 시 term.getId()를 정확하게 전달하고 있습니다.

Also applies to: 73-73, 87-87, 143-143

src/main/java/com/example/solidconnection/university/dto/UnivApplyInfoDetailResponse.java (1)

35-42: 팩토리 메서드 시그니처 변경이 적절합니다.

termName 매개변수를 추가하여 호출자가 학기 이름을 명시적으로 제공하도록 변경한 것이 좋습니다. 이는 UnivApplyInfo에서 term 필드를 제거하고 termId로 관리하는 리팩터링과 일관됩니다.

src/main/java/com/example/solidconnection/mentor/dto/MentorPreviewResponse.java (1)

22-32: 학기 이름 매개변수 추가가 일관성 있게 적용되었습니다.

MentorPreviewResponse.of 메서드에 termName 매개변수를 추가한 변경이 다른 DTO 응답 클래스들과 일관성을 유지합니다. mentor.getTerm()에서 외부 제공 termName으로 전환이 올바르게 구현되었습니다.

src/test/java/com/example/solidconnection/application/fixture/ApplicationFixture.java (1)

16-35: 테스트 픽스처가 올바르게 업데이트되었습니다.

지원서 메서드의 매개변수를 String term에서 long termId로 변경하고, 빌더 호출도 .termId(termId)로 업데이트한 것이 도메인 엔티티 변경사항과 일치합니다.

src/main/java/com/example/solidconnection/term/domain/Term.java (2)

1-49: Term 엔티티 구조가 적절합니다.

  1. id (자동 생성), name (유니크 제약), isCurrent (기본값 false) 필드 구성이 합리적입니다.
  2. setAsCurrent()setAsNotCurrent() 메서드로 현재 학기 상태를 관리하는 방식이 명확합니다.

31-32: 학기 이름 길이 제한이 충분한지 확인하세요.

name 필드의 최대 길이가 20자입니다. 현재 "2025-1" 형식을 사용한다면 충분하지만, 향후 더 긴 형식(예: "2025-1학기", "2025년 1학기")을 사용할 가능성이 있다면 길이를 늘리는 것을 고려해보세요.

프로젝트의 학기 명명 규칙을 확인하고 20자가 충분한지 검토해주세요.

src/test/java/com/example/solidconnection/mentor/fixture/MentorFixture.java (1)

15-23: TermFixture 통합이 올바르게 구현되었습니다.

termFixture.현재_학기("2025-1").getId()를 사용하여 현재 학기 ID를 가져오는 방식이 적절합니다. 테스트에서 학기 컨텍스트를 명확하게 제공합니다.

참고: "2025-1"이라는 하드코딩된 값이 테스트 전반에 걸쳐 일관되게 사용되는지 확인하세요.

src/test/java/com/example/solidconnection/application/service/ApplicationQueryServiceTest.java (3)

59-60: 테스트 설정이 명확하게 개선되었습니다.

  1. TermFixture를 주입하여 학기 컨텍스트를 명시적으로 관리
  2. @BeforeEach에서 현재 학기를 초기화하여 모든 테스트에서 일관된 학기 사용

테스트의 가독성과 유지보수성이 향상되었습니다.

Also applies to: 78-82


257-263: 이전 학기 테스트 케이스가 훌륭합니다.

이전_학기_지원자는_조회되지_않는다 테스트에서 termFixture.이전_학기("2024-2")를 사용하여 현재 학기와 이전 학기를 구분하는 시나리오를 명확하게 검증합니다. 이는 학기 필터링 로직의 정확성을 보장합니다.


96-98: term.getId() 사용이 일관되게 적용되었습니다.

모든 테스트 케이스에서 ApplicationFixture.지원서 호출 시 term.getId()를 전달하도록 업데이트되어 String term에서 Long termId로의 전환이 완전히 반영되었습니다.

Also applies to: 110-110, 120-120, 130-130, 162-162, 172-172, 182-182, 212-212, 222-222, 232-232, 263-263, 291-291, 303-303, 335-335, 345-345, 355-355, 378-378, 388-388, 398-398

src/main/java/com/example/solidconnection/mentor/dto/MentorDetailResponse.java (1)

23-33: MentorDetailResponse 팩토리 메서드 변경이 일관성 있습니다.

termName 매개변수를 추가하고 mentor.getTerm() 대신 이를 사용하도록 변경한 것이 다른 멘토/대학 관련 DTO들과 동일한 패턴을 따릅니다. 학기 정보를 외부에서 제공받도록 하는 리팩터링 목표와 부합합니다.

src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java (3)

77-79: 1. 메서드 시그니처와 내부 조건절이 termId 기반으로 깔끔하게 변경되었습니다.

findAllByFilter 메서드가 String term 대신 Long termId를 받도록 업데이트되었고, 내부 where 절에서도 termIdEq(univApplyInfo, termId)로 일관되게 전환되었습니다. 타입 변경으로 인해 null/blank 체크가 단순 null 체크로 간소화된 점도 좋습니다.

Also applies to: 92-92


121-126: 2. termIdEq 헬퍼 메서드의 타입 변경이 적절합니다.

기존 termEq(String) 메서드가 termIdEq(Long)으로 이름과 타입이 변경되었고, null 체크만 수행하도록 단순화되었습니다. Long 타입 비교로 인한 성능상 이점도 기대할 수 있습니다.


146-146: 3. 텍스트 검색 메서드도 동일하게 termId 기반으로 전환되었습니다.

findAllByText 메서드가 String term 대신 Long termId를 받도록 변경되었고, where 절에서 termIdEq를 호출합니다. 검색 로직의 일관성이 유지되었습니다.

Also applies to: 158-158

src/main/java/com/example/solidconnection/mentor/repository/MentorBatchQueryRepository.java (1)

10-11: 1. Term 관련 의존성이 추가되어 termId → termName 매핑을 지원합니다.

TermRepositoryTerm 엔터티가 새로 추가되어, 멘토 정보를 조회할 때 학기 이름을 함께 조회할 수 있게 되었습니다.

Also applies to: 30-30

src/main/java/com/example/solidconnection/application/repository/ApplicationRepository.java (2)

18-28: 1. findAllByUnivApplyInfoIds 쿼리 메서드가 termId 기반으로 변경되었습니다.

JPQL 쿼리의 조건절이 a.term = :term에서 a.termId = :termId로 변경되었고, 메서드 파라미터도 String term에서 long termId로 변경되어 일관성이 유지되었습니다.


30-42: 2. findBySiteUserIdAndTermId 메서드와 디폴트 헬퍼 메서드가 일관되게 업데이트되었습니다.

  • 쿼리 메서드가 findBySiteUserIdAndTermId로 이름이 변경되고 long termId 파라미터를 받습니다.
  • 디폴트 메서드 getApplicationBySiteUserIdAndTermId도 동일하게 업데이트되어 네이밍 일관성이 유지됩니다.
src/test/java/com/example/solidconnection/university/service/UnivApplyInfoQueryServiceTest.java (3)

14-15: 1. TermFixture를 활용한 테스트 초기화가 명확하게 추가되었습니다.

TermFixture가 의존성으로 추가되었고, @BeforeEach에서 현재 학기를 "2025-2"로 설정합니다. 이를 통해 모든 테스트가 일관된 학기 컨텍스트에서 실행됩니다.

Also applies to: 48-56


64-65: 2. 픽스처 호출과 예상 응답 생성이 termIdtermName 기반으로 일관되게 업데이트되었습니다.

  • 모든 픽스처 메서드 호출 시 term.getId()를 전달하여 termId 기반 엔터티를 생성합니다.
  • 예상 응답 생성 시 UnivApplyInfoPreviewResponse.from(univApplyInfo, term.getName())로 termName을 함께 전달합니다.
  • 리포지토리 메서드 검증 시에도 term.getId()를 사용하여 일관성이 유지됩니다.

Also applies to: 76-77, 108-119


267-283: 3. 캐싱 테스트도 termId 기반으로 적절히 업데이트되었습니다.

캐싱 검증 테스트에서 리포지토리 메서드 호출 확인 시 findAllByText(text, term.getId())를 사용하여, termId 기반 쿼리가 캐싱되는지 확인합니다.

src/test/java/com/example/solidconnection/university/fixture/UnivApplyInfoFixture.java (1)

14-20: 모든 픽스처 메서드가 termId 파라미터 기반으로 일관되게 리팩터링되었습니다.

  • 기존에 주입된 term 필드를 사용하던 방식에서, 각 메서드가 long termId 파라미터를 받아 명시적으로 설정하는 방식으로 변경되었습니다.
  • 모든 메서드에서 .termId(termId)를 호출하여 UnivApplyInfo 엔터티를 생성합니다.
  • 테스트에서 학기를 명시적으로 전달하도록 강제하여, 각 테스트의 학기 컨텍스트가 더 명확해졌습니다.

Also applies to: 22-28, 30-36, 38-44, 46-52, 54-60, 62-68, 70-76, 78-84, 86-92, 94-100

Comment on lines +35 to +38
termRepository.findByIsCurrentTrue().ifPresent(term -> {
Pageable page = PageRequest.of(0, RECOMMEND_UNIV_APPLY_INFO_NUM);
generalRecommends = univApplyInfoRepository.findRandomByTermId(term.getId(), page);
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

현재 학기 미설정 시 추천 목록이 null로 남는 문제

현재 학기가 설정되어 있지 않으면 generalRecommends가 그대로 null로 남아 이후 접근 지점에서 NPE가 발생해요. 의도한 CURRENT_TERM_NOT_FOUND 예외도 던지지 못하고 있어서 디버깅이 더 어렵습니다. Kindly 하지만 확실하게, 아래처럼 즉시 예외를 던지면서 리스트를 채우도록 바꿔주세요.

-        termRepository.findByIsCurrentTrue().ifPresent(term -> {
-            Pageable page = PageRequest.of(0, RECOMMEND_UNIV_APPLY_INFO_NUM);
-            generalRecommends = univApplyInfoRepository.findRandomByTermId(term.getId(), page);
-        });
+        Term term = termRepository.findByIsCurrentTrue()
+                .orElseThrow(() -> new CustomException(CURRENT_TERM_NOT_FOUND));
+        Pageable page = PageRequest.of(0, RECOMMEND_UNIV_APPLY_INFO_NUM);
+        generalRecommends = univApplyInfoRepository.findRandomByTermId(term.getId(), page);
📝 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
termRepository.findByIsCurrentTrue().ifPresent(term -> {
Pageable page = PageRequest.of(0, RECOMMEND_UNIV_APPLY_INFO_NUM);
generalRecommends = univApplyInfoRepository.findRandomByTermId(term.getId(), page);
});
Term term = termRepository.findByIsCurrentTrue()
.orElseThrow(() -> new CustomException(CURRENT_TERM_NOT_FOUND));
Pageable page = PageRequest.of(0, RECOMMEND_UNIV_APPLY_INFO_NUM);
generalRecommends = univApplyInfoRepository.findRandomByTermId(term.getId(), page);
🤖 Prompt for AI Agents
In
src/main/java/com/example/solidconnection/university/service/GeneralUnivApplyInfoRecommendService.java
around lines 35-38, replace the current optional-handling so that if no current
term is found you immediately throw the CURRENT_TERM_NOT_FOUND exception, and
ensure generalRecommends is always non-null by initializing it before use;
specifically, call termRepository.findByIsCurrentTrue().orElseThrow(() -> new
CustomException(ErrorCode.CURRENT_TERM_NOT_FOUND)) to get the term, then
populate generalRecommends =
univApplyInfoRepository.findRandomByTermId(term.getId(), PageRequest.of(0,
RECOMMEND_UNIV_APPLY_INFO_NUM)), and initialize generalRecommends to an empty
list at declaration (or set to Collections.emptyList()) to avoid NPEs if
something else goes wrong.

Comment on lines +39 to +50
Set<Long> termIds = univApplyInfos.stream()
.map(UnivApplyInfo::getTermId)
.collect(Collectors.toSet());

Map<Long, String> termMap = termRepository.findAllById(termIds).stream()
.collect(Collectors.toMap(Term::getId, Term::getName));

return univApplyInfos.stream()
.map(UnivApplyInfoPreviewResponse::from)
.map(univApplyInfo -> {
String termName = termMap.getOrDefault(univApplyInfo.getTermId(), "Unknown");
return UnivApplyInfoPreviewResponse.from(univApplyInfo, termName);
})
Copy link

@coderabbitai coderabbitai bot Oct 8, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

효율적인 배치 조회 패턴이지만 폴백 전략을 검토해주세요.

좋아요한 대학 정보 목록 조회 시 termId를 기반으로 학기 이름을 효율적으로 조회하도록 변경되었어요.

변경 내용:

  1. 모든 UnivApplyInfotermId를 Set으로 수집 (lines 39-41)
  2. TermRepository로 해당 Term들을 일괄 조회하여 Map 생성 (lines 43-44)
  3. 각 응답 생성 시 Map에서 termName 조회 (lines 47-50)

검토 필요 사항:

  • Line 48에서 getOrDefault(termId, "Unknown") 사용
  • Term이 존재하지 않는 경우 "Unknown"을 반환하는데, 이는 데이터 무결성 문제를 숨길 수 있어요
  • FK 제약조건이 있다면 이런 경우가 발생하지 않아야 하지만, 마이그레이션 중이거나 데이터 불일치 시 조용히 실패합니다

제안:

  • 프로덕션 환경에서 데이터 무결성이 보장된다면 "Unknown" 대신 예외를 던지는 것을 고려해보세요
  • 또는 로깅을 추가하여 이런 케이스를 추적하는 것도 좋아요

다음 스크립트로 UnivApplyInfo의 termId가 모두 유효한 Term을 참조하는지 확인할 수 있어요:


🏁 Script executed:

#!/bin/bash
# UnivApplyInfo의 termId가 존재하지 않는 Term을 참조하는지 확인

# UnivApplyInfo 엔티티 파일에서 termId 필드 확인
echo "=== UnivApplyInfo 엔티티의 termId 필드 확인 ==="
rg -A 5 "class UnivApplyInfo" --type java -g "**/UnivApplyInfo.java" | rg -A 3 "termId"

# FK 제약조건이 설정되어 있는지 확인
echo -e "\n=== FK 제약조건 확인 (Flyway 스크립트) ==="
fd -e sql . | xargs rg "FOREIGN KEY.*term_id|termId.*REFERENCES.*term" 2>/dev/null || echo "FK 제약조건을 찾을 수 없습니다 (Flyway 스크립트 미작성으로 예상됨)"

# 관련 에러 코드 확인
echo -e "\n=== Term 관련 에러 코드 확인 ==="
rg "TERM_NOT_FOUND|CURRENT_TERM_NOT_FOUND" --type java

Length of output: 3391


불완전한 termId 참조 시 폴백 전략 검토 필요

  1. 모든 UnivApplyInfotermId를 Set으로 수집했습니다 (lines 39-41).
  2. TermRepository로 일괄 조회 후 termMap을 생성했습니다 (lines 43-44).
  3. termMap.getOrDefault(..., "Unknown")으로 존재하지 않는 term을 "Unknown"으로 처리합니다 (lines 47-50).

다음 사항을 검토해주세요.

  1. "Unknown" 폴백은 누락된 term을 조용히 숨길 수 있습니다.
  2. Flyway 마이그레이션 스크립트에 FK 제약이 없어 실제 누락된 termId가 발생할 수 있습니다.
  3. 다른 서비스에서는 누락 시 CustomException(TERM_NOT_FOUND/CURRENT_TERM_NOT_FOUND)을 던지는 패턴을 사용하고 있습니다.

다음 제안을 고려하세요.

  1. 누락 시 예외를 던져 데이터 무결성을 보장하세요.
  2. 또는 로깅을 추가해 누락 케이스를 추적하세요.
  3. 필요 시 DB에 FK 제약을 추가하는 방안을 검토하세요.
🤖 Prompt for AI Agents
In
src/main/java/com/example/solidconnection/university/service/LikedUnivApplyInfoService.java
around lines 39-50, replace the silent "Unknown" fallback with explicit
handling: after building termMap, compute missingTermIds = termIds -
termMap.keySet(); if missingTermIds is not empty, either throw the existing
CustomException (e.g., TERM_NOT_FOUND or a new specific exception containing the
missing ids) to enforce data integrity, or at minimum log an error with the
missingTermIds and fail fast; then map univApplyInfos to responses using the
confirmed termMap (no "Unknown" fallback).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai 어떻게 예외 던질까? 보여줘봐

Copy link

Choose a reason for hiding this comment

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

@Gyuhyeok99, 누락된 term ID를 감지하고 예외를 던지는 방법을 보여드릴게요:

@Transactional(readOnly = true)
public List<UnivApplyInfoPreviewResponse> getLikedUnivApplyInfos(long siteUserId) {
    List<UnivApplyInfo> univApplyInfos = likedUnivApplyInfoRepository.findUnivApplyInfosBySiteUserId(siteUserId);
    Set<Long> termIds = univApplyInfos.stream()
            .map(UnivApplyInfo::getTermId)
            .collect(Collectors.toSet());

    Map<Long, String> termMap = termRepository.findAllById(termIds).stream()
            .collect(Collectors.toMap(Term::getId, Term::getName));

    // 누락된 term ID가 있는지 확인
    Set<Long> missingTermIds = termIds.stream()
            .filter(termId -> !termMap.containsKey(termId))
            .collect(Collectors.toSet());
    
    if (!missingTermIds.isEmpty()) {
        throw new CustomException(TERM_NOT_FOUND);
    }

    return univApplyInfos.stream()
            .map(univApplyInfo -> {
                String termName = termMap.get(univApplyInfo.getTermId()); // getOrDefault 대신 get 사용
                return UnivApplyInfoPreviewResponse.from(univApplyInfo, termName);
            })
            .toList();
}

주요 변경 사항:

  1. 누락 감지 (lines 11-14): termIds에는 있지만 termMap에는 없는 ID들을 찾습니다
  2. 예외 던지기 (lines 16-18): 누락된 ID가 있으면 즉시 CustomException(TERM_NOT_FOUND) 예외를 던집니다
  3. 안전한 접근 (line 22): getOrDefault 대신 get()을 사용해도 안전합니다 (이미 모든 term이 존재함을 확인했으므로)

추가 옵션 - 로깅과 함께 사용:

더 자세한 디버깅을 원하시면 로깅을 추가할 수도 있어요:

if (!missingTermIds.isEmpty()) {
    log.error("존재하지 않는 term ID가 감지되었습니다. missing IDs: {}", missingTermIds);
    throw new CustomException(TERM_NOT_FOUND);
}

이렇게 하면 데이터 무결성 문제를 조기에 발견하고 추적할 수 있어요!

Comment on lines +13 to +25
public Term 현재_학기(String name) {
return termFixtureBuilder.term()
.name(name)
.isCurrent(true)
.findOrCreate();
}

public Term 이전_학기(String name) {
return termFixtureBuilder.term()
.name(name)
.isCurrent(false)
.findOrCreate();
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

기존 Term가 이미 존재할 때 isCurrent가 갱신되지 않습니다.
같은 이름으로 저장된 Term가 isCurrent=false 상태라면 현재_학기(...)를 호출해도 그대로 false가 유지되어, 테스트 전제(현재 학기)와 실제 데이터가 어긋납니다. 하나의 테스트가 Term를 만들고 다른 테스트가 재사용하는 흐름에서 특히 문제가 됩니다. TermFixtureBuilder.findOrCreate() 단계에서 기존 Term의 isCurrent 값을 요청한 상태로 맞춰 주도록 수정해 주세요.

다음과 같이 기존 엔터티의 상태를 동기화하는 편이 안전합니다(실제 Term 엔터티의 갱신 메서드 이름에 맞게 조정해 주세요):

-    public Term findOrCreate() {
-        return termRepositoryForTest.findByName(name)
-                .orElseGet(() -> termRepositoryForTest.save(new Term(name, isCurrent)));
-    }
+    public Term findOrCreate() {
+        return termRepositoryForTest.findByName(name)
+                .map(existing -> {
+                    if (existing.isCurrent() != isCurrent) {
+                        existing.updateIsCurrent(isCurrent);
+                    }
+                    return existing;
+                })
+                .orElseGet(() -> termRepositoryForTest.save(new Term(name, isCurrent)));
+    }
📝 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
public Term 현재_학기(String name) {
return termFixtureBuilder.term()
.name(name)
.isCurrent(true)
.findOrCreate();
}
public Term 이전_학기(String name) {
return termFixtureBuilder.term()
.name(name)
.isCurrent(false)
.findOrCreate();
}
public Term findOrCreate() {
return termRepositoryForTest.findByName(name)
.map(existing -> {
if (existing.isCurrent() != isCurrent) {
existing.updateIsCurrent(isCurrent);
}
return existing;
})
.orElseGet(() -> termRepositoryForTest.save(new Term(name, isCurrent)));
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java (1)

52-53: 선택적 개선: 현재 학기 조회 성능 최적화 고려

현재 apply 메서드가 호출될 때마다 현재 학기를 DB에서 조회하고 있어요. 학기가 자주 변경되지 않는다면, 캐싱을 통해 성능을 개선할 수 있습니다.

개선 방안:

  • Spring Cache를 활용하여 현재 학기 정보를 캐싱
  • 관리자 API로 학기 변경 시 캐시 무효화

다만 이는 현재 PR의 범위를 벗어나는 최적화이므로, 향후 성능 이슈가 발생할 경우 고려하시면 좋을 것 같아요.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b896dc0 and b5455e9.

📒 Files selected for processing (2)
  • src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java (5 hunks)
  • src/main/java/com/example/solidconnection/university/service/UnivApplyInfoQueryService.java (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (4)
src/main/java/com/example/solidconnection/application/service/ApplicationSubmissionService.java (2)

52-53: ✅ 이전 리뷰 이슈가 올바르게 수정되었습니다!

과거 리뷰에서 지적되었던 잘못된 예외 코드(USER_NOT_FOUND) 사용 문제가 올바르게 수정되었어요:

수정된 내용:

  1. Line 4: CURRENT_TERM_NOT_FOUND import 추가 ✓
  2. Line 53: USER_NOT_FOUNDCURRENT_TERM_NOT_FOUND로 변경 ✓

현재 학기를 찾지 못했을 때 적절한 예외가 발생하도록 수정되었습니다.


24-25: LGTM! Term 테이블 기반 리팩터링이 깔끔하게 적용되었습니다.

전체적인 변경 사항이 일관되고 올바르게 구현되었어요:

주요 변경 사항:

  1. 의존성 추가 (Lines 24-25, 41)

    • Term 엔티티와 TermRepository import 및 필드 추가
  2. 현재 학기 조회 로직 (Lines 52-53)

    • termRepository.findByIsCurrentTrue()로 현재 학기 조회
    • 학기가 없을 경우 적절한 예외(CURRENT_TERM_NOT_FOUND) 발생
  3. termId 기반 조회 (Line 59)

    • 기존 지원서 조회 시 term.getId()를 사용하여 termId 기반으로 변경
  4. Application 생성 (Line 72)

    • 새 지원서 생성 시 term.getId()를 전달하여 termId 저장

모든 변경 사항이 PR 목표와 일치하며, 코드의 일관성이 유지되고 있습니다.

Also applies to: 41-41, 52-53, 59-59, 72-72

src/main/java/com/example/solidconnection/university/service/UnivApplyInfoQueryService.java (2)

40-42: Term 조회 및 예외 처리가 적절합니다

변경사항 요약:

  1. termId를 통해 Term 엔티티 조회
  2. 존재하지 않을 경우 TERM_NOT_FOUND 예외 발생
  3. 응답에 term.getName() 포함

로직이 명확하고 에러 핸들링도 적절합니다.


46-59: 변경 영향 및 확인 완료

  1. 메서드가 항상 현재 학기를 조회하도록 변경됨.
  2. 모든 호출부는 이미 searchUnivApplyInfoByFilter(request) 형태로 일관되어 있어 영향이 없음.
  3. 과거 학기 조회 기능이 제거되어 필요한 경우 별도 구현이 필요함.

Comment on lines +63 to 75
public UnivApplyInfoPreviewResponses searchUnivApplyInfoByText(String text) {
Term term = termRepository.findByIsCurrentTrue()
.orElseThrow(() -> new CustomException(CURRENT_TERM_NOT_FOUND));

List<UnivApplyInfoPreviewResponse> responses = univApplyInfoRepository.findAllByText(text, term.getId())
.stream()
.map(UnivApplyInfoPreviewResponse::from)
.map(univApplyInfo -> UnivApplyInfoPreviewResponse.from(
univApplyInfo,
term.getName()
))
.toList();
return new UnivApplyInfoPreviewResponses(responses);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

API 변경 검증 및 캐시 전략 개선 권장

변경사항 요약:

  1. 캐시 키가 "univApplyInfoTextSearch:{0}"로 수정됨 (이전 리뷰 반영 완료)
  2. term 파라미터 제거
  3. 항상 현재 학기만 조회하도록 변경

주의사항:

  1. 이 메서드의 모든 호출부에서 term 인자가 제거되었는지 확인 필요
  2. 캐시 키에 현재 학기 정보가 포함되지 않아, 학기가 변경(예: 2024-1 → 2024-2)되어도 이전 학기 데이터가 최대 24시간 동안 캐시에 남아있을 수 있습니다

호출부 검증:

캐시 전략 개선 권장사항:

학기 변경 시 캐시 무효화를 위해 다음 중 하나를 고려해 주세요:

  • 방안 1: 캐시 키에 term ID 포함 (예: "univApplyInfoTextSearch:{0}:{1}" 형태로 text와 현재 termId 조합)
  • 방안 2: 현재 학기가 변경될 때 해당 캐시를 명시적으로 evict하는 로직 추가

현재 구조에서는 학기 전환 시점에 잘못된 데이터가 제공될 수 있으므로, 향후 관리자 API로 학기를 변경하는 기능 개발 시 캐시 무효화 전략도 함께 고려해 주세요.


🏁 Script executed:

#!/bin/bash
# Description: searchUnivApplyInfoByText 메서드의 모든 호출부 확인

rg -nP --type=java -C3 'searchUnivApplyInfoByText\s*\('

Length of output: 8545


변경 사항을 요약합니다.

  1. 호출부 검증:
    • searchUnivApplyInfoByText 호출부에서 term 인자 제거가 이미 적용되어 추가 수정이 불필요합니다.
  2. 캐시 전략 개선 제안:
    • 캐시 키에 term ID를 포함하도록 변경하여 학기 변경 시에도 올바른 데이터를 조회하도록 합니다 (예: univApplyInfoTextSearch:{text}:{termId}).
    • 또는 학기 변경 시점에 해당 캐시를 명시적으로 무효화하는 로직을 추가합니다.
🤖 Prompt for AI Agents
In
src/main/java/com/example/solidconnection/university/service/UnivApplyInfoQueryService.java
around lines 63 to 75, the search result currently only keys/addresses cache by
text which will serve stale results across term changes; update any caching
logic used for searchUnivApplyInfoByText to include the current term ID in the
cache key (for example "univApplyInfoTextSearch:{text}:{termId}") or add
explicit cache invalidation when the current term changes so search results are
correct for each term; ensure you obtain term.getId() and incorporate it into
the cache key or invalidation routine wherever the search results are cached.

Copy link
Member

@whqtker whqtker left a comment

Choose a reason for hiding this comment

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

1-6번 동일
7. 추가한 term_id 에 대해 FK 설정
8. term_id 기반 새로운 인덱스 생성
9. 기존 term 컬럼 기반 인덱스 제거
10. 기존 term 컬럼 제거

저는 이렇게 생각해봤습니다 ! term_id 관련 작업을 진행하고 term을 제거하는 식으로 가면 잘 되지 않을까 생각합니다.

@UniqueConstraint(
name = "uk_term_name",
columnNames = {"name"}
)
Copy link
Member

Choose a reason for hiding this comment

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

전 암묵적으로 UK에 속하는 필드가 1개면 @Columnunique 옵션을 사용하고 2개 이상이라면 @UniqueConstraint 를 사용했습니다 ! 만약 UK 필드가 1개라면 unique 옵션을 사용하는 건 어떻게 생각하시나요 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

엇.. 이거 한 번 이야기해보죠 회의 때!

Copy link
Contributor

Choose a reason for hiding this comment

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

저도 단일 필드 유니크는 unique 옵션으로 사용한 적이 많아서 더 익숙합니다!

private Long id;

@Column(length = 20)
private String name;
Copy link
Member

Choose a reason for hiding this comment

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

엔티티 또는 DTO에 @Pattern 으로 XXXX-X 형식의 정규표현식 검증이 있으면 더욱 좋을 거 같습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이게 추가학기의 경우는 2025-1-a 이런식으로 되어있어서 굳이 정규표현식 검증 안하긴 했습니다..
필요할 거 같으면 어드민 API에서 dto에서 검증하겠습니다~

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b5455e9 and 1d49a86.

📒 Files selected for processing (1)
  • src/main/resources/db/migration/V35__migrate_term_to_table.sql (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Comment on lines 57 to 65
ALTER TABLE application
MODIFY COLUMN term_id BIGINT NOT NULL;

ALTER TABLE mentor
MODIFY COLUMN term_id BIGINT NOT NULL;

ALTER TABLE university_info_for_apply
MODIFY COLUMN term_id BIGINT NOT NULL;

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

새 term_id 전환 후 기존 term 컬럼이 남아 신규 insert를 막습니다.

이번 PR에서 Application/Mentor/UnivApplyInfo 엔터티가 문자열 term 필드를 제거했는데, 마이그레이션에서는 해당 컬럼을 그대로 두고 NOT NULL 제약도 유지됩니다. 배포 후 새 레코드를 저장하면 JPA가 term 값을 넣지 않아 Field 'term' doesn't have a default value 오류가 즉시 발생합니다. 이번 스크립트 안에서 term 관련 인덱스를 먼저 제거한 뒤 ALTER TABLE … DROP COLUMN term;(또는 최소한 nullable 처리)까지 완료해야 합니다. 미루면 신규 쓰기가 전부 실패하니 꼭 이번 단계에서 정리해 주세요.

+-- 예시: 기존 term 컬럼 및 관련 인덱스 제거
+ALTER TABLE application DROP INDEX idx_app_user_term_delete;
+ALTER TABLE application DROP INDEX idx_app_first_choice_term_search;
+ALTER TABLE application DROP INDEX idx_app_second_choice_term_search;
+ALTER TABLE application DROP INDEX idx_app_third_choice_term_search;
+ALTER TABLE application DROP COLUMN term;
+
+ALTER TABLE mentor DROP COLUMN term;
+ALTER TABLE university_info_for_apply DROP COLUMN term;
📝 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
ALTER TABLE application
MODIFY COLUMN term_id BIGINT NOT NULL;
ALTER TABLE mentor
MODIFY COLUMN term_id BIGINT NOT NULL;
ALTER TABLE university_info_for_apply
MODIFY COLUMN term_id BIGINT NOT NULL;
ALTER TABLE application
MODIFY COLUMN term_id BIGINT NOT NULL;
-- 예시: 기존 term 컬럼 및 관련 인덱스 제거
ALTER TABLE application DROP INDEX idx_app_user_term_delete;
ALTER TABLE application DROP INDEX idx_app_first_choice_term_search;
ALTER TABLE application DROP INDEX idx_app_second_choice_term_search;
ALTER TABLE application DROP INDEX idx_app_third_choice_term_search;
ALTER TABLE application DROP COLUMN term;
ALTER TABLE mentor
MODIFY COLUMN term_id BIGINT NOT NULL;
ALTER TABLE mentor DROP COLUMN term;
ALTER TABLE university_info_for_apply
MODIFY COLUMN term_id BIGINT NOT NULL;
ALTER TABLE university_info_for_apply DROP COLUMN term;
🤖 Prompt for AI Agents
In src/main/resources/db/migration/V35__migrate_term_to_table.sql around lines
57 to 65, the migration leaves the old string term column in place (and keeps
NOT NULL), which will cause new inserts to fail because JPA no longer sets term;
fix it by first dropping any indexes/constraints referencing the term column for
application, mentor, and university_info_for_apply, then either ALTER TABLE ...
DROP COLUMN term for each table (preferred) or ALTER TABLE ... MODIFY COLUMN
term VARCHAR(...) NULL to make it nullable before removing the column in a later
migration; ensure index drops occur before the DROP COLUMN and verify column
names and index names match the schema.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/main/resources/db/migration/V36__migrate_term_to_table.sql (3)

41-54: 기존 term을 term_id로 매핑하는 INNER JOIN UPDATE 성능 고려.

대용량 데이터를 다루는 세 테이블에서 모두 동일한 패턴의 UPDATE가 실행됩니다. 현재 구조:

  • application, mentor, university_info_for_apply 각각에서 INNER JOIN을 통한 UPDATE 수행
  • 기존 term 컬럼에 인덱스가 있다면 효율적이겠지만, 없다면 풀 스캔 가능성

검증: 기존 스키마에서 term 컬럼의 인덱스 존재 여부를 확인하고, 필요시 마이그레이션 전 임시 인덱스 생성을 고려하세요.


93-151: FK 드롭 동적 SQL 패턴의 복잡도 및 유지보수성.

같은 패턴이 6번 반복됩니다 (@drop_fk1~4, PREPARE/EXECUTE/DEALLOCATE 반복):

SET @drop_fk_n = (SELECT CONCAT(...) FROM INFORMATION_SCHEMA...);
SET @drop_fk_n = IFNULL(@drop_fk_n, 'SELECT 1');
PREPARE stmt_n FROM @drop_fk_n;
EXECUTE stmt_n;
DEALLOCATE PREPARE stmt_n;

개선 아이디어:
이 패턴을 저장 프로시저나 Flyway 자체의 플레이스홀더 기능으로 추상화하면 가독성과 유지보수성이 향상될 수 있습니다. 그러나 현재 단계에서는 동작하는 것을 우선하므로, 적어도 변수명을 더 명확하게 (@drop_fk_first_choice, @drop_fk_second_choice 등) 개선하세요.


1-183: 마이그레이션 검증 및 롤백 전략 가이드 추가 권장.

1. 마이그레이션 후 검증:

  • 모든 application, mentor, university_info_for_apply 행이 유효한 term_id를 가지는지 확인
  • 고아 레코드(term_id = NULL) 유무 체크
  • FK 제약 조건 검증 쿼리

2. 롤백 고려사항:

  • 마이그레이션이 실패한 경우 롤백 스크립트나 가이드 필요
  • 특히 FK 삭제 후 인덱스 변경 구간에서 중단되면 복구가 복잡할 수 있음
  • 프로덕션 배포 전 스테이징 환경에서 충분한 테스트 필수

3. 운영 가이드:

  • 이 마이그레이션을 실행하기 전 데이터베이스 백업 필수
  • 마이그레이션 실행 시간 및 예상 다운타임 공지
  • 마이그레이션 후 검증 체크리스트 작성
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d49a86 and 0ce3b05.

📒 Files selected for processing (1)
  • src/main/resources/db/migration/V36__migrate_term_to_table.sql (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
src/main/resources/db/migration/V36__migrate_term_to_table.sql (1)

79-90: 기존 인덱스와 신규 인덱스의 생성·삭제 순서 검증 필요.

변경 내용:

  • 라인 79-90: term_id 기반 신규 인덱스 3개 생성 (idx_app_first_choice_term_id_search 등)
  • 라인 154-157: 기존 term 기반 인덱스 4개 삭제 (idx_app_first_choice_search 등)

확인 사항:

  1. 신규 인덱스가 이미 존재하는지 확인 (CREATE INDEX 시 IF NOT EXISTS 미사용)
  2. 기존 인덱스 삭제 시 다른 쿼리의 동시 실행으로 인한 성능 영향
  3. 인덱스 이름이 실제 스키마와 정확히 일치하는지 확인

Also applies to: 154-157

Comment on lines +26 to +28
INSERT INTO term (name, is_current)
VALUES ('2026-1', TRUE)
ON DUPLICATE KEY UPDATE is_current = TRUE;
Copy link

@coderabbitai coderabbitai bot Oct 30, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

현재 학기('2026-1')가 마이그레이션 스크립트에 하드코딩되어 있습니다.

마이그레이션 실행 시점에 관계없이 '2026-1'이 강제로 현재 학기로 설정되는 구조입니다. 다음 우려 사항이 있습니다:

  1. 실행 시기에 맞지 않는 term 값 설정 가능성
  2. 다양한 학기 형식이나 변경 요청 시 스크립트 재수정 필요
  3. 향후 관리자 API 추가 시 일관성 문제 가능

제안: 마이그레이션 가이드 또는 환경 변수/매개변수화 방식을 통해 현재 학기를 지정하도록 개선하거나, 적어도 주석에 수동 조정 필요성을 명시하세요.

🤖 Prompt for AI Agents
In src/main/resources/db/migration/V36__migrate_term_to_table.sql around lines
26 to 28, the migration hardcodes the current term as '2026-1', which forces
that value on every run; change the script to avoid a fixed term by either
reading the current term from an environment variable or migration parameter
(and using that variable in the INSERT ... VALUES), or add a clear top-of-file
comment instructing operators to set the term value manually before running and
documenting the expected format; ensure the chosen approach validates non-empty
input and uses a safe UPSERT (ON DUPLICATE KEY UPDATE) with the provided value
rather than a literal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai
이 스크립트 반영되는 시기엔 2026-1로 할거야

Copy link

Choose a reason for hiding this comment

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

@Gyuhyeok99, 알겠습니다! 배포 시점에 2026-1 학기가 적용될 예정이라면 현재 스크립트가 적절하네요. 확인해주셔서 감사합니다. 👍

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/main/resources/db/migration/V36__migrate_term_to_table.sql (3)

42-56: 데이터 마이그레이션 완성도 검증 추가 권장

마이그레이션의 핵심 단계인 기존 term 값을 term_id로 변환하는 부분에서, 다음과 같은 개선이 있으면 좋겠습니다:

  1. 검증 로직 부재: UPDATE 이후 각 테이블에서 여전히 term_id가 NULL인 행이 있는지 확인하는 절차가 없습니다.
  2. 부분 마이그레이션 위험성: WHERE 절로 이미 마이그레이션된 행은 재처리하지 않지만, 혹시 놓친 행이 있을 수 있습니다.

제안:
다음을 추가하여 마이그레이션 완전성을 보증하세요:

  • 각 UPDATE 후 NULL 체크 (SELECT COUNT(*) WHERE term_id IS NULL)
  • 실패 시 트랜잭션 롤백 전략
  • 로그/확인용 SELECT 쿼리 주석으로 포함
-- 각 UPDATE 후 NULL 검증 추가
SELECT COUNT(*) AS null_count FROM application WHERE term_id IS NULL;
SELECT COUNT(*) AS null_count FROM mentor WHERE term_id IS NULL;
SELECT COUNT(*) AS null_count FROM university_info_for_apply WHERE term_id IS NULL;

-- 위 쿼리가 모두 0을 반환해야 진행

94-153: 동적 SQL 기반 FK 삭제 로직 복잡도 및 안정성 우려

학기 정보와 관련된 기존 FK를 조건부로 삭제하는 로직(lines 94~153)이 상당히 복잡하며, 몇 가지 우려 사항이 있습니다:

  1. 중복되는 패턴: 4개의 FK 삭제가 거의 동일한 PREPARE/EXECUTE 사이클을 반복합니다.
  2. 조용한 실패 가능성: INFORMATION_SCHEMA 쿼리가 결과를 찾지 못할 경우 IFNULL로 'SELECT 1'을 실행하는데, 이는 실제 오류를 감춘 채 진행할 수 있습니다.
  3. FK 명명 규칙 의존성: CONSTRAINT_NAME을 동적으로 조회하는데, 기존 제약 조건이 예상과 다른 이름일 경우 문제 발생.

개선 방안:

  • 동적 SQL을 한 번의 PROCEDURE 또는 더 간단한 로직으로 통합
  • 또는 명시적 FK 이름이 고정되어 있다면 직접 DROP 사용
  • 에러 로깅 추가하여 FK 삭제 실패 감지
-- 현재 방식 (복잡):
-- 4개의 PREPARE/EXECUTE 반복

-- 개선 방안 1: 고정 FK 이름 사용 (가능한 경우)
ALTER TABLE application DROP FOREIGN KEY IF EXISTS fk_application_first_choice_university_info_for_apply_id;
-- ...기타 FK도 동일

-- 개선 방안 2: 단일 동적 SQL 절차
-- (복잡도는 유사하지만 유지보수성 향상)

1-185: 마이그레이션 전략 전반에 대한 롤백 계획 및 문서화

이번 마이그레이션은 여러 테이블을 동시에 수정하는 대규모 작업이므로, 다음 사항이 권장됩니다:

  1. 롤백 스크립트: 마이그레이션 실패 시 원래 상태로 복구하는 역방향 마이그레이션 스크립트(V37__rollback_term_migration.sql 등)를 준비하세요.
  2. 실행 전 백업: 프로덕션 환경에서 마이그레이션 실행 전 데이터베이스 전체 백업을 권장합니다.
  3. 단계별 검증:
    • 각 UPDATE 후 행 수 확인
    • FK 재생성 이후 데이터 무결성 체크 (참조 정합성 검증)
    • 인덱스 생성 후 쿼리 성능 확인
  4. 배포 시점 재확인: 하드코딩된 '2026-1' 외 추가 학기가 필요하면 스크립트 재조정 필요.

이는 필수 사항은 아니지만, 대규모 마이그레이션 안정성을 위해 강력히 권장합니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ce3b05 and 10ffecc.

📒 Files selected for processing (1)
  • src/main/resources/db/migration/V36__migrate_term_to_table.sql (1 hunks)
🔇 Additional comments (2)
src/main/resources/db/migration/V36__migrate_term_to_table.sql (2)

81-93: 인덱스 생성 시점 및 FK 제약 조건 순서 확인

새로운 term_id 기반 인덱스를 lines 8193에서 생성하는데, FK 제약 조건은 lines 6879에서 이미 추가되었으므로 시간 순서상 문제는 없습니다. 다만 다음을 확인하세요:

  1. FK 제약 추가 이후 인덱스 생성이 더 명확: 현재 순서(FK 먼저, 인덱스 나중)가 MySQL에서 정상 작동하지만, 일반적 관례상 인덱스는 테이블 구조 확정 후 추가하는 것이 맞습니다.
  2. 인덱스 이름 충돌 여부: 기존 인덱스 삭제(lines 155~159) 이후 새 인덱스 생성이므로 이름 충돌은 없습니다. ✓

코멘트: 전체 시퀀스 흐름상 논리적으로는 안전해 보이며, 추가 조치는 필수적이지 않습니다.


155-164: 기존 인덱스 및 컬럼 삭제 전 최종 검증 필요

term 컬럼과 관련 인덱스를 삭제하는 단계(lines 155~164)에서 다음을 점검하세요:

  1. 인덱스 이름 확인: 삭제할 인덱스 4개(idx_app_user_term_delete, idx_app_first/second/third_choice_search)가 현재 DB에 실제로 존재하는가?
  2. term 컬럼 의존성: application, mentor, university_info_for_apply 테이블의 term 컬럼을 참조하는 다른 객체(뷰, 트리거 등)가 없는가?

제안:

-- 마이그레이션 전 사전 점검 쿼리
SHOW INDEXES FROM application WHERE Key_name IN (
    'idx_app_user_term_delete',
    'idx_app_first_choice_search',
    'idx_app_second_choice_search',
    'idx_app_third_choice_search'
);

실제 환경에서는 스크립트 실행 전에 위 쿼리로 기존 인덱스 존재 여부를 사전 확인하기 바랍니다.

Copy link
Contributor

@sukangpunch sukangpunch left a comment

Choose a reason for hiding this comment

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

고생하셨습니다!

# Conflicts:
#	src/main/resources/secret
#	src/test/java/com/example/solidconnection/application/service/ApplicationQueryServiceTest.java
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java (1)

34-52: 일관성을 위해 helper 메서드를 사용하는 것을 고려하세요.

이 메서드는 Line 48에서 univApplyInfo.termId.eq(termId)로 직접 비교하고 있지만, findAllByFilter(Line 93)와 findAllByText(Line 159)는 termIdEq helper 메서드를 사용합니다.

인터페이스의 타입이 Long으로 통일되면(이전 리뷰 코멘트 참조) 이 메서드도 nullable termId를 받게 되므로, 다른 메서드들과 동일하게 helper를 사용하는 것이 일관성과 null 처리 측면에서 바람직합니다.

다음 diff를 적용하세요:

                 .where(
                         regionCodeEq(country, regionCode)
                                 .and(countryOrUniversityContainsKeyword(country, university, keywords))
-                                .and(univApplyInfo.termId.eq(termId))
+                                .and(termIdEq(univApplyInfo, termId))
                 )
src/test/java/com/example/solidconnection/application/service/ApplicationQueryServiceTest.java (1)

82-99: 1. 현재 학기 Term 픽스처 초기화 검토

문장 단위로 말씀드릴게요.
현재 setUp()에서 termFixture.현재_학기("2025-2")가 호출될 때마다 DB에 동일한 학기를 계속 삽입한다면, 학기명에 유니크 제약이 있거나 is_current=true가 하나만 허용되는 경우 테스트가 중복 데이터로 실패할 수 있습니다.
테스트 실행마다 기존 학기를 재사용하거나, 픽스처가 중복 생성을 방지하도록 조정해 주세요.

src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java (1)

51-146: 1. 현재 학기 조회 로직 중복 제거 제안

친절하게 한 문장씩 안내드릴게요.
getApplicants, getApplicantsByUserApplications, validateSiteUserCanViewApplicants 세 곳에서 동일한 현재 학기 조회 코드가 반복되고 있어요.
private Term getCurrentTerm()과 같은 헬퍼를 두면 예외 처리와 로깅을 한 곳에서 관리할 수 있어 유지보수성이 높아집니다.
필요하다면 캐싱 정책 변경 시에도 한 군데만 손보면 돼서 위험을 줄일 수 있답니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10ffecc and 3f0caf0.

📒 Files selected for processing (8)
  • src/main/java/com/example/solidconnection/application/domain/Application.java (6 hunks)
  • src/main/java/com/example/solidconnection/application/service/ApplicationQueryService.java (7 hunks)
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java (1 hunks)
  • src/main/java/com/example/solidconnection/mentor/domain/Mentor.java (1 hunks)
  • src/main/java/com/example/solidconnection/university/domain/UnivApplyInfo.java (1 hunks)
  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepository.java (1 hunks)
  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java (7 hunks)
  • src/test/java/com/example/solidconnection/application/service/ApplicationQueryServiceTest.java (23 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/com/example/solidconnection/common/exception/ErrorCode.java
  • src/main/java/com/example/solidconnection/mentor/domain/Mentor.java
  • src/main/java/com/example/solidconnection/university/domain/UnivApplyInfo.java
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-26T05:07:54.817Z
Learnt from: nayonsoso
Repo: solid-connection/solid-connect-server PR: 486
File: src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java:0-0
Timestamp: 2025-08-26T05:07:54.817Z
Learning: 대학지원정보 텍스트 검색에서 region과 country 간의 INNER JOIN은 데이터 무결성을 위한 의도된 설계이다. country.region_code가 region 테이블에 존재하지 않는 경우는 잘못된 데이터 상태로 간주하여 검색 결과에서 제외한다.

Applied to files:

  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java
📚 Learning: 2025-08-26T05:00:56.556Z
Learnt from: nayonsoso
Repo: solid-connection/solid-connect-server PR: 486
File: src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java:166-178
Timestamp: 2025-08-26T05:00:56.556Z
Learning: UnivApplyInfo의 텍스트 검색 기능에서는 University.koreanName이 아닌 UnivApplyInfo.koreanName을 검색 대상으로 사용하는 것이 의도된 설계이다.

Applied to files:

  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java
  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepository.java
📚 Learning: 2025-08-20T13:10:27.822Z
Learnt from: nayonsoso
Repo: solid-connection/solid-connect-server PR: 471
File: src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java:80-85
Timestamp: 2025-08-20T13:10:27.822Z
Learning: University와 Country 엔티티는 EAGER 연관관계로 설정되어 있어서 fetchJoin() 없이도 자동으로 함께 로딩된다.

Applied to files:

  • src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepositoryImpl.java (3)

78-111: 잘 구현되었습니다.

termIdEq helper 메서드를 올바르게 활용하고 있으며, 언어 시험 점수 필터링 로직도 명확하게 분리되어 있습니다.


122-127: null 처리가 적절합니다.

Helper 메서드가 wrapper Long을 받아 null check를 수행하도록 올바르게 구현되었습니다. 이는 termId 기반 필터링이 선택적일 수 있음을 의미론적으로 잘 표현하고 있습니다.


147-186: 텍스트 검색 로직이 잘 구성되었습니다.

termIdEq helper를 통한 termId 필터링이 올바르게 적용되었고, 기존의 텍스트 매칭 및 우선순위 정렬 로직도 유지되고 있습니다.

Based on learnings (텍스트 검색에서 UnivApplyInfo.koreanName 사용 및 region과 country 간 INNER JOIN은 의도된 설계).

Comment on lines 9 to 13
List<UnivApplyInfo> findAllByRegionCodeAndKeywordsAndTermId(String regionCode, List<String> keywords, long term);

List<UnivApplyInfo> findAllByFilter(LanguageTestType testType, String testScore, String term, List<String> countryKoreanNames);
List<UnivApplyInfo> findAllByFilter(LanguageTestType testType, String testScore, Long termId, List<String> countryKoreanNames);

List<UnivApplyInfo> findAllByText(String text, String term);
List<UnivApplyInfo> findAllByText(String text, Long termId);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

타입과 파라미터명의 일관성을 맞춰주세요.

세 메서드 시그니처 간에 불일치가 있습니다:

  1. 타입 불일치: Line 9는 primitive long을, Lines 11과 13은 wrapper Long을 사용합니다.
  2. 파라미터명 불일치: Line 9는 term으로, Lines 11과 13은 termId로 명명되어 있습니다.

primitive long은 null을 표현할 수 없어서 구현체의 null check 로직(Line 122-127)과 맞지 않으며, 세 메서드 모두 termId 기반 필터링이라는 동일한 의미를 가지므로 일관된 시그니처를 사용해야 합니다.

다음 diff를 적용하여 일관성을 확보하세요:

-    List<UnivApplyInfo> findAllByRegionCodeAndKeywordsAndTermId(String regionCode, List<String> keywords, long term);
+    List<UnivApplyInfo> findAllByRegionCodeAndKeywordsAndTermId(String regionCode, List<String> keywords, Long termId);
📝 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
List<UnivApplyInfo> findAllByRegionCodeAndKeywordsAndTermId(String regionCode, List<String> keywords, long term);
List<UnivApplyInfo> findAllByFilter(LanguageTestType testType, String testScore, String term, List<String> countryKoreanNames);
List<UnivApplyInfo> findAllByFilter(LanguageTestType testType, String testScore, Long termId, List<String> countryKoreanNames);
List<UnivApplyInfo> findAllByText(String text, String term);
List<UnivApplyInfo> findAllByText(String text, Long termId);
List<UnivApplyInfo> findAllByRegionCodeAndKeywordsAndTermId(String regionCode, List<String> keywords, Long termId);
List<UnivApplyInfo> findAllByFilter(LanguageTestType testType, String testScore, Long termId, List<String> countryKoreanNames);
List<UnivApplyInfo> findAllByText(String text, Long termId);
🤖 Prompt for AI Agents
In
src/main/java/com/example/solidconnection/university/repository/custom/UnivApplyInfoFilterRepository.java
around lines 9 to 13, make the method signatures consistent by changing the
first method to use the Long wrapper and the same parameter name as the others:
replace the primitive long term with Long termId so all three methods use Long
termId; update any references in callers/implementations if necessary to the new
parameter name to preserve nullability and match the existing null-check logic
in the implementation (lines ~122-127).

@Gyuhyeok99 Gyuhyeok99 merged commit f653186 into solid-connection:develop Nov 3, 2025
1 of 2 checks passed
Gyuhyeok99 added a commit that referenced this pull request Nov 4, 2025
fix: PostRepository & CommentRepository의 일부 메서드의 정렬 순서 오류 해결 (#522) 
refactor: 이메일 유니크키 제거
refactor: 엔티티가 BaseEntity를 상속하도록 (#524) 
fix: BaseEntity 관련 마이그레이션 파일 수정 (#530) 
feat: 멘토 승격 api 구현 (#532) 
fix: 대학교 중복 오류 수정 (#510) 
refactor: 불필요한 로그 삭제 (#543)
fix: 운영환경 8081 포트 설정 추가 (#542) 
fix: dev환경 디비명 변경 (#546)
refactor: 불필요한 로그 삭제 (#547) 
refactor: 학기를 테이블로 관리하도록 변경 (#526) 
refactor: 모의지원 시 지원한 대학 정보 응답 추가 (#539)
@Gyuhyeok99 Gyuhyeok99 mentioned this pull request Nov 4, 2025
@Gyuhyeok99 Gyuhyeok99 deleted the refactor/418-delegation-term-table branch November 9, 2025 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: 파견 학기를 테이블로 관리한다.

3 participants