Conversation
…github.com/pknu-wap/Moadong into feature/#655-recommend-club-server-MOA-161
…er-MOA-161 [feature]추천 동아리를 보여주는 API 개발
…er-MOA-161 [feature] 동아리 추천 로직 개선
|
Warning
|
| Cohort / File(s) | Summary of Changes |
|---|---|
API 문서 정정backend/src/main/java/moadong/club/controller/ClubApplyController.java |
PUT/DELETE /apply/{appId}의 Swagger 설명에서 오타 수정(“자원자”→“지원자”), “appId - 지원서 아이디” 문구 추가. 코드 로직 변경 없음. |
지원 상태 체계 축소backend/src/main/java/moadong/club/enums/ApplicationStatus.java |
다수 상태 제거(DRAFT, SCREENING 등) 후 SUBMITTED, INTERVIEW_SCHEDULED, ACCEPTED, DECLINED만 유지. 상수 순서 변경 및 주석 수정. |
지원자 DTO 확장backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java |
record에 memo(String) 필드 추가로 시그니처 변경. of(...)에서 application.getMemo() 매핑 추가. |
클럽 상세 DTO 확장 및 연계backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java, backend/src/main/java/moadong/club/service/ClubProfileService.java |
ClubDetailedResult에 List<ClubSearchResult> recommendClubs 추가. of(Club) → of(Club, List)로 변경. ClubProfileService가 ClubSearchRepository로 추천 클럽 조회 후 DTO에 주입. |
추천 클럽 검색 추가backend/src/main/java/moadong/club/repository/ClubSearchRepository.java |
public List<ClubSearchResult> searchRecommendClubs(String category, String excludeClubId) 추가. 동일 카테고리/모집중 우선→카테고리 보강→랜덤 풀에서 모집상태 우선 선별하는 3단계 파이프라인과 중복 배제 헬퍼들 추가. |
상태 집계 로직 단순화backend/src/main/java/moadong/club/service/ClubApplyService.java |
getClubApplyInfo의 상태 스위치 단순화: SUBMITTED만 검토대상, INTERVIEW_SCHEDULED만 인터뷰 예정, ACCEPTED만 합격으로 카운트. 제거된 상태 반영. |
Sequence Diagram(s)
sequenceDiagram
participant Client
participant ClubProfileService
participant ClubSearchRepository
participant DB
participant DTO as ClubDetailedResult
Client->>ClubProfileService: getClubDetail(clubId)
ClubProfileService->>DB: find Club by clubId
DB-->>ClubProfileService: Club
ClubProfileService->>ClubSearchRepository: searchRecommendClubs(category, excludeClubId)
ClubSearchRepository->>DB: fetch category & recruiting clubs
DB-->>ClubSearchRepository: clubs subset
ClubSearchRepository->>DB: fetch fallback/random pool
DB-->>ClubSearchRepository: random pool
ClubSearchRepository-->>ClubProfileService: List<ClubSearchResult>
ClubProfileService->>DTO: of(club, recommendClubs)
DTO-->>Client: ClubDetailedResult
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
- [feature] 동아리 추천 로직 개선 #660: ClubSearchRepository에 추천 로직(searchRecommendClubs)과 단계적 선별이 추가된 동일 영역 변경.
- [feature]추천 동아리를 보여주는 API 개발 #656: ClubDetailedResult 확장과 ClubProfileService의 추천 클럽 연계가 유사하게 수정됨.
- [feature] 지원서 요약 API에 메모 필드추가 #635: ClubApplicantsResult에 memo 필드 추가 및 of(...) 매핑 변경이 동일하게 이루어짐.
Suggested labels
✨ Feature, 📬 API, 💾 BE, 📈 release
Suggested reviewers
- lepitaaar
- PororoAndFriends
- seongwon030
Tip
🔌 Remote MCP (Model Context Protocol) integration is now available!
Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.
✨ Finishing Touches
- 📝 Generate Docstrings
🧪 Generate unit tests
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
develop/be
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.
🪧 Tips
Chat
There are 3 ways to chat with CodeRabbit:
- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
I pushed a fix in commit <commit_id>, please review it.Open a follow-up GitHub issue for this discussion.
- Files and specific lines of code (under the "Files changed" tab): Tag
@coderabbitaiin a new review comment at the desired location with your query. - PR comments: Tag
@coderabbitaiin a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
Support
Need help? Create a ticket on our support page for assistance with any issues or questions.
CodeRabbit Commands (Invoked using PR/Issue comments)
Type @coderabbitai help to get the list of available commands.
Other keywords and placeholders
- Add
@coderabbitai ignoreanywhere in the PR description to prevent this PR from being reviewed. - Add
@coderabbitai summaryto generate the high-level summary at a specific location in the PR description. - Add
@coderabbitaianywhere in the PR title to generate the title automatically.
Status, Documentation and Community
- Visit our Status Page to check the current availability of CodeRabbit.
- Visit our Documentation for detailed information on how to use CodeRabbit.
- Join our Discord Community to get help, request features, and share feedback.
- Follow us on X/Twitter for updates and announcements.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (11)
backend/src/main/java/moadong/club/enums/ApplicationStatus.java (1)
7-7: 주석 표현 통일 제안: ‘불합’ → ‘불합격’한국어 사용자 문맥상 ‘불합격’이 자연스럽습니다.
- DECLINED, // 불합 + DECLINED, // 불합격backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java (1)
22-24: memo 필드 노출에 대한 NPE/민감정보 취급 점검
- NPE/일관성: API 응답에서 memo가 null이 되지 않도록 빈 문자열로 normalize하는 것을 권장합니다.
- 민감정보: memo가 심사 메모(개인식별/평가 코멘트 포함)라면 저장 시 암호화 또는 최소한 접근 제어 재확인 필요합니다. 현재 answers만 암복호 처리되고 memo는 평문으로 보입니다. 의도라면 OK, 아니라면 저장/조회 경로에 동일한 보호수단 적용을 검토해 주세요.
선호 시 아래처럼 null을 빈 문자열로 변환할 수 있습니다.
- .memo(application.getMemo()) + .memo(Optional.ofNullable(application.getMemo()).orElse(""))추가로, Optional 사용을 위해 상단에 import가 필요합니다(변경 범위 밖 코드 참고).
import java.util.Optional;원하시면 edit 경로(저장 시)에서도 memo 암호화를 포함한 일관 보호 로직을 제안드릴 수 있습니다.
Also applies to: 40-46
backend/src/main/java/moadong/club/service/ClubApplyService.java (1)
112-116: 상태 카운팅 축소 적용 OK. DECLINED 명시 여부만 판단해주세요제품 의도대로 SUBMITTED/INTERVIEW_SCHEDULED/ACCEPTED만 카운팅하는 것으로 이해됩니다. 의사 표현을 위해 DECLINED에 대한 명시적 no-op case를 추가하면 코드 독해성이 약간 올라갑니다(동작 동일).
switch (app.getStatus()) { case SUBMITTED -> reviewRequired++; case INTERVIEW_SCHEDULED -> scheduledInterview++; case ACCEPTED -> accepted++; + case DECLINED -> { /* count 미포함(의도) */ } }또한 과거 Enum 값이 DB에 남아있다면 본 루프에 도달하기 전 역직렬화에서 예외가 날 수 있습니다. Enum 축소에 대한 마이그레이션/컨버터 적용 여부를 위 파일 코멘트(Enums) 참고해 재확인 부탁드립니다.
backend/src/main/java/moadong/club/service/ClubProfileService.java (1)
55-59: 추천 목록 null/오류 내성 및 성능 고려(캐싱) 제안
- null 내성: searchRecommendClubs가 null 대신 빈 리스트를 반환하도록 보장하거나, 호출부에서 방어로직을 두는 것을 권장합니다.
- 성능: 카테고리+excludeClubId 조합 기준 캐싱(SPRING Cache 등)으로 상세 페이지 트래픽을 흡수할 수 있습니다. TTL 기반 캐시면 충분.
예시(선호 시):
- List<ClubSearchResult> clubSearchResults = clubSearchRepository.searchRecommendClubs(club.getCategory(), clubId); + List<ClubSearchResult> clubSearchResults = + java.util.Optional.ofNullable( + clubSearchRepository.searchRecommendClubs(club.getCategory(), clubId) + ).orElseGet(java.util.List::of);Repository 측에서 non-null 반환을 계약으로 명시한다면 호출부 변경은 생략 가능합니다. 캐싱 적용 필요 여부는 평균 호출량/지연시간을 기준으로 결정하면 됩니다. 원하시면 Cache 설정 제안드립니다.
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (3)
41-43: DateTimeFormatter 재생성 비용 제거 (상수화 권장)메서드 호출 시마다 포매터를 생성할 필요가 없습니다. 상수로 올려 재사용하면 가독성과 성능이 모두 개선됩니다.
적용 예시:
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm"); - period = clubRecruitmentInformation.getRecruitmentStart().format(formatter) + " ~ " - + clubRecruitmentInformation.getRecruitmentEnd().format(formatter); + period = clubRecruitmentInformation.getRecruitmentStart().format(RECRUITMENT_PERIOD_FMT) + " ~ " + + clubRecruitmentInformation.getRecruitmentEnd().format(RECRUITMENT_PERIOD_FMT);그리고 레코드 내부에 정적 상수를 추가해 주세요:
// 레코드 본문(필드 선언부 아래)에 추가 private static final DateTimeFormatter RECRUITMENT_PERIOD_FMT = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm");
73-78: 중복 호출 제거 및 NPE 방지 여지 축소이미 지역 변수
clubRecruitmentInformation을 보유하고 있으므로 동일 객체에 대한 게터를 반복 호출하지 않는 편이 안전하고 간결합니다.- .externalApplicationUrl(club.getClubRecruitmentInformation().getExternalApplicationUrl() == null ? "" : - club.getClubRecruitmentInformation().getExternalApplicationUrl()) + .externalApplicationUrl( + clubRecruitmentInformation == null || clubRecruitmentInformation.getExternalApplicationUrl() == null + ? "" : clubRecruitmentInformation.getExternalApplicationUrl() + ) ... - .faqs(club.getClubRecruitmentInformation().getFaqs() == null ? List.of() - : club.getClubRecruitmentInformation().getFaqs()) + .faqs(clubRecruitmentInformation == null || clubRecruitmentInformation.getFaqs() == null + ? List.of() : clubRecruitmentInformation.getFaqs())
79-79:recommendClubsnull 안전 처리프런트에서 배열을 기대한다면 null 대신 빈 리스트를 내려주는 편이 호환성이 높습니다.
- .recommendClubs(recommendClubs) + .recommendClubs(recommendClubs == null ? List.of() : recommendClubs)backend/src/main/java/moadong/club/repository/ClubSearchRepository.java (4)
111-117: 추천에서 ‘모집중’의 정의 일치 여부 확인 (UPCOMING 포함 여부)키워드 검색의 필터 로직(Line 206-215)에서는 OPEN 필터 시 ALWAYS/OPEN/UPCOMING을 모두 모집중으로 취급합니다. 반면 여기서는 ALWAYS/OPEN만 포함합니다. 의도일 수 있으나, 일관성 측면에서 확인이 필요합니다. 포함이 맞다면 다음과 같이 보완하세요.
criteria = criteria.and("recruitmentInformation.clubRecruitmentStatus") .in( ClubRecruitmentStatus.ALWAYS.toString(), - ClubRecruitmentStatus.OPEN.toString() + ClubRecruitmentStatus.OPEN.toString(), + ClubRecruitmentStatus.UPCOMING.toString() );
77-95: 매직 넘버 상수화로 튜닝 용이성 확보
maxCategoryCount=4,totalNeeded=6, 랜덤 풀10등은 후속 운영에서 쉽게 조정될 수 있도록 상수화하는 편이 좋습니다.- int maxCategoryCount = 4; + int maxCategoryCount = RECOMMEND_CATEGORY_MAX; ... - int totalNeeded = 6; + int totalNeeded = RECOMMEND_TOTAL; ... - List<ClubSearchResult> randomPool = findRandomClubs(excludeIds, 10); + List<ClubSearchResult> randomPool = findRandomClubs(excludeIds, RANDOM_POOL_SIZE);클래스 상단(필드 아래)에 상수를 추가해 주세요:
private static final int RECOMMEND_TOTAL = 6; private static final int RECOMMEND_CATEGORY_MAX = 4; private static final int RANDOM_POOL_SIZE = 10;
90-101: 반환 컬렉션 변경 가능성 관리현재는
result가 비어있지 않으면 변경 가능한 리스트를 그대로 반환합니다. 외부에서 결과를 변형하는 것을 막고 싶다면List.copyOf(result)로 불변 리스트를 반환하는 것도 고려해 볼만 합니다.- return result.isEmpty() ? Collections.emptyList() : result; + return result.isEmpty() ? Collections.emptyList() : List.copyOf(result);
104-135: 쿼리 필드 인덱스 점검 제안카테고리/상태/모집상태로 매칭 및 샘플링을 수행하므로, MongoDB 인덱스가 없다면 추천 질의가 느려질 수 있습니다. 다음 필드들에 대한 복합/단일 인덱스 구성이 적절한지 점검해 주세요:
state,category,recruitmentInformation.clubRecruitmentStatus.원하시면 인덱스 제안(SR/읽기 패턴 기준) 및 마이그레이션 스크립트 작성 도와드릴게요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
backend/src/main/java/moadong/club/controller/ClubApplyController.java(2 hunks)backend/src/main/java/moadong/club/enums/ApplicationStatus.java(1 hunks)backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java(2 hunks)backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java(1 hunks)backend/src/main/java/moadong/club/repository/ClubSearchRepository.java(2 hunks)backend/src/main/java/moadong/club/service/ClubApplyService.java(1 hunks)backend/src/main/java/moadong/club/service/ClubProfileService.java(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-05-19T05:45:52.957Z
Learnt from: lepitaaar
PR: Moadong/moadong#406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.
Applied to files:
backend/src/main/java/moadong/club/service/ClubApplyService.java
🧬 Code Graph Analysis (1)
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (1)
frontend/src/types/club.ts (1)
Club(3-12)
🔇 Additional comments (5)
backend/src/main/java/moadong/club/enums/ApplicationStatus.java (1)
3-8: ApplicationStatus enum 변경에 따른 호환성 점검 결과
- 제거된 enum 값(DRAFT, SCREENING, …)에 대한 코드·문자열 참조 없음
@Enumerated(EnumType.ORDINAL)사용 사례 없음권장 대응
- JPA Entity 필드에
@Enumerated(EnumType.STRING)명시- Mongo 저장 시 문자열 여부 확인
- 기존 데이터 마이그레이션: 제거된 상태→신규 상태 매핑 (예: SCREENING→SUBMITTED 등)
- 알 수 없는 문자열 기본값 매핑용 컨버터 추가 (Spring Data Mongo ReadConverter 등)
필요 시 Mongo 마이그레이션 스크립트 초안 제공 가능합니다.
backend/src/main/java/moadong/club/controller/ClubApplyController.java (1)
75-77: Swagger 문구 교정 및 파라미터 안내 추가 LGTM‘클럽 지원자의 …’로의 교정과 appId 안내 문구 추가로 가독성이 좋아졌습니다. 필요 시 @parameter를 통해 PathVariable에 직접 설명을 붙이는 것도 고려 가능하나 현재도 충분히 명확합니다.
Also applies to: 90-92
backend/src/main/java/moadong/club/service/ClubProfileService.java (1)
26-28: 추천 검색 리포지토리 주입 및 사용 연계 방향성 좋습니다의존성 주입과 서비스 경계 분리가 명확합니다. 해당 Repository가 빈으로 등록되어 있고 통합테스트에서 카테고리별 결과가 보장되는지만 확인되면 좋겠습니다.
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (1)
37-44: null 검사 불필요 –clubRecruitmentInformation은 항상 non-null입니다
Club의 무인자 생성자에서clubRecruitmentInformation = ClubRecruitmentInformation.builder().build()로 초기화되며, MongoDB 매핑 시에도 빈 객체가 유지되어 절대 null이 될 수 없습니다. 기존 코드대로hasRecruitmentPeriod()를 바로 호출해도 NPE 우려가 없으므로 해당 null 가드 제안은 무시해 주세요.Likely an incorrect or invalid review comment.
backend/src/main/java/moadong/club/repository/ClubSearchRepository.java (1)
68-101: 추천 검색 파이프라인 전반 설계는 명확합니다
- 카테고리 기반(모집중 우선 → 마감 보충) 후 랜덤 풀에서 상태 우선 선별로 마무리하는 3단계 접근이 깔끔합니다.
- 중복 방지를
excludeIds로 일관되게 처리한 점 좋습니다.
| private List<ClubSearchResult> findClubsByCategoryAndState(String category, Set<String> excludeIds, | ||
| boolean onlyRecruitAvailable, int limit) { | ||
| List<AggregationOperation> ops = new ArrayList<>(); | ||
|
|
||
| Criteria criteria = Criteria.where("category").is(category) | ||
| .and("_id").nin(excludeIds); | ||
|
|
||
| if (onlyRecruitAvailable) { | ||
| criteria = criteria.and("recruitmentInformation.clubRecruitmentStatus") | ||
| .in( | ||
| ClubRecruitmentStatus.ALWAYS.toString(), | ||
| ClubRecruitmentStatus.OPEN.toString() | ||
| ); | ||
| } | ||
|
|
||
| ops.add(Aggregation.match(criteria)); | ||
| ops.add(Aggregation.sample((long) limit)); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
추천 조회에 state=AVAILABLE 필터가 누락되어 비공개/비활성 동아리 노출 위험
키워드 검색(searchClubsByKeyword)에서는 Line 31에서 state=AVAILABLE을 강제하지만, 추천 조회 경로에서는 해당 필터가 없습니다. 비활성(숨김) 클럽이 추천에 섞일 수 있습니다. 추천 노출 품질과 정책 측면에서 치명적일 수 있으니 필수 반영을 권장합니다.
- Criteria criteria = Criteria.where("category").is(category)
- .and("_id").nin(excludeIds);
+ Criteria criteria = Criteria.where("category").is(category)
+ .and("_id").nin(excludeIds)
+ .and("state").is(ClubState.AVAILABLE.getName());📝 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.
| private List<ClubSearchResult> findClubsByCategoryAndState(String category, Set<String> excludeIds, | |
| boolean onlyRecruitAvailable, int limit) { | |
| List<AggregationOperation> ops = new ArrayList<>(); | |
| Criteria criteria = Criteria.where("category").is(category) | |
| .and("_id").nin(excludeIds); | |
| if (onlyRecruitAvailable) { | |
| criteria = criteria.and("recruitmentInformation.clubRecruitmentStatus") | |
| .in( | |
| ClubRecruitmentStatus.ALWAYS.toString(), | |
| ClubRecruitmentStatus.OPEN.toString() | |
| ); | |
| } | |
| ops.add(Aggregation.match(criteria)); | |
| ops.add(Aggregation.sample((long) limit)); | |
| private List<ClubSearchResult> findClubsByCategoryAndState(String category, Set<String> excludeIds, | |
| boolean onlyRecruitAvailable, int limit) { | |
| List<AggregationOperation> ops = new ArrayList<>(); | |
| Criteria criteria = Criteria.where("category").is(category) | |
| .and("_id").nin(excludeIds) | |
| .and("state").is(ClubState.AVAILABLE.getName()); | |
| if (onlyRecruitAvailable) { | |
| criteria = criteria.and("recruitmentInformation.clubRecruitmentStatus") | |
| .in( | |
| ClubRecruitmentStatus.ALWAYS.toString(), | |
| ClubRecruitmentStatus.OPEN.toString() | |
| ); | |
| } | |
| ops.add(Aggregation.match(criteria)); | |
| ops.add(Aggregation.sample((long) limit)); | |
| // …rest of method… | |
| } |
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/repository/ClubSearchRepository.java
around lines 104 to 121, the aggregation criteria for recommended clubs lacks a
filter for state=AVAILABLE, so hidden/inactive clubs can be returned; update the
Criteria used before Aggregation.match to include a condition requiring the club
state to be AVAILABLE (use the same representation as other queries, e.g.
ClubState.AVAILABLE.toString() or the string literal "AVAILABLE"), then run the
aggregation; ensure the new criterion is combined with existing category,
exclusion and recruitment-status checks so only AVAILABLE clubs are sampled.
| .and(ConditionalOperators.ifNull("$recruitmentInformation.tags").then(Collections.emptyList())) | ||
| .as("tags") |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
tags 기본값 타입 일관성: 빈 리스트 유지 권장 (기존 메서드와 불일치)
여기서는 tags의 기본값을 Collections.emptyList()로 설정(좋음)하지만, searchClubsByKeyword에서는 빈 문자열을 기본값으로 내려보냅니다(Line 56-57). ClubSearchResult.tags가 List<String>라면, 기존 메서드는 역직렬화/매핑 단계에서 타입 불일치 문제가 날 수 있습니다. 기존 메서드도 빈 리스트로 통일하는 것을 권장합니다.
기존 메서드 수정 예시(참고용):
// searchClubsByKeyword 의 project 단계에서
.and(ConditionalOperators.ifNull("$recruitmentInformation.tags").then(Collections.emptyList()))
.as("tags")🏁 Script executed:
#!/bin/bash
set -e
# 검색 메서드 시그니처와 tags 처리 부분 확인
rg -n 'searchClubsByKeyword' -C5 backend/src/main/java/moadong/club/repository/ClubSearchRepository.java
rg -n 'ifNull' -C5 backend/src/main/java/moadong/club/repository/ClubSearchRepository.javaLength of output: 3526
🏁 Script executed:
#!/bin/bash
set -e
# ClubSearchResult 클래스 내부의 tags 필드 타입 확인
rg -n 'class ClubSearchResult' -C5 backend/src/main/java/moadong/club/repository/ClubSearchResult.java
rg -n 'tags' -C5 backend/src/main/java/moadong/club/repository/ClubSearchResult.javaLength of output: 221
🏁 Script executed:
#!/bin/bash
set -e
# ClubSearchResult 클래스 파일 경로 탐색
find backend/src/main/java -type f -name "*ClubSearchResult*.java"Length of output: 146
🏁 Script executed:
#!/bin/bash
set -e
# ClubSearchResult 클래스의 tags 필드 선언부 확인
rg -n 'tags' -C5 backend/src/main/java/moadong/club/payload/dto/ClubSearchResult.javaLength of output: 387
searchClubsByKeyword에서 tags 기본값을 빈 리스트로 통일하세요
현재 searchClubsByKeyword 메서드에서는 tags의 null 대체값으로 빈 문자열("")을 사용하지만, 이후 유사 메서드에서는 Collections.emptyList()를 사용하고 있습니다. ClubSearchResult.tags가 List<String> 타입이므로, 역직렬화 시 타입 불일치 문제가 발생할 수 있습니다.
수정 대상:
- 파일:
backend/src/main/java/moadong/club/repository/ClubSearchRepository.java - 54–57행(
Aggregation.project내부 tags 처리 부분)
변경 예시:
- .and(ConditionalOperators.ifNull("$recruitmentInformation.tags").then(""))
+ .and(ConditionalOperators.ifNull("$recruitmentInformation.tags").then(Collections.emptyList()))Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/repository/ClubSearchRepository.java
around lines 129-130, the Aggregation.project step in searchClubsByKeyword sets
tags' null replacement to an empty string which mismatches ClubSearchResult.tags
(List<String>); change the ConditionalOperators.ifNull(...).then(...) call so it
returns Collections.emptyList() instead of "" (and add/import
java.util.Collections if missing) so tags deserialize as a List<String>.
| List<AggregationOperation> ops = new ArrayList<>(); | ||
| ops.add(Aggregation.match(Criteria.where("_id").nin(excludeIds))); | ||
| ops.add(Aggregation.sample((long) sampleSize)); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
랜덤 풀 생성에도 state=AVAILABLE 조건 적용 필요
위와 동일한 이유로, 랜덤 풀 또한 비활성 클럽을 포함하지 않도록 필터링해야 합니다.
- ops.add(Aggregation.match(Criteria.where("_id").nin(excludeIds)));
+ ops.add(Aggregation.match(
+ Criteria.where("_id").nin(excludeIds)
+ .and("state").is(ClubState.AVAILABLE.getName())
+ ));📝 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.
| List<AggregationOperation> ops = new ArrayList<>(); | |
| ops.add(Aggregation.match(Criteria.where("_id").nin(excludeIds))); | |
| ops.add(Aggregation.sample((long) sampleSize)); | |
| List<AggregationOperation> ops = new ArrayList<>(); | |
| ops.add(Aggregation.match( | |
| Criteria.where("_id").nin(excludeIds) | |
| .and("state").is(ClubState.AVAILABLE.getName()) | |
| )); | |
| ops.add(Aggregation.sample((long) sampleSize)); |
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/repository/ClubSearchRepository.java
around lines 182 to 185, the aggregation that builds the random sample currently
only filters out excluded IDs and thus may include inactive clubs; modify the
match stage to also require state = AVAILABLE by combining the existing
Criteria.where("_id").nin(excludeIds) with a state check (e.g.,
Criteria.where("state").is(State.AVAILABLE) or .is("AVAILABLE") depending on the
enum/field type) so the sample pool excludes inactive clubs before the sample
operation.
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit