-
Notifications
You must be signed in to change notification settings - Fork 8
feat: 어드민 멘토 승격 요청 페이징 조회 기능 추가 #576
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: 어드민 멘토 승격 요청 페이징 조회 기능 추가 #576
Conversation
* feat: mentor/repository 패키지에 custom 패키지 추가 - custom 패키지에 페이징 조회를 책임지는 MentorApplicationFilterRepository 추가 - MentorApplicationSearchCondition 에서 넘긴 keyword 기반으로 닉네임, 권역, 나라, 학교명으로 필터링 검색 기능 추가 - MentorApplicationSearchCondition 에서 넘긴 mentorApplicationStatus 기반으로 승인, 거절, 진행중 으로 필터링 기능 추가
Walkthrough
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50분
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (4)
src/main/java/com/example/solidconnection/mentor/repository/custom/MentorApplicationFilterRepositoryImpl.java (2)
31-94: 1) 멘토 지원서 검색 쿼리 설계 전반적으로 잘 맞습니다
- 세 가지 DTO projection을 static 상수로 만들어
select(...)에 재사용하는 구조가 깔끔하고, N+1 없이 한 번에 필요한 필드를 모두 가져오는 점이 좋습니다.mentorStatus,keyword,createdAt를 각각 helper 메서드로 분리해서 content 쿼리와 count 쿼리에서 동일한 where 조건을 공유하는 것도 페이지네이션 일관성 유지에 도움이 됩니다.- 키워드 검색을 닉네임/대학/지역/나라
containsIgnoreCaseOR 조합으로 구현한 부분이 기획에서 말한 “텍스트 기반 다수 컬럼 조회, 우선순위 없음” 요구사항과 잘 들어맞습니다.totalCount가null일 가능성은 사실상 없어서totalCount != null ? totalCount : 0L는 방어 코드에 가깝지만, 문제는 아니고 유지하셔도 무방합니다.- PENDING 상태에서
universityId가 null일 수 있어 left join을 사용하는 구조가 필요한데, 여기서leftJoin(university)...leftJoin(region)...leftJoin(country)를 쓰신 방향이 그 비즈니스 규칙과도 잘 맞습니다. (Based on learnings, universityId-null 규칙 확인됨)
65-70: 2) 생성자@Autowired는 선택 사항이라 제거해도 됩니다 (선택)
- 스프링 부트에서 생성자가 하나뿐인 경우에는
@Autowired가 없어도 자동 주입되므로,MentorApplicationFilterRepositoryImpl(EntityManager em)위의@Autowired는 생략 가능한 보일러플레이트입니다.- 팀 코드 스타일이 “생성자에는 @Autowired 생략”이라면 제거해서 다른 빈들과 일관성을 맞추는 것도 좋겠습니다.
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (2)
27-29: 2) 테스트 클래스@DisplayName문구가 도메인과 조금 어긋납니다 (선택)
- 현재 클래스 레벨
@DisplayName("어학 검증 관리자 서비스 테스트")는 이전 테스트에서 복사된 것으로 보이고, 실제로는 “멘토 승격 지원서 목록 조회”에 대한 테스트입니다.- 추후 리포트/IDE에서 테스트 명세를 볼 때 헷갈리지 않도록, 예를 들어
"관리자 멘토 승격 지원서 서비스 테스트"처럼 이 도메인에 맞는 이름으로 정리해 두면 좋겠습니다.
161-180: 3) 날짜 조건 테스트에서LocalDate.now()사용은 향후 유지보수 시 주의가 필요합니다 (선택)
MentorApplicationSearchCondition의createdAt을LocalDate.now()로 셋업하면, 테스트 실행 시점의 ‘오늘 날짜’에 생성된 신청서만 조회하는 동작은 잘 검증됩니다.- 다만 나중에 fixture에서
createdAt을 명시적으로 다른 날짜로 세팅하거나, 타임존 설정을 바꾸는 등의 변경이 생기면 이 테스트가 의도치 않게 깨질 수 있으니, 필요하다면 fixture에서createdAt을 고정 날짜로 세팅하고 그 날짜를 조건에 넘기는 형태로 바꾸는 것도 고려해볼 만합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java(1 hunks)src/main/java/com/example/solidconnection/admin/dto/MentorApplicationResponse.java(1 hunks)src/main/java/com/example/solidconnection/admin/dto/MentorApplicationSearchCondition.java(1 hunks)src/main/java/com/example/solidconnection/admin/dto/MentorApplicationSearchResponse.java(1 hunks)src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java(1 hunks)src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java(2 hunks)src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java(1 hunks)src/main/java/com/example/solidconnection/mentor/repository/custom/MentorApplicationFilterRepository.java(1 hunks)src/main/java/com/example/solidconnection/mentor/repository/custom/MentorApplicationFilterRepositoryImpl.java(1 hunks)src/main/resources/db/migration/V39__add_approved_at_mentro_application.sql(1 hunks)src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java(1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-11-17T06:30:49.502Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java:10-23
Timestamp: 2025-11-17T06:30:49.502Z
Learning: MentorApplication 도메인에서 universityId는 null일 수 있으며, MentorApplicationRequest에서도 이 필드에 대한 NotNull validation을 추가하지 않아야 한다.
Applied to files:
src/main/java/com/example/solidconnection/admin/dto/MentorApplicationResponse.javasrc/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
📚 Learning: 2025-11-20T14:03:56.450Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.450Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.
Applied to files:
src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.javasrc/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
📚 Learning: 2025-09-30T09:16:14.993Z
Learnt from: Gyuhyeok99
Repo: solid-connection/solid-connect-server PR: 522
File: src/main/java/com/example/solidconnection/common/BaseEntity.java:39-41
Timestamp: 2025-09-30T09:16:14.993Z
Learning: BaseEntity와 같은 엔티티의 타임스탬프 필드는 테스트를 위해 public setter를 추가하지 말고, reflection이나 EntityManager를 활용한 테스트 전용 헬퍼를 사용해야 합니다. 프로덕션 코드의 캡슐화를 유지하는 것이 중요합니다.
Applied to files:
src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java
📚 Learning: 2025-08-11T14:39:49.462Z
Learnt from: Gyuhyeok99
Repo: solid-connection/solid-connect-server PR: 443
File: src/main/resources/db/migration/V28__alter_mentor_introduction_pass_tip_not_null.sql:1-5
Timestamp: 2025-08-11T14:39:49.462Z
Learning: The mentor table currently has no data in production/staging environment, so NULL backfill operations are not needed when adding NOT NULL constraints to the introduction and pass_tip columns in the mentor table.
Applied to files:
src/main/resources/db/migration/V39__add_approved_at_mentro_application.sql
🧬 Code graph analysis (2)
src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java (1)
src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java (1)
RequiredArgsConstructor(18-37)
src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java (1)
src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java (1)
RequiredArgsConstructor(12-25)
⏰ 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 (9)
src/main/java/com/example/solidconnection/admin/dto/MentorApplicationSearchResponse.java (1)
1-8: 깔끔한 복합 DTO 설계입니다!사용자 정보와 멘토 지원서 정보를 하나의 검색 결과로 묶는 구조가 명확하고 적절합니다.
src/main/java/com/example/solidconnection/mentor/repository/MentorApplicationRepository.java (1)
5-10: 커스텀 필터 리포지토리 확장이 올바르게 구현되었습니다!
MentorApplicationFilterRepository인터페이스를 확장하여 페이징 검색 기능을 추가한 구조가 Spring Data JPA의 표준 패턴을 잘 따르고 있습니다.src/main/java/com/example/solidconnection/admin/dto/MentorApplicationResponse.java (1)
1-18: 어드민 응답 DTO가 잘 설계되었습니다!멘토 지원서 정보를 표현하는 데 필요한 모든 필드가 포함되어 있고, 새로 추가된
approvedAt필드도 적절히 포함되어 있습니다.src/main/java/com/example/solidconnection/mentor/repository/custom/MentorApplicationFilterRepository.java (1)
1-12: 커스텀 리포지토리 인터페이스가 명확하게 정의되었습니다!페이징 검색을 위한 메서드 시그니처가 Spring Data의 컨벤션을 잘 따르고 있으며, 검색 조건과 페이징 파라미터를 적절히 수용하고 있습니다.
src/main/java/com/example/solidconnection/admin/service/AdminMentorApplicationService.java (1)
1-25: 서비스 레이어가 깔끔하게 구현되었습니다!다음과 같이 적절히 설계되었습니다:
- 읽기 전용 트랜잭션:
@Transactional(readOnly = true)애노테이션으로 조회 성능 최적화- 명확한 위임: 리포지토리로 직접 위임하는 구조로 불필요한 복잡도 없음
- 일관된 명명: 메서드명이 의도를 명확히 표현
src/main/java/com/example/solidconnection/admin/dto/MentorApplicationSearchCondition.java (1)
6-9: 타임존 변환이 올바르게 처리되고 있습니다.검증 결과,
MentorApplicationFilterRepositoryImpl의createdAtEq()메서드(132-143번 라인)에서 다음과 같이 안전하게 처리하고 있습니다:
LocalDate를 받아 일일 범위로 변환 (atStartOfDay()사용)- 시스템 타임존을 명시적으로 지정 (
.atZone(SYSTEM_ZONE_ID))ZonedDateTime타입의 엔티티 필드와between()비교 수행타임존 정보 손실에 대한 우려는 구현체에서 이미 올바르게 해결되어 있으므로, 추가 조치가 필요 없습니다.
src/main/java/com/example/solidconnection/mentor/domain/MentorApplication.java (1)
70-71: 멘토 지원서 승인 처리 기능이 완전히 구현되지 않았습니다.검증 결과,
approvedAt필드는 도메인 엔티티와 DTO에 정의되어 있지만 실제로 값을 설정하는 비즈니스 로직이 전혀 없습니다. 더 심각한 문제는 멘토 지원서를 승인(APPROVED) 또는 거절하는 기능 자체가 구현되지 않았다는 점입니다.다음 항목들이 필요합니다:
MentorApplication엔티티에 상태 변경 메서드 추가
approve()메서드: 상태를APPROVED로 변경하고approvedAt을 현재 시간으로 설정reject()메서드: 상태를REJECTED로 변경하고 거절 사유 저장
AdminMentorApplicationService에 승인/거절 비즈니스 로직 추가
- 멘토 지원서 조회 후 상태 변경 메서드 호출
- 거절 시 사유 저장
AdminMentorApplicationController에 승인/거절 엔드포인트 추가
- PUT 또는 POST 엔드포인트로 승인/거절 요청 처리
현재 controller와 service에는 검색(GET) 기능만 있고, approve/reject 엔드포인트가 전혀 없습니다.
⛔ Skipped due to learnings
Learnt from: sukangpunch Repo: solid-connection/solid-connect-server PR: 562 File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93 Timestamp: 2025-11-20T14:03:56.450Z Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.Learnt from: sukangpunch Repo: solid-connection/solid-connect-server PR: 562 File: src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java:10-23 Timestamp: 2025-11-17T06:30:49.502Z Learning: MentorApplication 도메인에서 universityId는 null일 수 있으며, MentorApplicationRequest에서도 이 필드에 대한 NotNull validation을 추가하지 않아야 한다.src/main/java/com/example/solidconnection/admin/controller/AdminMentorApplicationController.java (1)
18-36: 1) 관리자 멘토 지원서 페이징 조회 컨트롤러 구조가 자연스럽습니다
@Valid @ModelAttribute로 검색 조건 DTO를 바인딩하고,Pageable을 그대로 서비스에 넘기는 패턴이 기존 스프링 관용구와 잘 맞습니다.- 서비스에서 받은
Page를 공통PageResponse.of(page)로 감싸ResponseEntity.ok(...)로 반환하는 것도 응답 일관성 측면에서 좋아 보입니다.src/main/java/com/example/solidconnection/mentor/repository/custom/MentorApplicationFilterRepositoryImpl.java (1)
132-144: 3) createdAt 일자 필터는 의도대로 ‘하루 구간’으로 잘 잘리고 있습니다
LocalDate createdAt을 받아startOfDay~endOfDay(+1일 시작 - 1ns)로 구간을 만든 뒤,createdAt컬럼과between으로 비교하는 방식이라 “특정 날짜에 생성된 신청서만” 잘 필터링될 것으로 보입니다.- 애플리케이션과 DB가 같은 타임존(또는 UTC 기반 ZonedDateTime)으로 운용된다는 전제에서는, 관리자 화면에서 기대하는 날짜 검색 UX와도 자연스럽게 맞을 것 같습니다.
| ALTER TABLE mentor_application | ||
| ADD COLUMN approved_at DATETIME(6); | ||
|
|
||
| UPDATE mentor_application | ||
| SET approved_at = NOW() | ||
| WHERE mentor_application_status = 'APPROVED' | ||
| AND approved_at IS NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
마이그레이션 파일명에 오타가 있습니다.
파일명이 V39__add_approved_at_mentro_application.sql인데, mentro는 mentor의 오타로 보입니다.
다음과 같이 파일명을 수정해주세요:
-V39__add_approved_at_mentro_application.sql
+V39__add_approved_at_mentor_application.sqlCommittable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/resources/db/migration/V39__add_approved_at_mentro_application.sql
lines 1-7 the filename contains a typo ("mentro" → "mentor"); rename the file to
V39__add_approved_at_mentor_application.sql (use git mv to preserve history),
keep the file contents unchanged, and update any references or build/migration
config that refer to the old filename so the migration tool recognizes the
corrected migration.
| UPDATE mentor_application | ||
| SET approved_at = NOW() | ||
| WHERE mentor_application_status = 'APPROVED' | ||
| AND approved_at IS NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
기존 승인 데이터의 실제 승인 시점이 손실됩니다.
4-7번 라인의 UPDATE 구문이 모든 기존 APPROVED 레코드에 대해 NOW()를 사용하여 동일한 타임스탬프를 설정합니다. 이는 각 멘토 지원서가 실제로 승인된 시점 정보를 잃게 되어 히스토리 추적이 불가능해집니다.
더 나은 접근 방법:
- 선호하는 방식:
created_at을 대체 값으로 사용 - 승인 시점의 근사치로 활용 - 대안: 기존 데이터는 NULL로 두고, 향후 승인부터만 정확한 시점 기록
방식 1을 적용한 diff:
UPDATE mentor_application
-SET approved_at = NOW()
+SET approved_at = created_at
WHERE mentor_application_status = 'APPROVED'
AND approved_at IS NULL;참고: Line 7의 AND approved_at IS NULL 체크는 방금 추가된 컬럼이므로 모든 값이 NULL이어서 실제로는 불필요합니다만, 재실행 방어를 위해 유지하는 것도 합리적입니다.
🤖 Prompt for AI Agents
In src/main/resources/db/migration/V39__add_approved_at_mentro_application.sql
around lines 4-7, the UPDATE uses NOW() which overwrites historical approval
times; instead set approved_at to the row's created_at as an approximate
approval timestamp: run UPDATE mentor_application SET approved_at = created_at
WHERE mentor_application_status = 'APPROVED' AND approved_at IS NULL; keep the
approved_at IS NULL guard for idempotency (or, if preferred, leave existing rows
NULL and only populate future approvals).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai 현재 서비스에서 승인 된 멘토 지원서는 존재할 수 없다는 가정이면?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sukangpunch 좋은 지적입니다!
만약 현재 프로덕션/스테이징 환경에 APPROVED 상태의 멘토 지원서가 존재하지 않는다면, 제가 제기한 우려는 해당되지 않습니다. UPDATE 구문이 0개의 행에 영향을 미치므로 데이터 손실 문제가 발생하지 않습니다.
해당 가정이 맞다면 현재 마이그레이션 스크립트는 안전합니다. 다만 확실성을 위해:
1. 이 가정을 확인해주실 수 있나요?
- 현재 운영/스테이징 DB에
mentor_application_status = 'APPROVED'인 레코드가 0건인지 확인
2. (선택사항) 명확성을 위해 주석 추가를 고려해보시겠어요?
-- Note: As of this migration, no APPROVED mentor applications exist in production/staging
-- This UPDATE serves as a defensive measure for any edge cases
UPDATE mentor_application
SET approved_at = NOW()
WHERE mentor_application_status = 'APPROVED'
AND approved_at IS NULL;가정이 맞다면 현재 코드 그대로 진행하셔도 괜찮습니다! 👍
🧠 Learnings used
Learnt from: Gyuhyeok99
Repo: solid-connection/solid-connect-server PR: 443
File: src/main/resources/db/migration/V28__alter_mentor_introduction_pass_tip_not_null.sql:1-5
Timestamp: 2025-08-11T14:39:49.462Z
Learning: The mentor table currently has no data in production/staging environment, so NULL backfill operations are not needed when adding NOT NULL constraints to the introduction and pass_tip columns in the mentor table.
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.450Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/main/resources/db/migration/V39__add_approved_at_mentor_application.sql(1 hunks)src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-11T14:39:49.462Z
Learnt from: Gyuhyeok99
Repo: solid-connection/solid-connect-server PR: 443
File: src/main/resources/db/migration/V28__alter_mentor_introduction_pass_tip_not_null.sql:1-5
Timestamp: 2025-08-11T14:39:49.462Z
Learning: The mentor table currently has no data in production/staging environment, so NULL backfill operations are not needed when adding NOT NULL constraints to the introduction and pass_tip columns in the mentor table.
Applied to files:
src/main/resources/db/migration/V39__add_approved_at_mentor_application.sql
📚 Learning: 2025-11-20T14:03:56.450Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.450Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.
Applied to files:
src/main/resources/db/migration/V39__add_approved_at_mentor_application.sqlsrc/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
📚 Learning: 2025-11-17T06:30:49.502Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java:10-23
Timestamp: 2025-11-17T06:30:49.502Z
Learning: MentorApplication 도메인에서 universityId는 null일 수 있으며, MentorApplicationRequest에서도 이 필드에 대한 NotNull validation을 추가하지 않아야 한다.
Applied to files:
src/main/resources/db/migration/V39__add_approved_at_mentor_application.sqlsrc/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.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/resources/db/migration/V39__add_approved_at_mentor_application.sql (2)
1-2: 승인된 멘토 지원서의 승인 시점 추적을 위한 컬럼 추가, LGTM!
DATETIME(6)타입은 microsecond 정밀도를 제공하여 Java의ZonedDateTime과 잘 매칭됩니다.
4-7: 기존 논의 사항이 해결되었으므로 승인합니다.과거 리뷰에서 제기된 히스토리 손실 우려는 프로덕션/스테이징에
APPROVED상태의 데이터가 없다는 확인을 통해 해결되었습니다.AND approved_at IS NULL조건은 재실행 방어를 위한 좋은 관행입니다.src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (1)
72-92: 나머지 테스트 메서드들은 잘 작성되었습니다!
- 상태별 필터링 테스트는
PENDING상태를 올바르게 검증합니다.- 닉네임, 대학명, 지역명 키워드 검색 테스트들이 적절한 예상 결과와 함께 구성되어 있습니다.
- 복합 조건 테스트도 모든 필터가 함께 작동하는지 확인합니다.
- 과거 리뷰에서 지적된
response.get()이슈가response.getContent()로 올바르게 수정되었습니다.Also applies to: 94-115, 117-138, 140-161, 186-210
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
Show resolved
Hide resolved
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
Show resolved
Hide resolved
* fix: flyway V39 스크립트 파일명 수정 * test: 테스트 코드 오류 수정, 검증 추가
a9dd644 to
9d8bedd
Compare
There was a problem hiding this 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
♻️ Duplicate comments (1)
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (1)
27-29: DisplayName이 여전히 테스트 내용과 불일치합니다.이전 리뷰에서 지적되었지만 아직 수정되지 않았습니다. 클래스는 멘토 지원서 관리 서비스를 테스트하는데, DisplayName은 "어학 검증 관리자 서비스 테스트"로 되어 있습니다.
다음과 같이 수정하세요:
-@DisplayName("어학 검증 관리자 서비스 테스트") +@DisplayName("어드민 멘토 지원서 관리 서비스 테스트")
🧹 Nitpick comments (1)
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (1)
69-211: 추가 테스트 케이스 고려를 제안합니다.현재 테스트는 주요 기능을 잘 검증하고 있지만, 다음과 같은 추가 테스트를 고려하면 더욱 견고해질 수 있습니다:
엣지 케이스 테스트
- 검색 결과가 없는 경우 (존재하지 않는 키워드 검색)
- 빈 문자열이나 null 키워드로 검색하는 경우
- 페이지 크기가 0이거나 음수인 경우
복합 검색 테스트
- 키워드가 여러 컬럼(닉네임, 대학명, 지역명, 국가명)에 동시에 매칭되는 케이스
- 키워드가 한 엔티티의 여러 필드에 걸쳐 매칭되는 케이스
정렬/순서 검증
- 조회 결과의 정렬 순서가 의도한 대로인지 검증 (예: 생성일 기준)
페이징 경계 테스트
- 마지막 페이지 조회
- 존재하지 않는 페이지 번호 조회
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/main/resources/db/migration/V39__add_approved_at_mentor_application.sql(1 hunks)src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/main/resources/db/migration/V39__add_approved_at_mentor_application.sql
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.450Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.
📚 Learning: 2025-11-20T14:03:56.450Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.450Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.
Applied to files:
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
📚 Learning: 2025-11-17T06:30:49.502Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java:10-23
Timestamp: 2025-11-17T06:30:49.502Z
Learning: MentorApplication 도메인에서 universityId는 null일 수 있으며, MentorApplicationRequest에서도 이 필드에 대한 NotNull validation을 추가하지 않아야 한다.
Applied to files:
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.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 (7)
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (7)
50-67: 테스트 픽스처 구성이 적절합니다.다양한 상태(APPROVED, PENDING, REJECTED)와 여러 대학을 조합하여 검색 시나리오를 충분히 커버할 수 있는 테스트 데이터가 잘 준비되었습니다.
72-92: LGTM! 상태 필터링 테스트가 올바릅니다.PENDING 상태로 필터링하여 예상된 결과를 정확하게 검증하고 있습니다.
94-115: LGTM! 닉네임 필터링 테스트가 올바릅니다.특정 닉네임으로 검색하여 해당하는 지원서만 조회되는지 정확하게 검증하고 있습니다.
117-138: LGTM! 대학명 필터링 테스트가 올바릅니다.대학명으로 검색하여 해당 대학의 지원서들만 조회되는지 정확하게 검증하고 있습니다.
140-161: LGTM! 지역명 필터링 테스트가 올바릅니다.지역명으로 검색하여 해당 지역의 지원서들만 조회되는지 정확하게 검증하고 있습니다.
163-184: LGTM! 나라명 필터링 테스트가 올바릅니다.국가명으로 검색하여 해당 국가의 지원서들만 조회되는지 정확하게 검증하고 있습니다. 그라츠 대학은 오스트리아에 위치하므로 예상 결과가 올바릅니다.
186-210: 추가 정보가 필요합니다. 다음 사항을 확인하겠습니다:테스트에서
LocalDate.now()사용 시 시간대 경계 문제 검토 필요합니다.Line 190에서
LocalDate.now()를 생성일 필터 기준으로 사용하고 있는데, 다음 사항들을 확인하세요:
fixture 생성 시점: MentorApplicationFixture에서 명시적인 생성 날짜 설정이 없어 JPA 자동 타임스탬프(LocalDateTime)에 의존합니다.
타입 불일치 위험: LocalDate를 LocalDateTime 필드와 비교할 때는 between 쿼리(atStartOfDay() ~ plusDays(1).atStartOfDay())를 사용해야 합니다. searchMentorApplications 메서드가 이를 적절히 처리하는지 확인하세요.
간헐적 실패 가능성: 테스트와 fixture 생성 시점 간격에 따라 테스트가 실패할 수 있습니다(예: fixture 생성 23:59, 테스트 실행 다음날 00:01).
제안사항:
LocalDate.now()대신 테스트 메서드 내에서 생성할 때 사용한 구체적인 날짜 사용- 또는
searchMentorApplications구현이 날짜 비교를 올바르게 처리하는지 확인
9d8bedd to
06a0492
Compare
There was a problem hiding this 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 (2)
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (2)
75-75: 코드 컨벤션: 쉼표 뒤 공백 추가를 권장합니다.
null,다음에 공백이 누락되었습니다. 일관된 코드 스타일을 위해 추가를 고려해보세요.- MentorApplicationSearchCondition condition = new MentorApplicationSearchCondition(MentorApplicationStatus.PENDING,null, null); + MentorApplicationSearchCondition condition = new MentorApplicationSearchCondition(MentorApplicationStatus.PENDING, null, null);
72-210: 선택사항: 반복되는 검증 패턴을 헬퍼 메서드로 추출할 수 있습니다.
- 6개 테스트 모두 동일한 assertion 패턴을 사용하고 있습니다.
- 코드 중복을 줄이고 유지보수성을 높이기 위해 헬퍼 메서드 추출을 고려해보세요.
예시:
private void assertMentorApplicationIds( Page<MentorApplicationSearchResponse> response, List<MentorApplication> expected ) { assertThat(response.getContent()).hasSize(expected.size()); assertThat(response.getContent()) .extracting(content -> content.mentorApplicationResponse().id()) .containsOnly(expected.stream() .map(MentorApplication::getId) .toArray(Long[]::new)); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.450Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.
📚 Learning: 2025-11-20T14:03:56.450Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/service/MentorMyPageService.java:76-93
Timestamp: 2025-11-20T14:03:56.450Z
Learning: MentorApplication의 universityId는 PENDING 상태에서는 null일 수 있지만, admin이 승인(APPROVED)할 때 반드시 대학 데이터를 생성하고 universityId를 채운 후 승인하므로, APPROVED 상태의 MentorApplication은 항상 non-null universityId를 가진다는 것이 비즈니스 규칙이다.
Applied to files:
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
📚 Learning: 2025-11-17T06:30:49.502Z
Learnt from: sukangpunch
Repo: solid-connection/solid-connect-server PR: 562
File: src/main/java/com/example/solidconnection/mentor/dto/MentorApplicationRequest.java:10-23
Timestamp: 2025-11-17T06:30:49.502Z
Learning: MentorApplication 도메인에서 universityId는 null일 수 있으며, MentorApplicationRequest에서도 이 필드에 대한 NotNull validation을 추가하지 않아야 한다.
Applied to files:
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.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 (5)
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java (5)
27-67: 테스트 설정이 잘 구성되어 있습니다.
- 통합 테스트 환경이 적절하게 설정되었습니다.
- 다양한 상태(승인, 대기, 거절)와 대학 조합으로 테스트 데이터를 준비했습니다.
- 검색 시나리오를 충분히 커버할 수 있는 픽스처가 마련되었습니다.
94-115: 닉네임 검색 테스트가 올바르게 구현되었습니다.
- 검색 조건과 예상 결과가 일치합니다.
- ID와 닉네임을 모두 검증하고 있습니다.
117-138: 대학명 검색 테스트가 올바르게 작성되었습니다.
- 검색 키워드와 픽스처 데이터가 일치합니다.
- 대학명 필드 검증이 정확합니다.
140-161: 지역명 검색 테스트가 정확합니다.
- 유럽 지역 검색이 올바른 대학(그라츠)을 반환합니다.
- 검색 결과 검증이 적절합니다.
163-184: 나라명 검색 테스트가 올바르게 수정되었습니다.
- 오스트리아 검색이 정확한 데이터(그라츠 대학)를 반환합니다.
- 이전 리뷰에서 지적된 데이터 불일치가 해결되었습니다.
| @Test | ||
| void 모든_조건으로_페이징하여_조회한다() { | ||
| // given | ||
| String regionKoreanName = "영미권"; | ||
| MentorApplicationSearchCondition condition = new MentorApplicationSearchCondition(MentorApplicationStatus.PENDING, regionKoreanName, LocalDate.now()); | ||
| Pageable pageable = PageRequest.of(0, 10); | ||
| List<MentorApplication> expectedMentorApplications = List.of(mentorApplication2); | ||
|
|
||
| // when | ||
| Page<MentorApplicationSearchResponse> response = adminMentorApplicationService.searchMentorApplications(condition, pageable); | ||
|
|
||
| // then | ||
| assertThat(response.getContent()).hasSize(expectedMentorApplications.size()); | ||
| assertThat(response.getContent()) | ||
| .extracting(content -> content.mentorApplicationResponse().id()) | ||
| .containsOnly(expectedMentorApplications.stream() | ||
| .map(MentorApplication::getId) | ||
| .toArray(Long[]::new)); | ||
| assertThat(response.getContent()) | ||
| .extracting(content -> content.mentorApplicationResponse().mentorApplicationStatus()) | ||
| .containsOnly(MentorApplicationStatus.PENDING); | ||
| assertThat(response.getContent()) | ||
| .extracting(content -> content.mentorApplicationResponse().region()) | ||
| .containsOnly(regionKoreanName); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
시간 의존적인 테스트 코드를 개선해주세요.
- Line 190에서
LocalDate.now()를 사용하여 생성일자를 필터링하고 있습니다. - 이 테스트는 픽스처가 오늘 날짜로 데이터를 생성한다고 가정하고 있어 취약합니다.
- CI/CD 환경이나 다른 시간대에서 실행 시 간헐적으로 실패할 수 있습니다.
다음 방법 중 하나를 권장합니다:
- 방법 1: 픽스처에서 명시적인 날짜를 설정하고 테스트에서도 동일한 날짜 사용
- 방법 2: 날짜를 모킹하거나 테스트용 고정 날짜 사용
- 방법 3: createdAt 필터를 제외하고 status + keyword만으로 조합 테스트 수행
픽스처의 생성일자 동작을 확인하기 위해 다음 스크립트를 실행해주세요:
🏁 Script executed:
#!/bin/bash
# Description: Verify how MentorApplicationFixture sets creation timestamps
# Check fixture implementation for date/time handling
ast-grep --pattern $'class MentorApplicationFixture {
$$$
}'
rg -A 10 "대기중_멘토신청|승인된_멘토신청|거절된_멘토신청" --type java -g "**/MentorApplicationFixture.java"
# Check if MentorApplication entity has any default timestamp behavior
rg -A 5 "@CreatedDate|@CreationTimestamp|createdAt" --type java -g "**/MentorApplication.java"Length of output: 4236
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Get full MentorApplicationFixtureBuilder to see if createdAt is set by default
rg -A 50 "class MentorApplicationFixtureBuilder" --type java
# Check the test setup - how is mentorApplication2 created?
rg -B 20 "모든_조건으로_페이징하여_조회한다" src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java | head -40
# Check MentorApplication entity for createdAt field definition
rg "class MentorApplication|private.*createdAt|LocalDate.*createdAt" --type java -g "**/MentorApplication.java" -A 3Length of output: 8172
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Find BaseEntity to see if @CreatedDate is there
rg "class BaseEntity|@CreatedDate|@CreationTimestamp" --type java -g "**/BaseEntity.java" -A 5
# Find test setup - @BeforeEach or initialization of mentorApplication2
rg -B 5 "mentorApplication2 =" src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
# Check the full test class setup area (first 100 lines)
rg "private MentorApplication|@BeforeEach" src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java -A 10 | head -60Length of output: 2009
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Find MentorApplicationSearchCondition to see what type it expects
rg "class MentorApplicationSearchCondition|private.*createdAt|public.*createdAt" --type java -A 3
# Find the search query implementation to see how it filters by date
rg "searchMentorApplications|createdAt" --type java -B 3 -A 5 -g "**/AdminMentorApplicationService*.java"Length of output: 12080
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Find the full implementation of createdAtEq method in MentorApplicationFilterRepositoryImpl
rg -A 10 "private BooleanExpression createdAtEq\(LocalDate" src/main/java/com/example/solidconnection/mentor/repository/custom/MentorApplicationFilterRepositoryImpl.java
# Also check for any timezone or date conversion logic
rg "ZonedDateTime|LocalDate|createdAt" src/main/java/com/example/solidconnection/mentor/repository/custom/MentorApplicationFilterRepositoryImpl.java -A 2 -B 2Length of output: 1881
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Check what SYSTEM_ZONE_ID is set to
rg "SYSTEM_ZONE_ID" src/main/java/com/example/solidconnection/mentor/repository/custom/MentorApplicationFilterRepositoryImpl.java -B 5 -A 2
# Also check the full test context around line 186-210
rg -B 50 "모든_조건으로_페이징하여_조회한다" src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java | grep -A 50 "@BeforeEach"Length of output: 651
테스트의 시간 의존성을 개선할 수 있습니다.
현재 코드를 검토한 결과, createdAtEq 메서드에서 LocalDate를 전체 하루 범위로 정확히 변환하고 있어 대부분의 경우 제대로 작동합니다. 다만 다음과 같은 점을 개선하면 더욱 견고한 테스트가 될 것입니다.
-
현재의 잠재적 위험성
- 테스트 설정과 실행 사이에 자정을 기준으로 날짜가 변경될 가능성 (매우 드물지만 존재)
- 시스템 타임존에 의존하여 다양한 환경에서 예측 불가능할 수 있음
-
개선 방법
- 픽스처와 테스트에서 동일한 명시적 날짜를 사용하는 것을 권장합니다.
- 현재:
LocalDate.now() - 개선:
LocalDate.of(2024, 11, 23)같이 고정된 날짜 사용
이렇게 하면 테스트 실행 시점에 관계없이 일관되게 동작합니다.
🤖 Prompt for AI Agents
In
src/test/java/com/example/solidconnection/admin/service/AdminMentorApplicationServiceTest.java
around lines 186 to 210, replace the time-dependent LocalDate.now() used to
build the MentorApplicationSearchCondition with a fixed explicit date (e.g.
LocalDate.of(2024, 11, 23)) and ensure any related test fixtures that rely on
the same date use the identical fixed date so the createdAtEq range and
assertions remain stable across runs and timezones.
JAEHEE25
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다!
| return query.where( | ||
| verifyMentorStatusEq(condition.mentorApplicationStatus()), | ||
| keywordContains(condition.keyword()), | ||
| createdAtEq(condition.createdAt()) | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
함수로 나눠져 있어서 깔끔하네요!
조건이 더 복잡해지면 BooleanBuilder를 사용하는 것도 좋을 것 같습니다
| return siteUser.nickname.containsIgnoreCase(keyword) | ||
| .or(university.koreanName.containsIgnoreCase(keyword)) | ||
| .or(region.koreanName.containsIgnoreCase(keyword)) | ||
| .or(country.koreanName.containsIgnoreCase(keyword)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
개인적인 생각으로는 일단 컬럼별 우선순위 없이 현재 방식을 유지해도 될 것 같습니다!
* fix: yml 들여쓰기 수정 (#555) * fix: yml 들여쓰기 수정 * fix: jdk 변경 * refactor: 멘토 및 채팅 관련 API 응답 수정 (#537) * refactor: 멘토의 멘토링 조회 응답에서 mentoringId가 아니라 roomId를 포함하도록 * refactor: 파트너가 멘토인 경우 partnerId는 mentorId로 - AS IS: 멘토/멘티 모두 partnerId가 siteUserId - TO BE: 멘티: siteUserId, 멘토: mentorId * refactor: 응답의 senderId가 mentorId/siteUserId가 되도록 * refactor: senderId에 해당하는 chatParticipant가 없을 경우 예외 처리하는 로직 추가 * refactor: 메서드명에 맞게 시그니처 변경 * refactor: getChatMessages 메서드에서 응답으로 siteUserId를 넘겨주도록 - AS IS: mentorId(mentor) / siteUserId(mentee) - TO BE: siteUserId(all) * refactor: 헬퍼 메서드로 메서드 복잡성을 분산한다 * refactor: getChatPartner 메서드의 응답으로 siteUserId를 넘겨주도록 - AS IS: mentorId(mentor) / siteUserId(mentee) - TO BE: siteUserId(all) * refactor: CD 성능 개선 (#552) * fix: deprecated된 base image를 eclipse-temurin:17-jdk로 변경 * refactor: scp 파일 전송하는 방식에서 GHCR로 push/pull하도록 변경 * fix: GHCR image 제거시 Org의 GITHUB_TOKEN 사용하도록 변경 * refactor : scp 파일 전송하는 방식에서 GHCR로 push/pull하도록 prod-cd.yml과 docker-compose.prod.yml 변경 * fix: prod 인스턴스 old image 이름 통일 * fix: prod-cd.yml StrictHostKeyChecking 옵션 문법 오류 수정 * fix: prod-cd.yml StrictHostKeyChecking 옵션 문법 오류 수정 * fix: dev-cd.yml Old images 정리 작업 중 이미지 이름 불일치 문제 해결 * chore: 마지막 줄 개행 추가 * chore: 마지막 줄 개행 추가 * feat: stage 인스턴스에 대한 최신 이미지 5개 유지 기능 및 old 이미지 제거 기능 추가 * chore: 중복된 환경변수 지정 제거 * chore: 중복된 pem키 생성 로직 제거 * fix: 잘못된 pem키 이름 수정 * refactor: 원격 호스트에서 pull할 경우, 최소 권한으로 실행하도록 Github App으로 임시토큰 발급하도록 수정 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 (#565) * fix: GitHub app token permission 문제 해결 (#566) * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정 * fix: Github App이 발행한 임시 토큰에 대해 Contents 읽기 권한 추가 * fix: GitHub app token permission 문제 오류 해결 (#567) * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정 * fix: Github App이 발행한 임시 토큰에 대해 Contents 읽기 권한 추가 * fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가 * fix: GitHub app token permission이 repo 레벨에서 부여되는 문제 해결 (#568) * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정 * fix: Github App이 발행한 임시 토큰에 대해 Contents 읽기 권한 추가 * fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가 * fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가 * fix: GitHub app token permission 권한 오류 해결 (#569) * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정 * fix: Github App이 발행한 임시 토큰에 대해 Contents 읽기 권한 추가 * fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가 * fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가 * fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가 * test: fork repo의 작업 branch에서 해당 workflows가 실행되도록 임시 수정 * refactor: test용 설정 제거 * feat: claude.md 파일 추가 (#560) * fix : 동일 멘토 멘티 중복 신청 불가능하도록 수정 (#563) * fix : 동일 멘토 멘티 중복 신청 불가능하도록 수정 - UK 제약조건 추가 - flyway script 추가 - CustomException 추가 - Service 로직 수정 - Test code 추가 * fix : column명 오류 수정 - column명 camelCase -> snake_case로 변경 * fix : column명 오류 수정 - column명 name으로 명시 * fix: GitHub app token permission 권한 오류 해결 (#570) * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정 * fix: Github App이 발행한 임시 토큰에 대해 읽기 권한 명시 문법 오류 수정 * fix: Github App이 발행한 임시 토큰에 대해 Contents 읽기 권한 추가 * fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가 * fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가 * fix: Github App이 발행한 임시 토큰에 대해 조직 레벨에서 읽기 권한 추가 * test: fork repo의 작업 branch에서 해당 workflows가 실행되도록 임시 수정 * refactor: test용 설정 제거 * fix: docker login username 불일치 문제 * refactor: 최소권한 원칙 적용을 위한 Action Job 분리 * refactor: 필요없는 주석 제거 * fix: GHCR 정리 권한 PAT로 해결 (#573) * feat: 지원서가 APPROVED 인 유저의 멘토 생성 기능 추가 (#562) * feat: 지원서가 APPROVED 인 유저의 멘토 생성 기능 추가 * refactor: submitMentorApplication 메서드의 멘토 지원 유효 검증 부분을 메서드로 추출 * refactor: MentorMyPage 생성, 수정 부분의 channel 생성, 업데이트 부분 중복 제거 * test: Mentor 생성 관련 테스트 추가 * fix: 코드래빗 리뷰 적용 * refactor: 멘토 생성 시 channelRequest가 null 일 떄 예외 처리 * feat: MentorApplicationRequest 필드에 유효성 어노테이션 추가 * test: 채널 검색 시 siteUserId로 조회하는 문제 해결 * fix: 리뷰 수정사항 적용 * fix: 파일 끝에 개행 추가 * refactor: 멘토 생성 메서드에서 siteUser의 검증 제외 * refactor: dto 단에서 채널 리스트 null 검증 * feat: MentorApplication에 termId 추가 flyway 스크립트 추가 * fix: flyway 버전 충돌 해결 * feat: 어드민 멘토 승격 요청 페이징 조회 기능 추가 (#576) * feat: 어드민 멘토 지원서 페이징 조회 기능 추가 * feat: mentor/repository 패키지에 custom 패키지 추가 - custom 패키지에 페이징 조회를 책임지는 MentorApplicationFilterRepository 추가 - MentorApplicationSearchCondition 에서 넘긴 keyword 기반으로 닉네임, 권역, 나라, 학교명으로 필터링 검색 기능 추가 - MentorApplicationSearchCondition 에서 넘긴 mentorApplicationStatus 기반으로 승인, 거절, 진행중 으로 필터링 기능 추가 * test: 어드민 멘토 지원서 페이징 조회 테스트 추가 * feat: MentorApplication 엔티티에 approved_at 필드 추가 flyway 스크립트 작성 * fix: 파일 끝에 개행 추가 * refactor: 페이징 조회 시 count 쿼리에 불필요한 조인 막기 * fix: 코드래빗 리뷰 적용 * fix: flyway V39 스크립트 파일명 수정 * test: 테스트 코드 오류 수정, 검증 추가 * test: 기대하는 값이랑 다른 테스트 응답을 수정합니다 * feat: 어드민 멘토 승격 지원서 승인/거절 기능, 상태 별 지원서 개수 조회 기능 추가 (#577) * feat: 어드민 멘토 승격 지원서 승인/거절 기능 추가 * test: 어드민 멘토 지원서 승인/거절 테스트 추가 * feat: 멘토 지원서 상태별 개수 조회 기능 추가 * test: 멘토 지원서 상태별 개수 조회 테스트 추가 * fix: 대학이 선택되지 않은 멘토 지원서 승인 시 예외 발생하도록 수정 * refactor: 리뷰 내용 반영 * refactor: MENTOR_APPLICATION_ALREADY_CONFIRM -> MENTOR_APPLICATION_ALREADY_CONFIRMED 로 수정 * refactor: 멘토 지원서 거절 사유 관련하여 기획에 명시되지 않은 길이 제한 제거 * refactor: 리뷰 적용 * refactor: 변수명, 필드명 일관성 맞추기 * test: assertAll 적용 * feat: region 관련 관리 기능 추가 (#561) * feat: 지역 생성 기능 구현 (AdminRegion) 지역을 생성하는 기능을 구현했습니다: - AdminRegionCreateRequest: 지역 생성 요청 DTO - AdminRegionResponse: 지역 응답 DTO - AdminRegionService.createRegion(): 중복 검사를 포함한 지역 생성 로직 - AdminRegionController.createRegion(): HTTP POST 엔드포인트 중복 검사: - 지역 코드 중복 확인 - 한글명 중복 확인 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * feat: 지역 수정/삭제/조회 기능 구현 및 테스트 추가 (AdminRegion) 지역 관리 기능을 완성했습니다: 구현 기능: - AdminRegionUpdateRequest: 지역 수정 요청 DTO - AdminRegionService.updateRegion(): 한글명 중복 검사를 포함한 지역 수정 - AdminRegionService.deleteRegion(): 지역 삭제 - AdminRegionService.getAllRegions(): 전체 지역 조회 - AdminRegionController: 수정/삭제/조회 HTTP 엔드포인트 테스트 코드 (AdminRegionServiceTest): - CREATE: 정상 생성, 코드 중복, 한글명 중복 테스트 - UPDATE: 정상 수정, NOT_FOUND, 중복 한글명, 동일 한글명 테스트 - DELETE: 정상 삭제, NOT_FOUND 테스트 - READ: 빈 목록, 전체 조회 테스트 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix: 지역 관리 관련 에러코드 추가 (ErrorCode) 지역 관리 기능에 필요한 에러코드를 추가했습니다: - REGION_NOT_FOUND: 존재하지 않는 지역 - REGION_ALREADY_EXISTS: 이미 존재하는 지역 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix: jpa 오류 수정 * refactor: 코드리뷰 반영 --------- Co-authored-by: Claude <[email protected]> * fix: config.alloy 파일 경로 불일치 문제 해결 (#586) * fix: config.alloy 파일 경로 불일치 문제 해결 * refactor: docker-compose를 수정하는게 아닌 cd.yml의 경로를 수정하여 해결 * feat: 소셜 로그인 사용자는 비밀번호 변경을 할 수 없다. (#582) * feat: 소셜 로그인 사용자는 비밀번호 변경을 할 수 없도록 * test: 소셜 로그인 사용자 비밀번호 변경 관련 테스트 코드 작성 * chore: 컨벤션에 맞게 메서드명 변경 - ~~ 예외가 발생한다 * chore: 충돌 해결 * fix: Upgrade 헤더 유무에 따라 Connection 헤더의 값을 동적으로 설정하도록 (#581) * fix: Upgrade 헤더 유무에 따라 Connection 헤더의 값을 동적으로 설정하도록 - Upgrade 헤더가 존재하면(e.g. WebSocket) upgrade로 설정 - Upgrade 헤더가 존재하지 않으면 keep-alive로 설정 * chore: 서브모듈 업데이트 * feat: 멘토 지원서 대학교 매핑 기능, 대학 선택 상태 페이징 조건 추가 (#583) * feat: 멘토 지원서 검색 조건에 UniversitySelectType 추가 * feat: 어드민 멘토 지원서 페이징 조회 응답에 UniversitySelectType 추가 * test: 멘토 지원서 조회 테스트 추가 - test: UniversitySelectType 기반 페이징 조회 테스트 추가 * feat: 멘토 지원서에 대학 매핑 기능 추가 * test: 멘토 지원서 대학 매핑 테스트 추가 * refactor: 의미 없는 import 제거 * refactor: 리뷰 내용 반영 * refactor: 개행 및 공백 추가 * refactor: pathVariable 네이밍을 kebab-case 로 통일 * refactor: Service 레이어의 검증 로직을 도메인으로 이동 * refactor: PENDING 상태 및 OTHER 타입 검증을 도메인 메서드로 관리 * refactor: assignUniversity() 호출 전 검증 책임을 도메인 엔티티에 위임 * test : assertAll 로 검증 그룹화 * refactor: 스프링 부트 앱 외의 사이드 인프라 배포 과정을 분리 (#592) * refactor: dev 환경에서의 side-infra 배포 과정 분리 * refactor: prod 환경에서의 side-infra 배포 과정 분리 * refactor: docker-compose 가 실행되고 있지 않아도 스크립트가 실패하지 않게 변경 * fix: docker compose up 시에 사용할 환경변수 중 누락된 변수를 추가 * fix: S3 이름 불일치 문제 해결 (#594) * fix: s3 이름 불일치 문제 해결 * fix: s3와의 연동된 cloudfront URL로 수정 * refactor: 분리한 사이드 인프라에 대해서 필요없는 파일 정리 (#596) * test: flyway 스크립트를 검증하는 테스트 코드 작성 (#588) * test: flyway 스크립트를 검증하는 테스트 코드 작성 * fix: DirtiesContext 어노테이션을 통해 기존 컨텍스트를 폐기하도록 - 새로운 MySQL 환경에서 마이그레이션이 이루어지도록 수정 * fix: flyway 검증용의 별도의 MySQL 컨테이너를 사용하도록 * chore: 테스트 의도를 쉽게 이해할 수 있도록 주석 추가 * chore: 명시적으로 컨테이너를 시작하도록 - 또한 MySQLTestContainer 코드와 유사한 컨벤션을 가지도록 수정 * refactor: 게시글 조회 응답에 댓글 deprecated 여부 포함하도록 (#599) * feat: 유저의 멘토 지원서 신청 이력 조회 기능 추가 (#603) * feat: 유저의 멘토 지원 이력 조회 기능 추가 * refactor: 매개변수 타입 통일 * refactor: long 타입을 Long 으로 수정 * test: 멘토 지원서 이력 조회 테스트 추가 * test: MentorApplicationFixtureBuilder 에 rejectedReason 필드 및 빌더 메서드 추가 * refactor: 리뷰 사항 적용 * test: 멘토 지원서 이력 조회 에서 user와 university 재사용 * refactor: 긴 uri 를 짧게 수정 * refactor: 서브모듈 해시값 되돌리기 * refactor: 개행 지우기 * refactor: applicationOrder 자료형을 long 으로 수정 * fix: applicationOrder 를 int 자료형으로 처리하도록 복구 - 순서를 나타내고, 해당 값이 21억을 넘길 수 없다 판단하여 더 적합한 int 자료형으로 복구 * test: long type 을 기대하던 테스트 에러 해결 * fix: 탈퇴한 사용자가 물리적 삭제가 되지 않았던 문제를 해결한다 (#574) * refactor: FK에 ON DELETE CASCADE 옵션 추가 * refactor: 삭제 메서드로 사용자 연관 데이터를 삭제하도록 * feat: 어드민 유저 차단 기능 추가 (#604) * feat: 어드민 차단 기능 * test: 어드민 차단 기능 * feat: API 성능 로깅, 쿼리 별 메트릭 전송 추가 (#602) * feat: HTTP 요청/응답 로깅 필터 구현 - traceId 기반 요청 추적 - 요청/응답 로깅 - CustomExceptionHandler와 중복 로깅 방지 - Actuator 엔드포인트 로깅 제외 * feat: ExceptionHandler에 중복 로깅 방지 플래그 및 userId 로깅 추가 * feat: API 수행시간 로깅 인터셉터 추가 * feat: ApiPerf 인터셉터, Logging 필터 빈 등록 * refactor: logback 설정 변경 - info, warn, error, api_perf 로 로그 파일 분리해서 관리 * feat: 쿼리 별 수행시간 메트릭 모니터링 추가 * feat: 데이터소스 프록시 의존성 및 config 파일 추가 * feat: 데이터 소스 프록시가 metric을 찍을 수 있도록 listener 클래스 추가 * feat: 요청 시 method, uri 정보를 listener에서 활용하기 위해 RequestContext 및 관련 interceptor 추가 * refactor: 비효율적인 Time 빌더 생성 개선 - Time.builder 를 사용하면 매번 빌더를 생성하여 비효율적인 문제를 meterRegistry.timer 방식으로 해결 * feat: 로깅을 위해 HttpServeletRequest 속성에 userId 추가 * refactor: logback 설정 중 local은 console만 찍도록 수정 * refactor: FILE_PATTERN -> LOG_PATTERN 으로 수정 * test: TokenAuthenticationFilter에서 request에 userId 설정 검증 추가 - principal 조회 예외를 막기 위해 siteUserDetailsService given 추가 * refacotr: 코드 래빗 리뷰사항 반영 * test: 중복되는 테스트 제거 * refactor: 사용하지 않는 필드 제거 * refactor: 리뷰 내용 반영 * refactor: ApiPerformanceInterceptor에서 uri 정규화 관련 코드 제거 * refactor: ApiPerformanceInterceptor에서 if-return 문을 if-else 문으로 수정 * refactor: 추가한 interceptor 의 설정에 actuator 경로 무시하도록 셋팅 * refactor: 중복되는 의존성 제거 * refactor: 로깅 시 민감한 쿼리 파라미터 마스킹 - EXCLUDE_QUERIES 에 해당하는 쿼리 파라미터 KEY 값의 VALUE 를 masking 값으로 치환 * refactor: 예외 처리 후에도 Response 로그 찍도록 수정 * refactor: CustomExceptionHandler 원상복구 - Response 로그를 통해 user를 추적할 수 있으므로 로그에 userId 를 추가하지 않습니다 * refactor: 리뷰 사항 반영 * refactor: RequestContext 빌더 제거 * refactor: RequestContextInterceptor import 수정 * refactor: logback yml 파일에서 timestamp 서버 시간과 동일한 규격으로 수정 * refactor: ApiPerformanceInterceptor 에서 동일 내용 로그 중복으로 찍는 문제 수정 * fix: decode를 두 번 하는 문제 수정 * test: 로깅 관련 filter, interceptor 테스트 추가 * refactor: 코드래빗 리뷰사항 반영 * test: contains 로 비교하던 검증 로직을 isEqualTo 로 수정 * test: preHandle 테스트 에서 result 값을 항상 검증 * refactor: 단위테스트에 TestContainer 어노테이션 제거 * fix: conflict 해결 * fix: docker-compose 충돌 해결 (#610) * chore: release github action 임의 실행 추가 * refactor: 기본 추천 대학 후보 추가 (#161) * fix: config.alloy 경로 수정 * hotfix: 모의지원 현황 어드민 권한 제거 * hotfix: import 제거 * chore: 서브모듈 해시 업데이트 (#611)
관련 이슈
작업 내용
멘토 지원서 관련하여 페이징 조회 기능을 추가하였습니다.
이전에 작성되어있는 어드민 성적 페이징 조회 기능을 그대로 참고 하였습니다.
기획 내용에서 text 기반 다수 칼럼 을 조회하는 것을 선호한다고 하여,
test 기반 조회가 가능해 보이는 닉네임, 권역, 나라, 파견학교 등에 대해서 매칭 하여 조회를 가능하게 하였습니다.
특이 사항
아직 부족한 점은, 검색에 있어서 우선순위가 존재하지 않습니다.
브루노 업데이트도 추후 진행하도록 하겠습니다!
리뷰 요구사항 (선택)