[WTH-458] 어드민 멤버 관리 페이지 API 개편 - #96
Hidden character warning
Conversation
- GET /admin/clubs/{clubId}/members를 PageResponse로 전환하고
기수 필터·이름/학과/학번 검색·정렬(ClubMemberSort) 지원
- 기수 정렬은 ClubMemberCardinal의 최대 기수번호 기준이라
Sort 프로퍼티로 표현할 수 없어 JPQL ORDER BY에서 처리
- 모든 정렬에 clubMemberId ASC 타이브레이커를 둬 페이지 경계 안정화
- 관리자용 멤버 상세 조회 추가 (WAITING/BANNED/LEFT 포함)
- 목록·상세 응답에 profileImageUrl, bio, joinedAt 추가
(멀티프로필 도입 이후 userProfile 우선, 레거시 필드 fallback)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WwXeZkkPXJvbhj4UVBs1nU
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough관리자 동아리 멤버 조회가 페이지네이션, 검색, 기수 필터, 정렬을 지원하도록 변경되었습니다. 응답에 프로필 정보와 가입일이 추가되었으며, 상태와 무관한 멤버 상세 조회와 관련 테스트가 확장되었습니다. Changes관리자 멤버 조회
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant 관리자
participant ClubAdminController
participant GetClubMemberQueryService
participant ClubMemberRepository
participant ClubMapper
관리자->>ClubAdminController: 페이지·검색·기수·정렬 요청
ClubAdminController->>GetClubMemberQueryService: findClubMembersForAdmin(...)
GetClubMemberQueryService->>ClubMemberRepository: 필터·정렬·페이지 조회
ClubMemberRepository-->>GetClubMemberQueryService: Page<ClubMember>
GetClubMemberQueryService->>ClubMapper: 멤버와 기수 매핑
ClubMapper-->>GetClubMemberQueryService: ClubMemberResponse
GetClubMemberQueryService-->>ClubAdminController: PageResponse<ClubMemberResponse>
ClubAdminController-->>관리자: 페이지 멤버 응답
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 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.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/test/kotlin/com/weeth/domain/club/domain/repository/ClubMemberAdminQueryTest.kt (1)
44-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLEFT 상태 멤버 포함 여부에 대한 테스트 커버리지 보강 제안.
PR 목표에
WAITING,BANNED,LEFT상태 멤버가 모두 목록에 포함되어야 한다고 명시되어 있으나,seed()와 "가입 대기·추방 멤버도 목록에 포함된다" 테스트는WAITING/BANNED만 검증하고LEFT는 다루지 않습니다. 쿼리 자체에는 상태 필터가 없어 현재는 자연히 포함되지만, 회귀 방지 관점에서LEFT케이스도 시딩·검증에 추가하는 것을 권장합니다.♻️ 제안: LEFT 상태 멤버 시딩 및 검증 추가
save("라대기", null, null, MemberStatus.WAITING, emptyList()) save("마추방", null, null, MemberStatus.BANNED, emptyList()) + save("바탈퇴", null, null, MemberStatus.LEFT, emptyList()) return club.id그리고 "가입 대기·추방 멤버도 목록에 포함된다" 테스트의 기대 목록에
MemberStatus.LEFT를 추가합니다.Also applies to: 150-170
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/kotlin/com/weeth/domain/club/domain/repository/ClubMemberAdminQueryTest.kt` around lines 44 - 79, Extend the ClubMemberAdminQueryTest seed() data with a member whose MemberStatus is LEFT, then update the “가입 대기·추방 멤버도 목록에 포함된다” test expectations to include that member and status. Keep the existing WAITING and BANNED fixtures and assertions unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/test/kotlin/com/weeth/domain/club/domain/repository/ClubMemberAdminQueryTest.kt`:
- Around line 132-148: Update the JOINED_DESC test in ClubMemberAdminQueryTest
to make the createdAt tie-breaker explicit and avoid relying on insertion-order
assumptions. Assert the result using the repository’s expected secondary
ordering, such as cm.id ascending for equal createdAt values, while retaining
verification that createdAt values are sorted descending.
---
Nitpick comments:
In
`@src/test/kotlin/com/weeth/domain/club/domain/repository/ClubMemberAdminQueryTest.kt`:
- Around line 44-79: Extend the ClubMemberAdminQueryTest seed() data with a
member whose MemberStatus is LEFT, then update the “가입 대기·추방 멤버도 목록에 포함된다” test
expectations to include that member and status. Keep the existing WAITING and
BANNED fixtures and assertions unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: f01771a0-0a5d-48b6-94e7-a4a18929f202
📒 Files selected for processing (10)
src/main/kotlin/com/weeth/domain/club/application/dto/request/ClubMemberSort.ktsrc/main/kotlin/com/weeth/domain/club/application/dto/response/ClubMemberResponse.ktsrc/main/kotlin/com/weeth/domain/club/application/mapper/ClubMapper.ktsrc/main/kotlin/com/weeth/domain/club/application/usecase/query/GetClubMemberQueryService.ktsrc/main/kotlin/com/weeth/domain/club/domain/repository/ClubMemberReader.ktsrc/main/kotlin/com/weeth/domain/club/domain/repository/ClubMemberRepository.ktsrc/main/kotlin/com/weeth/domain/club/presentation/ClubAdminController.ktsrc/main/kotlin/com/weeth/domain/club/presentation/ClubResponseCode.ktsrc/test/kotlin/com/weeth/domain/club/application/usecase/query/GetClubMemberQueryServiceTest.ktsrc/test/kotlin/com/weeth/domain/club/domain/repository/ClubMemberAdminQueryTest.kt
soo0711
left a comment
There was a problem hiding this comment.
수고하셨습니다!!
아래 하나랑 코드래빗 피드백만 보시면 될 것 같습니다 👍
| ): CommonResponse<List<ClubMemberResponse>> { | ||
| val members = getClubMemberQueryService.findClubMembersForAdmin(clubId, userId) | ||
| @RequestParam(defaultValue = "0") page: Int, | ||
| @RequestParam(defaultValue = "20") size: Int, |
There was a problem hiding this comment.
Figma 기준으로 페이지 사이즈가 10이라 size도 10으로 수정해야 할 것 같습니다!
- 멤버 목록 기본 size를 Figma 기준(10)에 맞춤 - JOINED_DESC 테스트가 createdAt 컬럼 정밀도에 의존하던 문제 해결. seed에서 네이티브 UPDATE로 가입일을 1분 간격으로 확정하고, 값이 실제로 서로 다른지 먼저 단언해 정밀도가 바뀌면 순서가 아니라 그 지점에서 실패하도록 함 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WwXeZkkPXJvbhj4UVBs1nU
개요
어드민 멤버 관리 페이지 개편에 맞춰 멤버 목록 API를 페이지네이션·검색·정렬 기반으로 전환하고, 상세 모달용 단건 조회를 추가했습니다.
기존
GET /admin/clubs/{clubId}/members는 파라미터 없이 전체 멤버를List로 반환하고 있어 새 화면 요구사항(페이지네이션, 기수 탭, 검색, 정렬)을 담을 수 없었습니다.변경 사항
1. 멤버 목록 — 페이지네이션·필터·검색·정렬
CommonResponse<List<ClubMemberResponse>>→CommonResponse<PageResponse<ClubMemberResponse>>로 변경됩니다 (프론트 대응 필요)sort:CARDINAL_DESC(기본) /CARDINAL_ASC/NAME_ASC/JOINED_DESC2. 관리자용 멤버 상세 조회
상세 모달용. 일반 사용자용
findMyMemberProfile과 달리WAITING/BANNED/LEFT멤버도 조회됩니다. 목록 행과 동일한ClubMemberResponse를 재사용합니다(account 도메인의AccountTransactionResponse와 같은 방식).3. 응답 필드 추가
profileImageUrl,bio,joinedAt구현 노트
기수 정렬을 Spring
Sort로 표현할 수 없습니다. 기수는ClubMemberCardinal별도 엔티티(멤버 1:N)라 프로퍼티 매핑이 안 됩니다. 그래서ClubMemberSort는 다른 정렬 enum과 달리toSort()를 두지 않고, JPQLORDER BY에서 최대 기수번호 스칼라 서브쿼리로 처리합니다. 정렬 키는 문자열 연결이 아니라CASE WHEN :sortKey = '...'비교라 정렬 키 인젝션 여지가 없습니다.페이지 경계 안정성: 모든 정렬 분기 끝에
cm.id ASC타이브레이커를 둬서 같은 기수 멤버가 페이지 경계에서 중복/누락되지 않습니다.N+1 회피: 기수 목록은 조회된 페이지의 멤버에 대해서만 일괄 조회합니다. count 쿼리와 본 쿼리의 WHERE 조건은 완전히 동일하고, fetch join은 ToOne(
user,userProfile)뿐이라 페이징이 인메모리로 떨어지지 않습니다.테스트
ClubMemberAdminQueryTest(신규,@DataJpaTest+ Testcontainers) — 정렬 4종의 순서까지 실DB로 단언, 기수 필터·keyword 검색·동시 적용, 페이지 경계 중복/누락 없음, 상태 무관 상세 조회. ORDER BY에 스칼라 서브쿼리를 쓰므로 mock이 아니라 실제 MySQL로 검증했습니다.GetClubMemberQueryServiceTest— 페이지 응답 조립, keyword 공백 trim,size1~100 보정, 상세 조회 실패 경로(미존재/타 동아리)./gradlew ktlintCheck test통과.API 코드
MEMBER_FIND_DETAIL_SUCCESS(신규)이번 PR에서 제외한 것
updateCardinals·applyOb에도 있는 문제라 별도 이슈로 분리합니다.memberStatus) 필터 파라미터 — 승인 대기 멤버를 찾기 위해 후속으로 필요할 수 있습니다.🤖 Generated with Claude Code
https://claude.ai/code/session_01WwXeZkkPXJvbhj4UVBs1nU
Summary by CodeRabbit
새 기능
개선
테스트