-
Notifications
You must be signed in to change notification settings - Fork 2
[feature] 동아리 소개 필드 추가 #963
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
[feature] 동아리 소개 필드 추가 #963
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. Warning
|
| 코호트 / 파일(s) | 변경 요약 |
|---|---|
엔티티: Club 변경 backend/src/main/java/moadong/club/entity/Club.java |
description:String와 faqs:List<Faq>를 clubDescription:ClubDescription으로 통합; 생성자·빌더 초기화 및 update(ClubInfoRequest) 매핑 변경 |
새로운 엔티티들 backend/src/main/java/moadong/club/entity/ClubDescription.java, .../ClubAward.java, .../ClubIdealCandidate.java |
클럽 상세 설명을 위한 ClubDescription, 수상 정보 ClubAward, 이상적 지원자 ClubIdealCandidate 추가 (Lombok 사용) |
DTO: Description 관련 추가/변경 backend/src/main/java/moadong/club/payload/dto/ClubDescriptionDto.java, .../ClubAwardDto.java, .../ClubIdealCandidateDto.java, .../FaqDto.java |
검증 어노테이션이 포함된 DTO 레코드 추가; from(...)/toEntity() 매핑 메서드 제공 |
요청/응답 DTO 변경 backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java, backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java |
description 타입을 String->ClubDescriptionDto로 변경; faqs 관련 타입/필드 조정 및 매핑 업데이트 |
테스트 픽스처 업데이트 backend/src/test/java/moadong/fixture/ClubFixture.java, backend/src/test/java/moadong/fixture/ClubRequestFixture.java |
픽스처에서 ClubDescriptionDto 사용으로 변경: 일부 메서드 시그니처와 목(mock) 제거/수정 |
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related issues
- [refactor] MOA-452 동아리 수정 API 명세 변경 #960 — Club 정보 모델에서 기존 description/faqs를 대체하는 변경과 직접적으로 연관됨.
- [feature] MOA-453 동아리 소개 필드 추가 #962 —
ClubDescription엔티티 도입 및 Club.description 리팩토링 목표와 일치함.
Possibly related PRs
- PR
#963— 동일한 ClubDescription 전환(엔티티/DTO/매핑) 변경을 다룸. - PR
#539— FAQ와 클럽 설명 모델링 변경을 공유함(ClubDetailedResult 등 동일 파일 영향). - PR
#956— 백엔드에서 String description → ClubDescription로 데이터 구조 변경과 관련 있음.
Suggested labels
📬 API
Suggested reviewers
- oesnuj
- Zepelown
- seongwon030
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 제목 '[feature] 동아리 소개 필드 추가'는 주요 변경사항(description 필드를 문자열에서 복합 객체로 변경)을 명확하게 설명합니다. |
| Linked Issues check | ✅ Passed | PR은 MOA-453의 모든 코딩 요구사항을 충족합니다: description 엔티티(ClubDescription, ClubAward, ClubIdealCandidate) 추가, 명세에 맞는 구조 구현, faqs를 description 내부로 이동 완료. |
| Out of Scope Changes check | ✅ Passed | 모든 변경사항이 MOA-453 범위 내입니다. ClubDescription 관련 엔티티와 DTO 추가, 필드 구조화, faqs 이동 등 모두 연관 이슈의 목표와 일치합니다. |
📜 Recent 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 (1)
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java
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 8f43ad0. |
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: 4
🧹 Nitpick comments (1)
backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java (1)
76-81: 메서드 이름이 기능과 일치하지 않습니다
updateDescription메서드는 더 이상 description 필드를 업데이트하지 않고 모집 정보(모집 시작일, 종료일, 대상, URL)만 업데이트합니다. description 필드가 ClubDescription 엔티티로 이동했으므로, 메서드 이름을updateRecruitmentInfo와 같이 실제 동작을 반영하도록 변경하는 것이 좋습니다.🔎 메서드 이름 변경 제안
- 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(); }
📜 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/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.javabackend/src/test/java/moadong/fixture/ClubFixture.javabackend/src/test/java/moadong/fixture/ClubRequestFixture.java
💤 Files with no reviewable changes (2)
- backend/src/main/java/moadong/club/repository/ClubSearchRepository.java
- 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')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
📚 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/main/java/moadong/club/service/ClubProfileService.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/main/java/moadong/club/service/ClubProfileService.javabackend/src/main/java/moadong/club/entity/ClubDescription.javabackend/src/main/java/moadong/club/entity/Club.javabackend/src/test/java/moadong/fixture/ClubRequestFixture.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/main/java/moadong/club/service/ClubProfileService.javabackend/src/main/java/moadong/club/entity/Club.javabackend/src/test/java/moadong/fixture/ClubRequestFixture.java
🧬 Code graph analysis (7)
backend/src/main/java/moadong/club/entity/ClubIdealCandidate.java (3)
backend/src/main/java/moadong/user/entity/User.java (1)
Builder(21-99)backend/src/main/java/moadong/club/entity/ClubDescription.java (1)
Getter(10-21)backend/src/main/java/moadong/club/entity/ClubAward.java (1)
Getter(10-17)
backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java (1)
frontend/src/types/club.ts (1)
ClubDescription(28-31)
backend/src/main/java/moadong/club/entity/ClubAward.java (1)
backend/src/main/java/moadong/club/entity/ClubDescription.java (1)
Getter(10-21)
backend/src/main/java/moadong/club/entity/ClubDescription.java (2)
backend/src/main/java/moadong/club/entity/ClubAward.java (1)
Getter(10-17)backend/src/main/java/moadong/club/entity/ClubIdealCandidate.java (1)
Getter(10-17)
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/ClubDetailedResult.java (1)
frontend/src/types/club.ts (1)
ClubDescription(28-31)
backend/src/test/java/moadong/fixture/ClubRequestFixture.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 (13)
backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java (1)
18-22: 임포트 재구성 확인됨시간 관련 클래스들과 List 임포트가 재구성되었습니다. 기능적 영향은 없습니다.
backend/src/main/java/moadong/club/service/ClubProfileService.java (1)
35-55: 코드 정리가 잘 되었습니다.사용하지 않는 import와 ClubSearchResult 의존성을 제거하여 코드가 더 깔끔해졌습니다. 로직 변경 없이 단순화가 잘 이루어졌습니다.
backend/src/main/java/moadong/club/entity/ClubAward.java (1)
10-17: 엔티티 구조가 올바르게 작성되었습니다.Lombok을 사용한 간단한 데이터 클래스로 잘 구성되어 있습니다. ClubDescription에 임베드되어 사용되므로 MongoDB의 기본 매핑으로 충분합니다.
backend/src/main/java/moadong/club/entity/ClubIdealCandidate.java (1)
10-17: 엔티티 구조가 올바르게 작성되었습니다.이상적인 지원자 정보를 담는 간단한 데이터 클래스로 잘 구성되어 있습니다.
backend/src/main/java/moadong/club/entity/Club.java (2)
49-50: 엔티티 필드 추가가 올바르게 구현되었습니다.
ClubDescription필드가 적절하게 추가되었으며:
- MongoDB 매핑을 위한
@Field("description")어노테이션 사용 ✓- 모든 생성자에서 NPE 방지를 위해 초기화 ✓
update(ClubInfoRequest)메서드에서 올바르게 할당 ✓Also applies to: 54-92, 104-104
125-127:updateDescription메서드는 현재 요청 구조와 호환됩니다.
ClubRecruitmentInformation.updateDescription()메서드를 확인했으며, 해당 메서드는recruitmentStart,recruitmentEnd,recruitmentTarget,externalApplicationUrl만 접근하고 있습니다. 현재ClubRecruitmentInfoUpdateRequest가 이 모든 필드를 포함하고 있으므로 호환성 문제는 없습니다.backend/src/main/java/moadong/club/payload/request/ClubRecruitmentInfoUpdateRequest.java (1)
5-10: 요청 객체 단순화가 적절하게 이루어졌습니다.
description과faqs필드를ClubDescription으로 이동시키면서, 이 요청 객체는 모집 시간과 대상에만 집중하게 되었습니다. 명확한 책임 분리가 잘 되었습니다.backend/src/main/java/moadong/club/entity/ClubDescription.java (1)
10-21: 엔티티 구조가 명세에 맞게 잘 작성되었습니다.ClubDescription 엔티티가 PR 목표에 명시된 구조를 정확하게 반영하고 있습니다:
- introDescription, activityDescription, benefits ✓
- awards (List) ✓
- idealCandidate (ClubIdealCandidate) ✓
- faqs 필드가 외부에서 내부로 이동 ✓
backend/src/test/java/moadong/fixture/ClubRequestFixture.java (2)
15-27: 테스트 픽스처가 새로운 구조에 맞게 업데이트되었습니다.
ClubInfoRequest에ClubDescription.builder().build()를 추가하여 새 생성자 시그니처와 일치시켰습니다. 빈 객체를 사용하는 것은 기본 테스트 픽스처로는 적절합니다.
30-37: 요청 객체 단순화가 올바르게 반영되었습니다.
ClubRecruitmentInfoUpdateRequest의 매개변수가 4개로 줄어든 것이 정확하게 반영되었습니다.description과faqs제거가 올바르게 적용되었습니다.backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (3)
5-5: LGTM: 필요한 import 추가ClubDescription 엔티티 import가 올바르게 추가되었습니다.
70-70: description 필드의 null 가능성 확인
club.getClubDescription()이 null을 반환할 수 있는지 확인이 필요합니다. 만약 null이 가능하다면 API 응답에서 description이 null로 전달되는데, 이것이 의도된 동작인지 확인하세요.다른 필드들은 대부분 null일 때 빈 문자열이나 빈 컬렉션으로 기본값을 설정하는 패턴을 따르고 있습니다.
38-38: 팩토리 메서드 시그니처 변경이 모든 호출부에 반영되었습니다
recommendClubs파라미터 제거에 따른 시그니처 변경이 ClubProfileService의 호출부에 올바르게 적용되었습니다.
| List<String> feeds, | ||
| String introduction, | ||
| String description, | ||
| ClubDescription 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 ClubDetailedResult.java and ClubDescription class
fd -t f "ClubDetailedResult.java" backend/
fd -t f "ClubDescription.java" backend/Repository: Moadong/moadong
Length of output: 192
🏁 Script executed:
# Search for ClubDescription in frontend
fd -t f -name "*.ts" -o -name "*.tsx" frontend/ | head -20
rg -t ts "ClubDescription" frontend/src --max-count=5Repository: Moadong/moadong
Length of output: 1587
🏁 Script executed:
# Read ClubDetailedResult.java to see the structure around line 23
cat -n backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.javaRepository: Moadong/moadong
Length of output: 5157
🏁 Script executed:
# Read ClubDescription backend entity
cat -n backend/src/main/java/moadong/club/entity/ClubDescription.javaRepository: Moadong/moadong
Length of output: 678
🏁 Script executed:
# Read frontend ClubDescription type definition
cat -n frontend/src/types/club.tsRepository: Moadong/moadong
Length of output: 936
🏁 Script executed:
# Check how ClubDetail is used in frontend, particularly the description field
rg -t ts "ClubDetail" frontend/src --max-count=20 -A2Repository: Moadong/moadong
Length of output: 18142
🏁 Script executed:
# Check API response handling for club detail
fd -t f "*.ts" frontend/src/apis | xargs grep -l -i "club.*detail" | head -5Repository: Moadong/moadong
Length of output: 342
🏁 Script executed:
# Search for API calls that fetch club details
rg -t ts "getClubDetail\|fetchClubDetail\|club.*detail" frontend/src/apis --max-count=10Repository: Moadong/moadong
Length of output: 41
🏁 Script executed:
# Check the API client code for club detail fetching
cat -n frontend/src/apis/getClubDetail.tsRepository: Moadong/moadong
Length of output: 692
🏁 Script executed:
# Check the hook that processes the API response
cat -n frontend/src/hooks/queries/club/useGetClubDetail.tsRepository: Moadong/moadong
Length of output: 872
🏁 Script executed:
# Check for any transformation or mapping logic
rg -t ts "description" frontend/src/hooks/queries/club/useGetClubDetail.ts -B3 -A3Repository: Moadong/moadong
Length of output: 41
🏁 Script executed:
# Verify what pages/components actually use the description field
rg -t ts "clubDetail\.description\|description:" frontend/src/pages/ClubDetailPage --max-count=10Repository: Moadong/moadong
Length of output: 41
🏁 Script executed:
# Check IntroduceBox component which likely uses description
fd -t f "IntroduceBox.tsx" frontend/src
cat frontend/src/pages/ClubDetailPage/components/IntroduceBox/IntroduceBox.tsxRepository: Moadong/moadong
Length of output: 1911
🏁 Script executed:
# Final verification: Check if there's any serializer or JSON adapter that might transform ClubDescription
fd -t f "JsonSerializer\|@JsonProperty" backend/src/main/java/moadong/club/payload/dto | head -5
rg -t java "@JsonProperty\|@JsonSerialize" backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.javaRepository: Moadong/moadong
Length of output: 41
🏁 Script executed:
# Check for Jackson annotations in ClubDetailedResult
rg -t java "Jackson\|@Json\|Serializable" backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.javaRepository: Moadong/moadong
Length of output: 41
🏁 Script executed:
# Look for serialization configuration
fd -t f "WebConfig\|JsonConfig\|JacksonConfig" backend/srcRepository: Moadong/moadong
Length of output: 41
🏁 Script executed:
# Find the controller that returns ClubDetailedResult
rg -t java "ClubDetailedResult" backend/src/main/java --max-count=10Repository: Moadong/moadong
Length of output: 895
🏁 Script executed:
# Check the actual controller method that returns the club detail
fd -t f "ClubController.java" backend/srcRepository: Moadong/moadong
Length of output: 41
description 필드 타입 변경으로 인한 API 호환성 문제
description 필드가 String에서 ClubDescription 객체로 변경되었으나, 프론트엔드는 여전히 문자열을 기대합니다. IntroduceBox 컴포넌트가 description을 마크다운 문자열로 렌더링하려 하므로 런타임 에러가 발생합니다.
프론트엔드의 ClubDetail 타입을 업데이트하거나, 백엔드에서 적절한 필드(introDescription 등)를 문자열로 매핑해야 합니다.
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java around
line 23, the description field was changed from String to ClubDescription which
breaks the frontend expecting a markdown string; restore API compatibility by
adding a new String field (e.g., introDescription or descriptionMarkdown) and
populate it from ClubDescription (e.g., description.toMarkdown() or
description.getContent()) in the DTO mapping or factory method so the existing
frontend can continue to receive a plain markdown string; alternatively, if you
prefer not to add a field, revert the type back to String and ensure the service
layer maps ClubDescription -> String before constructing the DTO.
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java
Outdated
Show resolved
Hide resolved
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로 사용하던 코드 리팩토링하였습니다.
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
♻️ Duplicate comments (1)
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (1)
84-85: NullPointerException 위험: getClubDescription() null 체크 필요
club.getClubDescription()이 null인 경우.getFaqs()호출 시 NPE가 발생합니다. 이전 리뷰에서도 동일한 문제가 지적되었습니다.🔎 NPE 방지를 위한 수정 제안
- .faqs(club.getClubDescription().getFaqs() == null ? List.of() - : club.getClubDescription().getFaqs().stream().map(FaqDto::from).toList()) + .faqs(club.getClubDescription() == null || club.getClubDescription().getFaqs() == null + ? List.of() + : club.getClubDescription().getFaqs().stream().map(FaqDto::from).toList())
📜 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 (11)
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/ClubRequestFixture.java
🚧 Files skipped from review as they are similar to previous changes (2)
- backend/src/main/java/moadong/club/entity/ClubAward.java
- backend/src/test/java/moadong/fixture/ClubRequestFixture.java
🧰 Additional context used
🧠 Learnings (7)
📓 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: 548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.196Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
Learnt from: Zepelown
Repo: Moadong/moadong PR: 406
File: backend/src/main/java/moadong/club/entity/ClubApplicationQuestion.java:35-36
Timestamp: 2025-05-15T12:03:57.833Z
Learning: 엔티티 클래스에서 요청 객체(DTO)를 직접 참조하지 않고, 대신 도메인 모델에 해당하는 별도의 클래스를 엔티티 패키지에 정의하여 계층 간 분리를 유지해야 합니다. 이는 Options, QuestionItem과 같은 클래스에도 적용됩니다.
📚 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/dto/ClubDetailedResult.javabackend/src/main/java/moadong/club/entity/ClubDescription.javabackend/src/main/java/moadong/club/payload/request/ClubInfoRequest.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/main/java/moadong/club/entity/Club.javabackend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.javabackend/src/main/java/moadong/club/entity/ClubDescription.javabackend/src/main/java/moadong/club/payload/request/ClubInfoRequest.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/payload/dto/ClubDetailedResult.javabackend/src/main/java/moadong/club/entity/ClubIdealCandidate.java
📚 Learning: 2025-07-19T05:05:10.196Z
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 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
Applied to files:
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.javabackend/src/main/java/moadong/club/entity/ClubDescription.java
📚 Learning: 2025-05-15T12:03:57.356Z
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`와 같은 별도의 클래스를 정의하고 사용해야 합니다. 이렇게 하면 요청 객체 변경이 데이터베이스 스키마나 엔티티 계층에 영향을 미치지 않습니다.
Applied to files:
backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java
📚 Learning: 2025-05-15T12:03:57.833Z
Learnt from: Zepelown
Repo: Moadong/moadong PR: 406
File: backend/src/main/java/moadong/club/entity/ClubApplicationQuestion.java:35-36
Timestamp: 2025-05-15T12:03:57.833Z
Learning: 엔티티 클래스에서 요청 객체(DTO)를 직접 참조하지 않고, 대신 도메인 모델에 해당하는 별도의 클래스를 엔티티 패키지에 정의하여 계층 간 분리를 유지해야 합니다. 이는 Options, QuestionItem과 같은 클래스에도 적용됩니다.
Applied to files:
backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java
🧬 Code graph analysis (3)
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/entity/ClubDescription.java (3)
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)
backend/src/main/java/moadong/club/entity/ClubIdealCandidate.java (4)
backend/src/main/java/moadong/user/entity/User.java (1)
Builder(21-99)backend/src/main/java/moadong/club/entity/ClubAward.java (1)
Getter(10-19)backend/src/main/java/moadong/club/entity/ClubDescription.java (1)
Getter(10-27)backend/src/main/java/moadong/club/entity/Faq.java (1)
NoArgsConstructor(7-13)
🔇 Additional comments (10)
backend/src/main/java/moadong/club/payload/dto/ClubAwardDto.java (1)
8-25: LGTM! DTO 구조가 적절하게 설계되었습니다.엔티티와 DTO 간의 변환 메서드(
from,toEntity)가 깔끔하게 구현되어 있고,@Size제약 조건도 적절히 적용되어 있습니다.backend/src/main/java/moadong/club/payload/request/ClubInfoRequest.java (1)
4-4: LGTM! DTO 계층 분리가 올바르게 적용되었습니다.
ClubDescriptionDto를 사용하여 엔티티(ClubDescription)와 요청 객체 간의 의존성을 적절히 분리했습니다. 이전 리뷰에서 지적된 계층 간 의존성 문제가 해결되었습니다. Based on learnings, 엔티티와 DTO 분리 원칙을 준수하고 있습니다.Also applies to: 20-20
backend/src/main/java/moadong/club/payload/dto/ClubIdealCandidateDto.java (1)
8-25: LGTM! ClubAwardDto와 일관된 패턴으로 구현되었습니다.
from()과toEntity()메서드가 적절하게 구현되어 있으며, null 체크도 포함되어 있습니다.backend/src/main/java/moadong/club/entity/ClubIdealCandidate.java (1)
10-19: LGTM! 엔티티 구조가 적절합니다.Lombok 어노테이션을 사용하여 보일러플레이트 코드를 줄였고,
ClubAward엔티티와 일관된 패턴을 따르고 있습니다.backend/src/main/java/moadong/club/entity/Club.java (1)
49-50: LGTM! 생성자에서 clubDescription 초기화가 잘 되어 있습니다.모든 생성자에서
clubDescription을 빈 빌더로 초기화하여 null 상태를 방지하고 있습니다. 이는 다른 필드들(clubRecruitmentInformation)과 일관된 패턴입니다.Also applies to: 54-61, 63-71, 73-82, 84-92
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (1)
21-21: LGTM! DTO 타입 변경이 적절합니다.
description과faqs필드가 DTO 타입으로 변경되었고,ClubDescriptionDto.from()은 null을 안전하게 처리합니다.Also applies to: 32-32, 68-68
backend/src/main/java/moadong/club/entity/ClubDescription.java (1)
10-27: LGTM! 명세에 맞는 복합 객체 구조가 잘 설계되었습니다.PR 목표에 따라
description을 단순 문자열에서 복합 객체로 변경했습니다.ClubAward,ClubIdealCandidate,Faq등 중첩 엔티티 참조가 적절하게 구성되어 있습니다.backend/src/main/java/moadong/club/payload/dto/ClubDescriptionDto.java (1)
9-50: LGTM! DTO 매핑 로직이 잘 구현되었습니다.
from()과toEntity()메서드 모두 null 안전 처리가 적절히 적용되어 있고,@Valid어노테이션으로 중첩 DTO의 유효성 검사가 전파됩니다. 컬렉션 타입에 대한 스트림 매핑도 깔끔합니다.backend/src/main/java/moadong/club/payload/dto/FaqDto.java (2)
1-4: LGTM!패키지 구조와 import가 적절합니다. DTO가 엔티티를 참조하는 것은 올바른 의존성 방향입니다.
13-16: LGTM!엔티티에서 DTO로의 변환 로직이 명확하고, null 처리도 적절합니다.
| this.clubRecruitmentInformation.update(request); | ||
| this.description = request.description(); | ||
| this.faqs = request.faqs(); | ||
| 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.
NullPointerException 위험: description null 체크 필요
request.description()이 null인 경우 toEntity() 호출 시 NPE가 발생합니다. 방어적 null 체크를 추가하세요.
🔎 NPE 방지를 위한 수정 제안
- this.clubDescription = request.description().toEntity();
+ this.clubDescription = request.description() == null
+ ? ClubDescription.builder().build()
+ : request.description().toEntity();📝 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.
| this.clubDescription = request.description().toEntity(); | |
| this.clubDescription = request.description() == null | |
| ? ClubDescription.builder().build() | |
| : request.description().toEntity(); |
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/entity/Club.java around line 104, calling
request.description().toEntity() can throw a NullPointerException if
request.description() is null; add a defensive null check and set
this.clubDescription to either null or a default/empty description entity when
request.description() is null (e.g., use a conditional/Optional to call
toEntity() only when description is non-null), ensuring the assignment never
invokes toEntity() on null.
| public record FaqDto( | ||
| @Size(max = 100) | ||
| String question, | ||
|
|
||
| @Size(max = 500) | ||
| String answer | ||
| ) { |
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.
🛠️ Refactor suggestion | 🟠 Major
필수 필드 검증 추가를 권장합니다.
FAQ의 질문과 답변은 사용자에게 표시되는 중요한 콘텐츠이므로, @NotBlank 검증을 추가하는 것이 좋습니다. 현재 @Size만으로는 null이나 빈 문자열을 허용하게 됩니다.
🔎 제안하는 수정사항
+import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import moadong.club.entity.Faq;
public record FaqDto(
+ @NotBlank
@Size(max = 100)
String question,
+ @NotBlank
@Size(max = 500)
String answer
) {📝 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.
| public record FaqDto( | |
| @Size(max = 100) | |
| String question, | |
| @Size(max = 500) | |
| String answer | |
| ) { | |
| import jakarta.validation.constraints.NotBlank; | |
| import jakarta.validation.constraints.Size; | |
| import moadong.club.entity.Faq; | |
| public record FaqDto( | |
| @NotBlank | |
| @Size(max = 100) | |
| String question, | |
| @NotBlank | |
| @Size(max = 500) | |
| String answer | |
| ) { |
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/payload/dto/FaqDto.java around lines 6 to
12, the DTO currently only uses @Size which allows null or blank values; add
@NotBlank to both question and answer to enforce non-null/non-empty input (place
@NotBlank before @Size), and add the corresponding import for
javax.validation.constraints.NotBlank so validation fails for null or blank FAQ
fields.
| public Faq toEntity() { | ||
| return new Faq(question, answer); | ||
| } |
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:
fd -e java -t f "Faq.java" | head -5Repository: Moadong/moadong
Length of output: 109
🏁 Script executed:
cat -n backend/src/main/java/moadong/club/entity/Faq.javaRepository: Moadong/moadong
Length of output: 377
🏁 Script executed:
cat -n backend/src/main/java/moadong/club/payload/dto/FaqDto.javaRepository: Moadong/moadong
Length of output: 656
🏁 Script executed:
# 검증을 위해 FaqDto 컨텍스트 재확인
cd backend && find . -name "*.java" -type f | xargs rg "FaqDto" | head -20Repository: Moadong/moadong
Length of output: 1015
FaqDto의 question과 answer 필드에 null 체크 추가
toEntity() 메서드가 question 및 answer 필드의 null 검증 없이 엔티티를 생성합니다. FaqDto의 필드에는 @Size 어노테이션만 있고 @NotBlank나 @NotNull이 없으므로 null 값이 전달될 수 있습니다.
question과 answer가 null이 될 수 없다면, FaqDto 필드에 @NotBlank 어노테이션을 추가하거나 toEntity() 메서드에서 null 체크를 수행해야 합니다.
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/payload/dto/FaqDto.java around lines
18-20, the toEntity() method constructs a Faq without validating question and
answer which may be null; update the DTO by adding @NotBlank to the question and
answer fields to enforce non-null/non-empty at validation time, and also add
defensive null checks in toEntity() that throw an IllegalArgumentException (or
IllegalStateException) with a clear message if question or answer is null/blank
before constructing and returning the Faq entity.
61872ed
into
refactor/#960-recruitment-api-update-MOA-452
#️⃣연관된 이슈
#962
📝작업 내용
동아리 설명을 바뀐 명세에 맞게 수정하였습니다.
PUT /api/club/info또한 외부에있던
faqs필드를 description 내부로 이동시켰습니다중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
릴리스 노트
Refactor
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.