-
Notifications
You must be signed in to change notification settings - Fork 2
[refactor] 동아리 수정 API 명세 변경 #961
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
[refactor] 동아리 수정 API 명세 변경 #961
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | 변경 요약 |
|---|---|
도메인: Club 관련 변경 backend/src/main/java/moadong/club/entity/Club.java |
ClubDescription clubDescription 필드 추가, 모든 생성자/빌더 및 update(ClubInfoRequest)에서 초기화/할당 추가 |
도메인: 모집 정보 정리 backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java |
description(String) 및 faqs 필드 제거; updateDescription(...)에서 관련 할당 제거 |
새 VO/Entity 추가 backend/src/main/java/moadong/club/entity/ClubDescription.java, .../ClubAward.java, .../ClubIdealCandidate.java |
클럽 상세 설명을 위한 ClubDescription, 수상(ClubAward), 이상적 후보(ClubIdealCandidate) 엔티티 추가 |
DTO/Record 추가 및 변환 backend/src/main/java/moadong/club/payload/dto/ClubDescriptionDto.java, .../ClubAwardDto.java, .../ClubIdealCandidateDto.java, .../FaqDto.java |
엔티티↔DTO 변환(from/toEntity) 구현 및 검증 애노테이션 추가 |
API/요청 객체 변경 backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java, .../ClubRecruitmentInfoUpdateRequest.java |
ClubInfoRequest에 description: ClubDescriptionDto 추가; ClubRecruitmentInfoUpdateRequest에서 description/faqs 제거 |
응답 DTO 변경 backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java |
description 타입을 ClubDescriptionDto로 변경, faqs 및 recommendClubs 필드 제거; of(Club) 팩토리로 시그니처 변경 |
검색/추천 제거 backend/src/main/java/moadong/club/repository/ClubSearchRepository.java |
추천 관련 공개 메서드 searchRecommendClubs(...) 및 모든 내부 헬퍼 메서드 제거 |
서비스 조정 backend/src/main/java/moadong/club/service/ClubProfileService.java |
getClubDetail 등에서 추천 클럽 조회 제거 및 ClubDetailedResult.of(club) 호출로 변경 |
테스트/픽스처 업데이트 backend/src/test/java/moadong/fixture/ClubFixture.java, .../ClubRequestFixture.java |
픽스처에서 recruitment description/faqs 제거 및 ClubDescriptionDto 포함 등 테스트 데이터 수정 |
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related issues
- MOA-452: 모집정보 수정 API 명세 변경 — 본 PR은 모집정보 수정에서 FAQ/Description 제거와 recommend clubs 삭제 요구를 구현합니다.
- [refactor] MOA-452 동아리 수정 API 명세 변경 #960: 모집정보 description/faqs 제거 및 recommend-clubs 삭제 관련 변경 사항과 일치합니다.
- [feature] MOA-453 동아리 소개 필드 추가 #962:
ClubDescription엔티티/DTO 추가 및 Club에 통합하는 목표와 직접적으로 일치합니다.
Possibly related PRs
- [feature] 동아리 소개 필드 추가 #963: ClubDescription 및 관련 DTO/변환을 추가하는 PR로 코드 레벨 변경이 겹칩니다.
- [feature]추천 동아리를 보여주는 API 개발 #656: 추천 기능(searchRecommendClubs) 관련 변경을 되돌리거나 삭제하는 PR과 기능 충돌 가능성이 있습니다.
- [fix] 추천동아리 목록을 제거한다 #813: 추천 동아리 기능 제거(백엔드/프론트)의 다른 기여와 연결됩니다.
Suggested labels
📬 API
Suggested reviewers
- oesnuj
- Zepelown
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | You can run @coderabbitai generate docstrings to improve docstring coverage. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | PR 제목이 '[refactor] 동아리 수정 API 명세 변경'으로 변경사항의 핵심을 명확하게 요약하고 있습니다. |
| Linked Issues check | ✅ Passed | 모든 연결된 이슈(MOA-452) 요구사항을 충족합니다: 모집정보 API에서 description/faqs 필드 제거, recommend clubs 데이터 삭제. |
| Out of Scope Changes check | ✅ Passed | ClubAward, ClubDescription, ClubIdealCandidate 같은 새 엔티티/DTO 추가는 description/faqs를 클럽 정보 수정 API로 이동하기 위한 필수 구조 변경이므로 범위 내입니다. |
✨ Finishing touches
- 📝 Generate docstrings
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
refactor/#960-recruitment-api-update-MOA-452
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 @coderabbitai help to get the list of available commands and usage tips.
Test Results75 tests 72 ✅ 18s ⏱️ Results for commit 61872ed. ♻️ This comment has been updated with latest results. |
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java (1)
76-81: 메서드 이름이 실제 동작과 일치하지 않습니다.
updateDescription메서드는 더 이상 description을 업데이트하지 않고, 모집 기간 관련 정보(recruitmentStart, recruitmentEnd, recruitmentTarget, externalApplicationUrl)만 업데이트합니다. 메서드 이름을 실제 동작에 맞게 변경하는 것을 권장합니다.예:
updateRecruitmentInfo또는updateRecruitmentPeriod🔎 제안하는 리팩토링
- public void updateDescription(ClubRecruitmentInfoUpdateRequest request) { + public void updateRecruitmentInfo(ClubRecruitmentInfoUpdateRequest request) { this.recruitmentStart = request.recruitmentStart(); this.recruitmentEnd = request.recruitmentEnd(); this.recruitmentTarget = request.recruitmentTarget(); this.externalApplicationUrl = request.externalApplicationUrl(); }이 변경을 적용하면
Club.java의 Line 124도 함께 수정해야 합니다:public void update(ClubRecruitmentInfoUpdateRequest request) { - clubRecruitmentInformation.updateDescription(request); + clubRecruitmentInformation.updateRecruitmentInfo(request); }
🧹 Nitpick comments (2)
backend/src/main/java/moadong/club/entity/Club.java (1)
49-51: 데이터베이스 마이그레이션 전략을 확인하세요.
Club엔티티에 새로운 필드(description,faqs)가 추가되었습니다. 다음 사항을 확인해주세요:
- 기존 동아리 데이터에서
ClubRecruitmentInformation의 description과 faqs를Club으로 마이그레이션하는 스크립트가 준비되어 있는지 확인- 마이그레이션 전략이 문서화되어 있는지 확인
- 롤백 시나리오가 고려되었는지 확인
필드가 nullable로 설정되어 있어 점진적 마이그레이션이 가능하지만, 데이터 일관성 확보를 위한 명시적인 마이그레이션 계획이 필요합니다.
데이터 마이그레이션 스크립트 작성을 도와드릴까요?
backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java (1)
20-21: 새로운 필드에 대한 유효성 검증 추가를 고려하세요.
description과faqs필드에 유효성 검증 어노테이션이 없습니다:
description:@NotBlank,@Size등의 제약 추가 검토faqs:List<Faq>를 검증하기 위해@Valid추가 및Faq엔티티의question,answer필드에@NotBlank등의 제약 추가 검토참고로
Faq엔티티는 현재 유효성 검증 어노테이션이 전혀 없으므로, 중첩된 객체 검증을 위해 필드 수준의 제약을 추가하는 것이 좋습니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira 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/entity/Club.javabackend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.javabackend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.javabackend/src/main/java/moadong/club/payload/request/ClubInfoRequest.javabackend/src/main/java/moadong/club/payload/request/ClubRecruitmentInfoUpdateRequest.javabackend/src/main/java/moadong/club/repository/ClubSearchRepository.javabackend/src/main/java/moadong/club/service/ClubProfileService.java
💤 Files with no reviewable changes (1)
- backend/src/main/java/moadong/club/repository/ClubSearchRepository.java
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 Learning: 2025-05-19T05:45:52.957Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 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/entity/Club.javabackend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.javabackend/src/main/java/moadong/club/service/ClubProfileService.java
📚 Learning: 2025-09-30T05:26:41.788Z
Learnt from: alsdddk
Repo: Moadong/moadong PR: 765
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:431-435
Timestamp: 2025-09-30T05:26:41.788Z
Learning: In the Moadong codebase's club application feature (backend/src/main/java/moadong/club/), multiple ClubApplicationForm entities can have ACTIVE status for the same clubId, semesterYear, and semesterTerm simultaneously. There is no uniqueness constraint requiring only one ACTIVE form per semester.
Applied to files:
backend/src/main/java/moadong/club/entity/Club.javabackend/src/main/java/moadong/club/payload/request/ClubInfoRequest.javabackend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.javabackend/src/main/java/moadong/club/service/ClubProfileService.java
📚 Learning: 2025-08-25T14:43:52.320Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 703
File: backend/src/main/java/moadong/club/controller/ClubApplyController.java:84-84
Timestamp: 2025-08-25T14:43:52.320Z
Learning: In the Moadong codebase, questionId and clubId are equivalent identifiers that represent the same entity. The ClubApplicationRepository.findAllByIdInAndQuestionId method correctly uses clubId as the questionId parameter for filtering club applications.
Applied to files:
backend/src/main/java/moadong/club/entity/Club.javabackend/src/main/java/moadong/club/service/ClubProfileService.java
🔇 Additional comments (4)
backend/src/main/java/moadong/club/payload/request/ClubRecruitmentInfoUpdateRequest.java (1)
5-10: 변경 사항이 적절합니다.description과 faqs 필드가 제거되어 이제 모집 정보 업데이트 요청이 모집 기간 및 관련 정보에만 집중하도록 명확해졌습니다.
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (1)
37-37: 리팩토링이 적절하게 수행되었습니다.추천 동아리 기능 제거와 description/faqs 필드의 데이터 소스 변경이 올바르게 반영되었습니다:
- 팩토리 메서드가 단순화되어
Club객체만 받도록 변경- description과 faqs를
Club엔티티에서 직접 조회하도록 수정Also applies to: 69-70, 86-87
backend/src/main/java/moadong/club/entity/Club.java (1)
101-102: 필드 업데이트 로직이 올바르게 구현되었습니다.
ClubInfoRequest에서 description과 faqs를 받아 Club 엔티티에 설정하는 로직이 적절합니다.backend/src/main/java/moadong/club/service/ClubProfileService.java (1)
54-56: 서비스 레이어 변경이 적절합니다.추천 동아리 기능 제거에 맞춰
ClubDetailedResult생성 로직이 올바르게 단순화되었습니다.
seongwon030
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.
고생하셨습니다
entity를 dto로 사용하던 코드 리팩토링하였습니다.
…ocument-MOA-453 [feature] 동아리 소개 필드 추가
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
backend/src/main/java/moadong/club/entity/Club.java (1)
126-127:updateDescription메서드의 이름이 실제 동작과 일치하지 않습니다.Line 125의
update(ClubRecruitmentInfoUpdateRequest request)메서드가 호출하는clubRecruitmentInformation.updateDescription(request)는 실제로 description을 업데이트하지 않습니다.ClubRecruitmentInformation.updateDescription의 구현을 보면recruitmentStart,recruitmentEnd,recruitmentTarget,externalApplicationUrl만 업데이트하며, 요청 객체에도 description 필드가 없습니다. 메서드 이름을 실제 동작을 반영하는 이름으로 변경해야 합니다 (예:updateRecruitmentInfo).
🧹 Nitpick comments (1)
backend/src/main/java/moadong/club/entity/ClubAward.java (1)
10-19: LGTM! 엔티티 구조가 올바릅니다.Lombok 애노테이션과 필드 정의가 적절하며, DTO에 의존하지 않는 올바른 계층 분리를 따르고 있습니다.
다만,
achievementsList 필드에 대한 방어적 복사(defensive copy)를 고려해보세요. 현재는 빌더를 통해 전달된 List가 그대로 참조되어 외부에서 수정 가능합니다:🔎 방어적 복사를 적용한 제안
@Getter @Builder @AllArgsConstructor @NoArgsConstructor public class ClubAward { private String semester; - private List<String> achievements; + @Builder.Default + private List<String> achievements = new ArrayList<>(); + + // 빌더에서 방어적 복사 적용 + public static class ClubAwardBuilder { + public ClubAwardBuilder achievements(List<String> achievements) { + this.achievements = achievements != null ? new ArrayList<>(achievements) : new ArrayList<>(); + return this; + } + } }Based on learnings, 엔티티는 DTO에 의존하지 않아야 하며 이 점이 잘 지켜지고 있습니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (12)
backend/src/main/java/moadong/club/entity/Club.javabackend/src/main/java/moadong/club/entity/ClubAward.javabackend/src/main/java/moadong/club/entity/ClubDescription.javabackend/src/main/java/moadong/club/entity/ClubIdealCandidate.javabackend/src/main/java/moadong/club/payload/dto/ClubAwardDto.javabackend/src/main/java/moadong/club/payload/dto/ClubDescriptionDto.javabackend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.javabackend/src/main/java/moadong/club/payload/dto/ClubIdealCandidateDto.javabackend/src/main/java/moadong/club/payload/dto/FaqDto.javabackend/src/main/java/moadong/club/payload/request/ClubInfoRequest.javabackend/src/test/java/moadong/fixture/ClubFixture.javabackend/src/test/java/moadong/fixture/ClubRequestFixture.java
💤 Files with no reviewable changes (1)
- backend/src/test/java/moadong/fixture/ClubFixture.java
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: Zepelown
Repo: Moadong/moadong PR: 406
File: backend/src/main/java/moadong/club/entity/ClubApplicationQuestion.java:32-33
Timestamp: 2025-05-15T12:03:57.356Z
Learning: 엔티티 클래스는 요청/응답 객체(DTO)에 의존해서는 안 됩니다. 계층 간 의존성 문제를 방지하기 위해 엔티티와 DTO는 분리되어야 합니다. 예를 들어, `ClubApplicationQuestion` 엔티티가 `ClubApplicationRequest.Options`와 같은 요청 객체를 직접 참조하는 대신, 엔티티 패키지 내에 `QuestionOptions`와 같은 별도의 클래스를 정의하고 사용해야 합니다. 이렇게 하면 요청 객체 변경이 데이터베이스 스키마나 엔티티 계층에 영향을 미치지 않습니다.
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Learnt from: seongwon030
Repo: Moadong/moadong PR: 548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.196Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
📚 Learning: 2025-08-25T14:43:52.320Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 703
File: backend/src/main/java/moadong/club/controller/ClubApplyController.java:84-84
Timestamp: 2025-08-25T14:43:52.320Z
Learning: In the Moadong codebase, questionId and clubId are equivalent identifiers that represent the same entity. The ClubApplicationRepository.findAllByIdInAndQuestionId method correctly uses clubId as the questionId parameter for filtering club applications.
Applied to files:
backend/src/main/java/moadong/club/entity/ClubIdealCandidate.javabackend/src/test/java/moadong/fixture/ClubRequestFixture.java
📚 Learning: 2025-09-30T05:26:41.788Z
Learnt from: alsdddk
Repo: Moadong/moadong PR: 765
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:431-435
Timestamp: 2025-09-30T05:26:41.788Z
Learning: In the Moadong codebase's club application feature (backend/src/main/java/moadong/club/), multiple ClubApplicationForm entities can have ACTIVE status for the same clubId, semesterYear, and semesterTerm simultaneously. There is no uniqueness constraint requiring only one ACTIVE form per semester.
Applied to files:
backend/src/main/java/moadong/club/entity/ClubAward.javabackend/src/test/java/moadong/fixture/ClubRequestFixture.javabackend/src/main/java/moadong/club/entity/ClubDescription.javabackend/src/main/java/moadong/club/entity/Club.java
📚 Learning: 2025-05-19T05:45:52.957Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 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/test/java/moadong/fixture/ClubRequestFixture.javabackend/src/main/java/moadong/club/entity/Club.javabackend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java
🧬 Code graph analysis (6)
backend/src/main/java/moadong/club/entity/ClubIdealCandidate.java (1)
backend/src/main/java/moadong/club/entity/ClubDescription.java (1)
Getter(10-27)
backend/src/main/java/moadong/club/entity/ClubAward.java (1)
backend/src/main/java/moadong/club/entity/ClubDescription.java (1)
Getter(10-27)
backend/src/test/java/moadong/fixture/ClubRequestFixture.java (1)
frontend/src/types/club.ts (1)
ClubDescription(28-31)
backend/src/main/java/moadong/club/entity/ClubDescription.java (4)
backend/src/main/java/moadong/club/entity/ClubAward.java (1)
Getter(10-19)backend/src/main/java/moadong/club/entity/ClubIdealCandidate.java (1)
Getter(10-19)backend/src/main/java/moadong/club/entity/Faq.java (1)
NoArgsConstructor(7-13)frontend/src/types/club.ts (1)
ClubDescription(28-31)
backend/src/main/java/moadong/club/entity/Club.java (1)
frontend/src/types/club.ts (1)
ClubDescription(28-31)
backend/src/main/java/moadong/club/payload/dto/ClubDescriptionDto.java (1)
frontend/src/types/club.ts (1)
ClubDescription(28-31)
⏰ 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: test
🔇 Additional comments (12)
backend/src/main/java/moadong/club/payload/dto/ClubIdealCandidateDto.java (2)
8-13: 검증 제약조건을 명확히 해주세요.Line 9의
List<@Size(max = 10) String> tags는 각 태그 문자열의 최대 길이를 10으로 제한하는 것이지, 리스트의 크기를 제한하는 것이 아닙니다.의도를 확인해주세요:
- 각 태그 문자열의 최대 길이를 10자로 제한하려는 것이 맞나요?
- 아니면 태그 리스트의 최대 개수를 10개로 제한하려는 것인가요?
만약 리스트 크기를 제한하려면 다음과 같이 수정해야 합니다:
🔎 리스트 크기 제한 제안
public record ClubIdealCandidateDto( - List<@Size(max = 10) String> tags, + @Size(max = 10) + List<String> tags, @Size(max = 700) String content ) {또는 두 가지를 모두 제한하려면:
public record ClubIdealCandidateDto( - List<@Size(max = 10) String> tags, + @Size(max = 10) + List<@Size(max = 50) String> tags,
14-24: LGTM! 매핑 로직이 올바릅니다.null 안전성을 확보한
from()팩토리 메서드와 엔티티 변환을 위한toEntity()메서드가 적절히 구현되어 있습니다.backend/src/main/java/moadong/club/entity/ClubIdealCandidate.java (1)
10-19: LGTM! 엔티티 구조가 올바릅니다.Lombok 애노테이션과 필드 정의가 적절하며, DTO에 의존하지 않는 올바른 계층 분리를 따르고 있습니다.
Based on learnings, 엔티티는 요청/응답 객체(DTO)에 의존해서는 안 되며 이 점이 잘 지켜지고 있습니다.
backend/src/main/java/moadong/club/payload/dto/FaqDto.java (1)
6-21: LGTM! DTO 구현이 올바릅니다.검증 제약조건(
@Size)이 적절하게 설정되어 있고, 엔티티와의 양방향 매핑 메서드(from(),toEntity())가 null 안전하게 구현되어 있습니다.backend/src/main/java/moadong/club/payload/dto/ClubAwardDto.java (2)
8-13: 검증 제약조건을 명확히 해주세요.Line 12의
List<@Size(max = 100) String> achievements는 각 성과 문자열의 최대 길이를 100으로 제한하는 것이지, 리스트의 크기를 제한하는 것이 아닙니다.ClubIdealCandidateDto와 동일한 패턴이므로, 프로젝트 전체에서 일관된 검증 전략을 확인해주세요:
- 각 성과 문자열의 최대 길이만 제한하는 것이 맞나요?
- 성과 리스트의 최대 개수도 제한해야 하나요?
필요하다면 리스트 크기 제한을 추가하세요:
🔎 리스트 크기 제한 제안
public record ClubAwardDto( @Size(max = 50) String semester, - List<@Size(max = 100) String> achievements + @Size(max = 20) + List<@Size(max = 100) String> achievements ) {
14-24: LGTM! 매핑 로직이 올바릅니다.null 안전성을 확보한
from()팩토리 메서드와 엔티티 변환을 위한toEntity()메서드가 적절히 구현되어 있습니다.backend/src/test/java/moadong/fixture/ClubRequestFixture.java (1)
24-24: ClubDescriptionDto.from() 메서드는 null 필드를 안전하게 처리합니다.Line 24의 코드는 예상대로 동작합니다.
ClubDescriptionDto.from()메서드가 null 입력과 null 필드를 방어적으로 처리하므로 (if (description == null) return null;과 null 체크를 통한 조건부 스트리밍), 모든 필드가 null인 ClubDescription 객체로부터 생성된 DTO도 안전합니다.다만 테스트 시나리오의 현실성을 높이기 위해, 더 구체적인 테스트 데이터 사용을 권장합니다:
ClubDescriptionDto.from(ClubDescription.builder() + .introDescription("소개") + .activityDescription("활동 설명") + .benefits("혜택") .build()),backend/src/main/java/moadong/club/entity/Club.java (1)
60-60: 생성자 초기화가 올바르게 구현되었습니다.모든 생성자에서
clubDescription을 적절히 초기화하여 NPE를 방지하고 있습니다.Also applies to: 70-70, 81-81, 91-91
backend/src/main/java/moadong/club/payload/dto/ClubDescriptionDto.java (3)
40-49: LGTM!
toEntity()메서드의 널 처리가 안전하게 구현되어 있습니다.
28-38: 널 처리가 적절하게 구현되었습니다.
from()메서드의 널 처리가 올바르게 구현되어 있습니다. Line 34의ClubIdealCandidateDto.from()을 비롯해ClubAwardDto.from()과FaqDto.from()모두 null 입력을 안전하게 처리(return null)하므로 문제없습니다.
9-27: 프론트엔드와 백엔드 타입 불일치를 확인하세요.백엔드의
ClubDescriptionDto는 복잡한 중첩 구조(introDescription, activityDescription, awards, idealCandidate, benefits, faqs)를 가지고 있지만,frontend/src/types/club.ts의ClubDescription인터페이스는 단순한{id: string, description: string | null}구조입니다. 이는 타입 불일치로 런타임 오류를 발생시킬 수 있습니다.다음 스크립트를 실행하여 프론트엔드의 ClubDescription 사용처를 확인하세요:
#!/bin/bash # Description: 프론트엔드에서 ClubDescription 타입의 사용처 확인 # ClubDescription 타입 정의 확인 rg -nP -A5 'interface ClubDescription' frontend/src/types/ # ClubDescription 사용처 확인 rg -nP 'ClubDescription' frontend/src/ --type=ts --type=tsxbackend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (1)
67-67: 널 안전성 우려는 이미 충분히 처리되어 있습니다.
clubDescription필드는 Club 엔티티의 모든 생성자에서ClubDescription.builder().build()로 초기화되므로 null을 반환하지 않습니다. 또한ClubDescriptionDto.from()은 null 입력에 대해 안전하게 처리하므로 추가적인 null 검증이 필요하지 않습니다.
| this.state = ClubState.AVAILABLE; | ||
| this.socialLinks = request.socialLinks(); | ||
| this.clubRecruitmentInformation.update(request); | ||
| this.clubDescription = request.description().toEntity(); |
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
🏁 Script executed:
# Find and examine ClubInfoRequest.java
find backend -name "ClubInfoRequest.java" -type fRepository: Moadong/moadong
Length of output: 130
🏁 Script executed:
# Check if the file exists and read it
cat backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.javaRepository: Moadong/moadong
Length of output: 689
🏁 Script executed:
# Also check the Club.java file around line 104 for context
sed -n '95,115p' backend/src/main/java/moadong/club/entity/Club.javaRepository: Moadong/moadong
Length of output: 820
🏁 Script executed:
# Check for any validation annotations on description field
rg -n '@NotNull|@Nullable|@Valid' backend/src/main/java/moadong/club/payload/request/Repository: Moadong/moadong
Length of output: 1951
null 안전성 검증 필요 - 논증이 타당합니다.
ClubInfoRequest의 description 필드에는 검증 애노테이션이 없어 null이 될 수 있습니다. 현재 코드에서 request.description().toEntity()를 직접 호출하면 null인 경우 NPE가 발생합니다. @NotNull 애노테이션을 추가하거나, 호출 전에 null 체크를 추가해야 합니다.
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/entity/Club.java around line 104, the code
directly calls request.description().toEntity() which can throw an NPE because
ClubInfoRequest.description may be null; either enforce non-null by adding
@NotNull on the description field in ClubInfoRequest (and validate incoming
requests) or add a null-check here and handle the null case (e.g., assign a
default/empty ClubDescription entity or throw a clear IllegalArgumentException)
before calling toEntity().
| @Getter | ||
| @Builder | ||
| @AllArgsConstructor | ||
| @NoArgsConstructor | ||
| public class ClubDescription { | ||
|
|
||
| private String introDescription; | ||
|
|
||
| private String activityDescription; | ||
|
|
||
| private List<ClubAward> awards; | ||
|
|
||
| private ClubIdealCandidate idealCandidate; | ||
|
|
||
| private String benefits; | ||
|
|
||
| private List<Faq> faqs; | ||
| } |
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.
null 안전성과 불변성을 강화해주세요.
ClubDescription 엔티티의 모든 필드가 nullable이며, List 필드들에 대한 방어적 복사가 없습니다. 이는 다음과 같은 문제를 야기할 수 있습니다:
- NPE 위험: null 필드 접근 시 NullPointerException 발생 가능
- 외부 수정 가능: List 필드들이 외부에서 수정될 수 있음
다음 위치에서 null 처리를 확인해주세요:
#!/bin/bash
# ClubDescription을 사용하는 코드에서 null 체크 확인
rg -A 5 'clubDescription\.(get|awards|faqs|idealCandidate)' --type java🔎 방어적 복사 및 null 안전성 강화 제안
+import java.util.ArrayList;
+import java.util.Collections;
+
@Getter
-@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ClubDescription {
private String introDescription;
private String activityDescription;
private List<ClubAward> awards;
private ClubIdealCandidate idealCandidate;
private String benefits;
private List<Faq> faqs;
+
+ @Builder
+ public ClubDescription(
+ String introDescription,
+ String activityDescription,
+ List<ClubAward> awards,
+ ClubIdealCandidate idealCandidate,
+ String benefits,
+ List<Faq> faqs) {
+ this.introDescription = introDescription;
+ this.activityDescription = activityDescription;
+ this.awards = awards != null ? new ArrayList<>(awards) : new ArrayList<>();
+ this.idealCandidate = idealCandidate;
+ this.benefits = benefits;
+ this.faqs = faqs != null ? new ArrayList<>(faqs) : new ArrayList<>();
+ }
+
+ public List<ClubAward> getAwards() {
+ return awards != null ? Collections.unmodifiableList(awards) : Collections.emptyList();
+ }
+
+ public List<Faq> getFaqs() {
+ return faqs != null ? Collections.unmodifiableList(faqs) : Collections.emptyList();
+ }
}🤖 Prompt for AI Agents
backend/src/main/java/moadong/club/entity/ClubDescription.java lines 10-27: all
fields are nullable and List fields lack defensive copies; change the design to
enforce null-safety and immutability by making fields non-null where possible,
initialize List fields to empty immutable collections (e.g.,
Collections.emptyList()) via Lombok @Builder.Default or constructor defaults,
remove or avoid exposing setters/NoArgsConstructor for mutability, and ensure
getters return unmodifiable copies (or use ImmutableList) so callers cannot
modify internal lists; update constructors/builders to defensively copy incoming
lists (e.g., new ArrayList<>(input) wrapped with Collections.unmodifiableList)
and validate/replace null scalar fields with empty strings or Optional as
appropriate.
| List<String> tags, | ||
| String introduction, | ||
| String presidentName, | ||
| ClubDescriptionDto description, |
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
🏁 Script executed:
# Find ClubInfoRequest.java
fd -t f "ClubInfoRequest.java" --type fRepository: Moadong/moadong
Length of output: 130
🏁 Script executed:
# Find ClubDescriptionDto.java
fd -t f "ClubDescriptionDto.java" --type fRepository: Moadong/moadong
Length of output: 129
🏁 Script executed:
# Examine ClubInfoRequest.java
cat -n backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.javaRepository: Moadong/moadong
Length of output: 871
🏁 Script executed:
# Examine ClubDescriptionDto.java
cat -n backend/src/main/java/moadong/club/payload/dto/ClubDescriptionDto.javaRepository: Moadong/moadong
Length of output: 2065
중첩된 DTO의 검증 제약조건을 활성화하려면 @Valid 애노테이션을 추가하세요.
ClubDescriptionDto 필드에 @Valid 애노테이션이 누락되어 있습니다. ClubDescriptionDto에는 @Size 제약조건과 중첩된 @Valid 애노테이션(awards, idealCandidate, faqs 필드)이 있으므로, 부모 객체인 ClubInfoRequest에서 description 필드에 @Valid를 추가하여 계층적 검증을 활성화해야 합니다.
+import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import moadong.club.payload.dto.ClubDescriptionDto;
import moadong.club.enums.ClubCategory;
import moadong.club.enums.ClubDivision;
import moadong.global.annotation.PhoneNumber;
public record ClubInfoRequest(
@NotBlank
String name,
ClubCategory category,
ClubDivision division,
List<String> tags,
String introduction,
String presidentName,
+ @Valid
ClubDescriptionDto description,
@PhoneNumber
String presidentPhoneNumber,
Map<String, String> socialLinks
) {📝 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.
| ClubDescriptionDto description, | |
| import jakarta.validation.Valid; | |
| import jakarta.validation.constraints.NotBlank; | |
| import moadong.club.payload.dto.ClubDescriptionDto; | |
| import moadong.club.enums.ClubCategory; | |
| import moadong.club.enums.ClubDivision; | |
| import moadong.global.annotation.PhoneNumber; | |
| public record ClubInfoRequest( | |
| @NotBlank | |
| String name, | |
| ClubCategory category, | |
| ClubDivision division, | |
| List<String> tags, | |
| String introduction, | |
| String presidentName, | |
| @Valid | |
| ClubDescriptionDto description, | |
| @PhoneNumber | |
| String presidentPhoneNumber, | |
| Map<String, String> socialLinks | |
| ) { |
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java
around line 20, the description field is declared as "ClubDescriptionDto
description" but is missing the @Valid annotation so nested constraints on
ClubDescriptionDto (and its nested lists) won't be cascaded; add the @Valid
annotation to the description field declaration (i.e., annotate it with @Valid)
and ensure the correct javax.validation.Valid or jakarta.validation.Valid import
is present so hierarchical validation is enabled.
#️⃣연관된 이슈
#960
📝작업 내용
추천동아리 목록 필드 제거
기존 모집정보 수정 api에 존재하던 description, faqs 필드를 동아리 정보 수정 api로 이동
PUT /api/club/info{ "name": "string", "category": "봉사", "division": "중동", "tags": [ "string" ], "introduction": "string", "presidentName": "string", "description": "string", "faqs": [ { "question": "string", "answer": "string" } ], "presidentPhoneNumber": "string", "socialLinks": { "additionalProp1": "string", "additionalProp2": "string", "additionalProp3": "string" } }중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
릴리스 노트
새로운 기능
리팩토링
테스트
✏️ Tip: You can customize this high-level summary in your review settings.