Skip to content

Conversation

@sukangpunch
Copy link
Contributor

관련 이슈

작업 내용

어드민에서 멘토 지원서를 승인 할 때, 유저의 Role 을 Mentor 로 승격하도록 기능을 수정하였습니다.

특이 사항

리뷰 요구사항 (선택)

@sukangpunch sukangpunch self-assigned this Feb 2, 2026
@sukangpunch sukangpunch added the 버그 Something isn't working label Feb 2, 2026
@sukangpunch sukangpunch requested a review from JAEHEE25 as a code owner February 2, 2026 12:25
@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2026

Walkthrough

  1. AdminMentorApplicationService에 멘토 생성 흐름과 MentorRepository 주입을 추가했습니다.
  2. 승인 시 SiteUser 조회, validateUserCanCreateMentor 검증, SiteUser.becomeMentor 호출, Mentor 엔티티 생성 및 저장 로직이 추가되었습니다.
  3. SiteUser 도메인에 역할을 MENTOR로 변경하는 public 메서드 becomeMentor()를 추가했습니다.
  4. 테스트(AdminMentorApplicationServiceTest)에 MentorRepository·SiteUserRepository 주입과 멘토 생성/중복 예외 검증을 추가했습니다.
  5. MentorMyPageController에서 멘토 마이페이지 생성 POST 엔드포인트와 관련 DTO를 제거했습니다.
  6. Mentor 엔티티의 introduction·passTip을 nullable로 변경하고 createChannels 메서드를 삭제했습니다.
  7. MentorMyPageCreateRequest DTO 파일을 삭제했습니다.
  8. MentorMyPageService에서 createMentorMyPage 공개 API와 관련 헬퍼를 제거하고 업데이트 로직을 일부 인라인화했습니다.
  9. DB 마이그레이션 V44로 mentor.introduction 및 mentor.pass_tip 컬럼을 NULL 허용으로 변경했습니다.
  10. MentorMyPageServiceTest에서 멘토 생성 관련 테스트 클래스를 삭제했습니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • wibaek
  • Hexeong
  • JAEHEE25
  • lsy1307
  • whqtker
🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning 일부 변경사항이 #636의 주요 목표(유저 role 변경 및 멘토 생성)와 직접적으로 관련되지 않은 것으로 보입니다. MentorMyPageCreateRequest, MentorMyPageController의 createMentorMyPage 메서드 제거 등은 범위를 벗어난 변경입니다. PR 범위를 #636 이슈의 핵심 요구사항(승인 시 유저 role 변경 및 멘토 생성)에만 집중하고, MentorMyPage 생성 관련 제거 사항은 별도 이슈로 분리하는 것을 권장합니다.
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 (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항인 '멘토 지원서 승인 시 유저 Role을 Mentor로 승격'을 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명이 템플릿 구조를 따르고 있으며, 관련 이슈(#636)와 작업 내용이 명확히 기술되어 있습니다.
Linked Issues check ✅ Passed PR의 코드 변경사항이 #636의 요구사항과 일치합니다. AdminMentorApplicationService에서 SiteUser의 role을 MENTOR로 변경하고 Mentor 엔티티를 생성하는 로직이 구현되었으며, 테스트도 추가되었습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
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.

고생하셨습니다 ~!

다만 현재는 siteUserroleMENTOR 로 변경하는 거 같은데, mentor 엔티티 또한 생성해야 합니다 ! 이후 관련하여 테스트(승인하면 멘토 생성)도 추가해주시면 좋겠습니다 !!

@sukangpunch sukangpunch closed this Feb 3, 2026
@sukangpunch sukangpunch reopened this Feb 3, 2026
@sukangpunch
Copy link
Contributor Author

고생하셨습니다 ~!

다만 현재는 siteUserroleMENTOR 로 변경하는 거 같은데, mentor 엔티티 또한 생성해야 합니다 ! 이후 관련하여 테스트(승인하면 멘토 생성)도 추가해주시면 좋겠습니다 !!

알겠습니다!

- not null 인 필드에 빈문자열로 값을 채우는 것 보다, null 허용이 더 의미
  있다 판단하여 null 을 허용하도록 하였습니다.
- 멘토 생성의 주체가 어드민으로 변경되어 Mentor 도메인의 Mentor 생성 api 를 제거
- 중복 멘토 생성 예외 처리 및 테스트 추가
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

Caution

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

⚠️ Outside diff range comments (1)
src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java (1)

40-40: ⚠️ Potential issue | 🟡 Minor

사용하지 않는 mentorApplicationRepository 의존성을 제거하세요.

createMentorMyPage 메서드가 제거되면서 이 클래스 내에서 mentorApplicationRepository를 참조하는 코드가 더 이상 없습니다. 불필요한 의존성은 정리하는 것이 좋습니다.

  1. import 제거 (15번 줄)
  2. 필드 주입 제거 (40번 줄)
정리할 코드
-import com.example.solidconnection.mentor.repository.MentorApplicationRepository;
     private final MentorRepository mentorRepository;
     private final SiteUserRepository siteUserRepository;
     private final HostUniversityRepository hostUniversityRepository;
     private final TermRepository termRepository;
-    private final MentorApplicationRepository mentorApplicationRepository;
🧹 Nitpick comments (2)
src/main/java/com/example/solidconnection/mentor/domain/Mentor.java (1)

56-68: 생성자의 universityId 파라미터 타입 불일치에 주의하세요.

  1. 생성자 파라미터는 Long universityId (박싱 타입, line 60)인데, 필드는 long universityId (원시 타입, line 46)입니다.
  2. 만약 null이 전달되면 auto-unboxing 시 NullPointerException이 발생합니다.

학습된 비즈니스 규칙에 따르면 APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가지므로 현재 흐름에서는 문제가 없지만, 방어적 코딩 관점에서 파라미터 타입을 long으로 통일하거나, null 체크를 추가하는 것을 고려해 주세요. Based on learnings, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 비즈니스 규칙이 있습니다.

💡 파라미터 타입 통일 제안
     public Mentor(
             String introduction,
             String passTip,
             long siteUserId,
-            Long universityId,
+            long universityId,
             long termId
     ) {
src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java (1)

48-67: 멘토 생성 로직이 잘 구현되어 있습니다. 다만 검증 순서를 개선하면 더 안전합니다.

현재 흐름:

  1. mentorApplication.approve() (상태 변경) → line 51
  2. validateUserCanCreateMentor() (중복 검증) → line 55

트랜잭션 롤백으로 인해 실제 데이터 무결성 문제는 없지만, approve() 이전에 모든 사전 검증을 완료하는 것이 방어적 프로그래밍 관점에서 더 안전합니다. 만약 향후 approve() 내부에 이벤트 발행 등의 부수 효과가 추가된다면, 롤백만으로는 충분하지 않을 수 있습니다.

🔄 검증 순서 변경 제안
     `@Transactional`
     public void approveMentorApplication(Long mentorApplicationId) {
         MentorApplication mentorApplication = mentorApplicationRepository.findById(mentorApplicationId)
                 .orElseThrow(() -> new CustomException(MENTOR_APPLICATION_NOT_FOUND));
-        mentorApplication.approve();
 
         SiteUser siteUser = siteUserRepository.findById(mentorApplication.getSiteUserId())
                 .orElseThrow(() -> new CustomException(USER_NOT_FOUND));
         validateUserCanCreateMentor(siteUser.getId());
 
+        mentorApplication.approve();
         siteUser.becomeMentor();
         Mentor mentor = new Mentor(
                 null,
                 null,
                 siteUser.getId(),
                 mentorApplication.getUniversityId(),
                 mentorApplication.getTermId()
         );
 
         mentorRepository.save(mentor);
     }

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 left a comment

Choose a reason for hiding this comment

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

고생하셨씁니다~~

Comment on lines 49 to 57
MentorApplication mentorApplication = mentorApplicationRepository.findById(mentorApplicationId)
.orElseThrow(() -> new CustomException(MENTOR_APPLICATION_NOT_FOUND));

mentorApplication.approve();

SiteUser siteUser = siteUserRepository.findById(mentorApplication.getSiteUserId())
.orElseThrow(() -> new CustomException(USER_NOT_FOUND));
validateUserCanCreateMentor(siteUser.getId());

siteUser.becomeMentor();
Copy link
Contributor

Choose a reason for hiding this comment

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

어차피 한 트랜잭션 내에서 작동하니 크게 상관은 없을 거 같긴한데 검증이 먼저되는 게 좋을 거 같아서

siteUser = siteUserRepository.findById(...) // 1. 유저 조회
validateUserCanCreateMentor(siteUser.getId()); // 2. 검증

mentorApplication.approve(); // 3. 상태 변경
siteUser.becomeMentor(); // 4. Role 승격
mentorRepository.save(mentor); // 5. 멘토 생성

이런식으로 순서를 바꾸는 건 어떤가요?
사소하긴 합니다

Copy link
Contributor Author

Choose a reason for hiding this comment

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

해당 방향으로 수정 하겠습니다!

Comment on lines 58 to 64
Mentor mentor = new Mentor(
null,
null,
siteUser.getId(),
mentorApplication.getUniversityId(),
mentorApplication.getTermId()
);
Copy link
Contributor

Choose a reason for hiding this comment

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

이거 null넘기는 것보단 밑에 3개만 받는 생성자나 정적팩토리 메서드 만드는 건 어떤가요?
예전에 형준님이 언급했었던 컨벤션인 거 같긴한데 저희가 잘 지켜지진 않고 있는 거로 압니다..
근데 지금 보니 좀 신경쓰이는 거 같아서 이번거부터 조금씩 고쳐보는 거 어떤가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

좋습니다! 3개만 받는 생성자를 활용 하는 방향으로 수정 해 보겠습니다.

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.

확인했습니다 ~ 고생하셨어요 👍

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java`:
- Around line 68-72: Add a DB-level UNIQUE constraint on the mentor.site_user_id
to prevent TOCTOU race conditions in addition to the existing application check
in validateUserCanCreateMentor (which uses mentorRepository.existsBySiteUserId).
Fix this by adding a migration that runs ALTER TABLE mentor ADD CONSTRAINT
uk_mentor_site_user_id UNIQUE (site_user_id) and/or annotate the Mentor entity
with `@Table`(uniqueConstraints = `@UniqueConstraint`(columnNames = "siteUserId"))
(ensure the column name matches the mapped column, e.g., site_user_id), and keep
the existing existsBySiteUserId check to preserve the friendly
CustomException(MENTOR_ALREADY_EXISTS) for pre-checks.
🧹 Nitpick comments (1)
src/main/java/com/example/solidconnection/mentor/domain/Mentor.java (1)

70-78: 새 생성자는 깔끔합니다. 다만 Long universityIdlong universityId 자동 언박싱 주의가 필요합니다.

  1. 파라미터 타입이 Long(boxed)인데, 필드 타입은 long(primitive)입니다 (Line 46).
  2. 비즈니스 규칙상 승인 시점에 universityId는 항상 non-null이지만, 만약 null이 전달되면 NullPointerException이 발생합니다.
  3. 기존 생성자(Line 56-68)도 동일한 패턴이라 이 PR의 범위는 아니지만, 방어적으로 파라미터를 long으로 통일하거나 null 체크를 추가하면 더 안전합니다.
💡 선택적 리팩터링 제안
     public Mentor(
-            long siteUserId,
-            Long universityId,
-            long termId
+            long siteUserId,
+            long universityId,
+            long termId
     ) {

Based on learnings, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 비즈니스 규칙이 있으므로, long 타입이 더 적절합니다.

Comment on lines +68 to 72
private void validateUserCanCreateMentor(long siteUserId) {
if (mentorRepository.existsBySiteUserId(siteUserId)) {
throw new CustomException(MENTOR_ALREADY_EXISTS);
}
}
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

🧩 Analysis chain

🏁 Script executed:

# Mentor 관련 마이그레이션 파일 찾기
find . -type f -name "*.sql" -o -name "*Mentor*Migration*.java" | head -20

Repository: solid-connection/solid-connect-server

Length of output: 1469


🏁 Script executed:

# site_user_id 유니크 제약 관련 SQL 패턴 검색
rg -i 'unique.*site_user_id|site_user_id.*unique' --type sql -A 2 -B 2

Repository: solid-connection/solid-connect-server

Length of output: 2555


🏁 Script executed:

# Mentor 엔티티 클래스 조회
find . -type f -name "Mentor.java" | grep -E 'entity|domain|model'

Repository: solid-connection/solid-connect-server

Length of output: 150


🏁 Script executed:

# 전체 Mentor 엔티티 파일 위치 찾기
fd -t f "Mentor.java"

Repository: solid-connection/solid-connect-server

Length of output: 148


🏁 Script executed:

# Mentor 엔티티 파일 확인
cat -n ./src/main/java/com/example/solidconnection/mentor/domain/Mentor.java

Repository: solid-connection/solid-connect-server

Length of output: 3974


🏁 Script executed:

# V19 마이그레이션 파일 (Mentor 테이블 생성) 확인
cat -n ./src/main/resources/db/migration/V19__create_mentor_related_tables.sql

Repository: solid-connection/solid-connect-server

Length of output: 2317


🏁 Script executed:

# V26 마이그레이션 파일 (term column 추가) 확인
cat -n ./src/main/resources/db/migration/V26__add_term_column_to_mentor.sql

Repository: solid-connection/solid-connect-server

Length of output: 155


멘토 중복 생성을 방지하기 위해 데이터베이스 레벨 유니크 제약을 추가하세요.

검증 결과, 현재 코드는 다음과 같은 상황입니다:

  1. 애플리케이션 레벨 검증

    • existsBySiteUserId() 체크로 비즈니스 규칙을 지키고 있습니다.
  2. 데이터베이스 레벨 보호 부재 (권장 개선)

    • mentor 테이블에 site_user_id 컬럼에 대한 UNIQUE 제약이 없습니다.
    • Mentor 엔티티에도 @Table(uniqueConstraints) 정의가 없습니다.
    • TOCTOU(Time-of-Check-to-Time-of-Use) 경쟁 조건이 발생할 수 있습니다.
  3. 권장사항

    • 마이그레이션 파일에서 ALTER TABLE mentor ADD CONSTRAINT uk_mentor_site_user_id UNIQUE (site_user_id); 추가
    • 또는 Mentor 엔티티에 @Table(uniqueConstraints = @UniqueConstraint(columnNames = "siteUserId")) 추가

어드민 전용 기능이라 동시 호출 가능성은 낮지만, 데이터 무결성을 위해 DB 레벨 제약을 추가하면 더욱 안전합니다.

🤖 Prompt for AI Agents
In
`@src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java`
around lines 68 - 72, Add a DB-level UNIQUE constraint on the
mentor.site_user_id to prevent TOCTOU race conditions in addition to the
existing application check in validateUserCanCreateMentor (which uses
mentorRepository.existsBySiteUserId). Fix this by adding a migration that runs
ALTER TABLE mentor ADD CONSTRAINT uk_mentor_site_user_id UNIQUE (site_user_id)
and/or annotate the Mentor entity with `@Table`(uniqueConstraints =
`@UniqueConstraint`(columnNames = "siteUserId")) (ensure the column name matches
the mapped column, e.g., site_user_id), and keep the existing existsBySiteUserId
check to preserve the friendly CustomException(MENTOR_ALREADY_EXISTS) for
pre-checks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

버그 Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: 멘토 지원서 승인 시 사용자 role을 MENTOR로 변경하도록 수정

3 participants