-
Notifications
You must be signed in to change notification settings - Fork 2
[feature] 모집글 수정 시 최근 업데이트 일자를 보여준다 #898
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] 모집글 수정 시 최근 업데이트 일자를 보여준다 #898
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | 변경 내용 |
|---|---|
엔티티: 모집정보 필드 추가backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java |
private LocalDateTime lastModifiedDate 필드 추가 및 public void updateLastModifiedDate() 메서드 추가(현재 시간으로 설정). |
서비스: 업데이트 시각 갱신 호출backend/src/main/java/moadong/club/service/ClubProfileService.java |
updateClubRecruitmentInfo 내부에서 Recruitment 상태 계산 후 club.getClubRecruitmentInformation().updateLastModifiedDate() 호출 추가. |
DTO: 출력 필드 추가backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java |
응답 필드로 String lastModifiedDate 추가; of(...)/빌더에서 lastModifiedDate를 "yyyy.MM.dd HH:mm" 형식으로 포맷해 설정하도록 추가. |
테스트: 단위 테스트 추가backend/src/test/java/moadong/club/service/ClubProfileServiceDateTest.java |
모집글 업데이트 시 lastModifiedDate가 설정되는지 검증하는 테스트 클래스 추가(리포지토리 모킹, RecruitmentStateCalculator 정적 모킹 사용). |
테스트 픽스처 확장backend/src/test/java/moadong/fixture/ClubRequestFixture.java |
public static ClubRecruitmentInfoUpdateRequest defaultRequest() 헬퍼 메서드 추가 (LocalDateTime 사용). |
소소한 엔티티 변경backend/src/main/java/moadong/club/entity/Club.java |
@Version 어노테이션 제거 및 불필요한 임포트/포맷 정리(버전 필드는 유지되나 어노테이션 제거). |
Sequence Diagram(s)
sequenceDiagram
participant Client
participant ClubProfileService
participant RecruitmentStateCalculator as Calculator
participant ClubEntity as Club
participant Repo as ClubRepository
Client->>ClubProfileService: updateClubRecruitmentInfo(request, user)
ClubProfileService->>Calculator: RecruitmentStateCalculator.calculate(club, request)
ClubProfileService->>Club: club.updateRecruitmentInfo(requestData)
ClubProfileService->>Club: club.getClubRecruitmentInformation().updateLastModifiedDate()
ClubProfileService->>Repo: clubRepository.save(club)
Repo-->>ClubProfileService: savedClub
ClubProfileService-->>Client: 응답 (업데이트 완료)
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
- 집중 검토 포인트:
lastModifiedDate의 시간대(Zone) 일관성 및 포맷 위치(DTO vs 서비스) 확인- 영속성 저장 흐름(변경감지 vs 명시적 save)에서 값이 정상 저장되는지 검증 (
ClubRepository관련) - 시간 민감 테스트 안정성(±1초 검사)이 CI 환경에서 신뢰할 수 있는지 확인
Club.java에서@Version제거가 낙관적 락 동작에 미치는 영향
Possibly related issues
- MOA-393: 모집글 수정 시 최근 업데이트 일자를 보여준다 — 본 PR은 모집글 수정 시점의 최근 업데이트 일자 저장·노출을 구현함
- [feature] MOA-393 모집글 수정 시 최근 업데이트 일자를 보여준다 #893 — UI에 노출할 last-updated 구현 관련 이슈와 기능적으로 일치함
Possibly related PRs
- [feature] 상시 모집 상태 추가 및 모집 정보 변경 시 반영되도록 변경 #485 — 동일 서비스 메서드(
updateClubRecruitmentInfo) 관련 변경으로 연관성 높음 - [Release] BE v1.0.6 배포 #675 —
ClubDetailedResult관련 DTO 변경과 중첩될 수 있음 - [feature] 액세스 토큰 및 리프레시 토큰의 만료 시간을 수정하고, 로그인, 관리자 계정 관련의 동시성 문제를 해결한다 #713 —
Club엔티티의 버전/영속성 관련 변경(어노테이션 변경)과 충돌 가능성 존재
Suggested labels
✅ Test
Suggested reviewers
- PororoAndFriends
- lepitaaar
- 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 | The PR title clearly describes the main feature: displaying the last update date when recruitment posts are edited. |
| Linked Issues check | ✅ Passed | The PR implements the core requirement from MOA-393: adding and displaying the last modified date for recruitment information when updated. |
| Out of Scope Changes check | ✅ Passed | All changes are directly related to the linked issue MOA-393; no out-of-scope modifications detected in the codebase. |
✨ 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
feature/#893-show-last-updated-on-post-edit-MOA-393
📜 Recent review details
Configuration used: CodeRabbit 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/test/java/moadong/club/service/ClubProfileServiceDateTest.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- backend/src/test/java/moadong/club/service/ClubProfileServiceDateTest.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 Results69 tests 66 ✅ 15s ⏱️ Results for commit 8898972. ♻️ 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: 1
🧹 Nitpick comments (4)
backend/src/main/java/moadong/club/service/ClubProfileService.java (1)
38-50:@Transactional어노테이션 누락
updateClubInfo메서드(라인 30)에는@Transactional이 있지만,updateClubRecruitmentInfo에는 없습니다. 일관성을 위해 추가를 권장합니다.또한,
LocalDateTime.now()는 시스템 기본 타임존을 사용하는 반면,RecruitmentStateCalculator는ZonedDateTime.now(ZoneId.of("Asia/Seoul"))을 사용합니다. 타임존 일관성을 위해 명시적으로 지정하는 것이 좋습니다.+ @Transactional public void updateClubRecruitmentInfo(ClubRecruitmentInfoUpdateRequest request, CustomUserDetails user) { Club club = clubRepository.findClubByUserId(user.getId()) .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND)); club.update(request); RecruitmentStateCalculator.calculate( club, club.getClubRecruitmentInformation().getRecruitmentStart(), club.getClubRecruitmentInformation().getRecruitmentEnd() ); - club.setLastModified(LocalDateTime.now()); + club.setLastModified(LocalDateTime.now(java.time.ZoneId.of("Asia/Seoul"))); clubRepository.save(club); }backend/src/main/java/moadong/club/entity/Club.java (1)
54-56: 중복된@Getter어노테이션 제거클래스 레벨에 이미
@Getter가 선언되어 있으므로(라인 30), 필드 레벨의@Getter는 중복입니다.@Setter - @Getter private LocalDateTime lastModified;backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (1)
40-48:DateTimeFormatter중복 생성동일한 패턴(
"yyyy.MM.dd HH:mm")으로DateTimeFormatter가 두 번 생성됩니다 (라인 40, 46). 상수로 추출하면 코드 중복을 줄이고 성능을 개선할 수 있습니다.+ private static final DateTimeFormatter DATE_TIME_FORMATTER = + DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm"); + public static ClubDetailedResult of(Club club, List<ClubSearchResult> recommendClubs) { String period = "미정"; ClubRecruitmentInformation clubRecruitmentInformation = club.getClubRecruitmentInformation(); if (clubRecruitmentInformation.hasRecruitmentPeriod()) { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm"); - period = clubRecruitmentInformation.getRecruitmentStart().format(formatter) + " ~ " - + clubRecruitmentInformation.getRecruitmentEnd().format(formatter); + period = clubRecruitmentInformation.getRecruitmentStart().format(DATE_TIME_FORMATTER) + " ~ " + + clubRecruitmentInformation.getRecruitmentEnd().format(DATE_TIME_FORMATTER); } String lastModified = ""; if (club.getLastModified() != null) { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm"); - lastModified = club.getLastModified().format(formatter); + lastModified = club.getLastModified().format(DATE_TIME_FORMATTER); }backend/src/test/java/moadong/club/service/ClubProfileServiceDateTest.java (1)
59-68:clubRepository.save()호출 검증 추가 권장
lastModified가 설정된 후 실제로 저장되는지 검증하면 테스트 신뢰성이 높아집니다.//WHEN clubProfileService.updateClubRecruitmentInfo(request, customUserDetails); //THEN assertNotNull(club.getLastModified()); //1초 전후 차이로 살펴보기 LocalDateTime now = LocalDateTime.now(); assertTrue(club.getLastModified().isAfter(now.minusSeconds(1))); assertTrue(club.getLastModified().isBefore(now.plusSeconds(1))); + + // 저장 호출 검증 + org.mockito.Mockito.verify(clubRepository).save(club); }
📜 Review details
Configuration used: CodeRabbit 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 (5)
backend/src/main/java/moadong/club/entity/Club.java(2 hunks)backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java(3 hunks)backend/src/main/java/moadong/club/service/ClubProfileService.java(2 hunks)backend/src/test/java/moadong/club/service/ClubProfileServiceDateTest.java(1 hunks)backend/src/test/java/moadong/fixture/ClubRequestFixture.java(2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 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.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/service/ClubProfileService.javabackend/src/main/java/moadong/club/entity/Club.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/service/ClubProfileService.java
🧬 Code graph analysis (1)
backend/src/test/java/moadong/club/service/ClubProfileServiceDateTest.java (4)
backend/src/main/java/moadong/club/util/RecruitmentStateCalculator.java (1)
RecruitmentStateCalculator(15-78)backend/src/test/java/moadong/fixture/ClubRequestFixture.java (1)
ClubRequestFixture(12-64)backend/src/test/java/moadong/fixture/UserFixture.java (1)
UserFixture(9-33)backend/src/test/java/moadong/unit/club/ClubProfileServiceTest.java (1)
UnitTest(23-57)
⏰ 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 (1)
backend/src/test/java/moadong/fixture/ClubRequestFixture.java (1)
27-36: LGTM!테스트 픽스처로서 적절하게 구현되었습니다.
defaultRequest()메서드가 테스트에서 일관된 요청 객체 생성을 돕습니다.
backend/src/test/java/moadong/club/service/ClubProfileServiceDateTest.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
backend/src/main/java/moadong/club/entity/Club.java (2)
54-55: public setter 노출을 제한하여 캡슐화를 개선하세요.현재
@Setter어노테이션으로 인해setLastModified(LocalDateTime)public 메서드가 생성되어, 외부 코드에서 임의로 타임스탬프를 수정할 수 있습니다. 이는 다음과 같은 문제를 야기할 수 있습니다:
- 비즈니스 로직과 무관하게 타임스탬프가 변경될 수 있음
- 데이터 무결성 보장이 어려움
다음 방법 중 하나를 고려해보세요:
방법 1 (권장):
@Setter를 제거하고 엔티티의update()메서드 내에서 타임스탬프를 직접 설정- @Setter private LocalDateTime lastModified; public void update(ClubRecruitmentInfoUpdateRequest request) { clubRecruitmentInformation.updateDescription(request); + this.lastModified = LocalDateTime.now(); }방법 2: Spring Data MongoDB의 auditing 기능 활용 (모든 업데이트에 반영하려는 경우)
- @Setter + @LastModifiedDate private LocalDateTime lastModified;Note: 현재 구현에서는 모집 정보 수정 시에만 업데이트하려는 의도로 보이므로 방법 1이 더 적합합니다.
57-91: 생성자에서 lastModified 초기화를 고려하세요.현재 모든 생성자에서
lastModified필드가 초기화되지 않아 새로운 인스턴스는 null 값을 가집니다. 모집 정보 업데이트 추적이 목적이라면 현재 구현도 충분하지만, 일관성을 위해 다음을 고려할 수 있습니다:
- 생성 시점의 타임스탬프로 초기화 (필요한 경우)
- 또는 null을 명시적으로 문서화
예시 (선택사항):
public Club() { this.name = ""; this.category = ""; this.division = ""; this.state = ClubState.UNAVAILABLE; this.clubRecruitmentInformation = ClubRecruitmentInformation.builder().build(); + this.lastModified = null; // 명시적으로 null 표시, 또는 LocalDateTime.now()로 초기화 }
📜 Review details
Configuration used: CodeRabbit 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 (2)
backend/src/main/java/moadong/club/entity/Club.java(2 hunks)backend/src/test/java/moadong/club/service/ClubProfileServiceDateTest.java(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- backend/src/test/java/moadong/club/service/ClubProfileServiceDateTest.java
🧰 Additional context used
🧠 Learnings (1)
📚 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.java
🔇 Additional comments (1)
backend/src/main/java/moadong/club/entity/Club.java (1)
3-3: LGTM!새로운
lastModified필드를 위한 필수 import가 올바르게 추가되었습니다.Also applies to: 13-13
lepitaaar
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.
수고하셨습니다! 기능개발하면서 테스트코드까지 작성하신거 좋네요~ 아래 리뷰들 확인해주시면 감사하겠습니다
backend/src/test/java/moadong/club/service/ClubProfileServiceDateTest.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java (1)
70-70: lastModifiedDate 시간대 처리 일관성은 선택적으로 정리해볼 수 있습니다
updateLastModifiedDate()에서LocalDateTime.now()를 그대로 사용하는 것은 기능적으로 문제 없지만, 이 엔티티가 모집기간에 대해서는ZoneId.of("Asia/Seoul")를 명시적으로 사용하는 만큼, 최근 수정 일자도 동일한 기준(예: 서울 타임존 기준의 now 혹은 UTC 고정)으로 맞춰두면 장기적으로 혼동을 줄일 수 있습니다.예를 들어 KST 고정이 의도라면, 다음처럼 구현하는 것도 한 가지 방법입니다 (참고용):
public void updateLastModifiedDate() { setLastModifiedDate(ZonedDateTime.now(ZoneId.of("Asia/Seoul")).toLocalDateTime()); }지금 구현을 그대로 두어도 동작에는 문제 없고, 운영 환경 타임존이 고정이라면 필수 변경은 아닙니다.
Also applies to: 128-134
backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java (1)
32-32: 최근 수정 일자 포맷 및 매핑이 요구사항에 잘 맞습니다
ClubDetailedResult에lastModifiedDate필드를 추가하고,ClubRecruitmentInformation.lastModifiedDate가 있을 때만"yyyy.MM.dd HH:mm"포맷의 문자열로 변환해서 내려주는 흐름이 자연스럽고, null일 때 빈 문자열로 처리하는 방식도 기존 DTO 패턴과 일관적입니다. 빌더에.lastModifiedDate(lastModifiedDate)로 잘 연결된 것도 확인했습니다.추가로, 선택사항이지만
DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm")가recruitmentPeriod와lastModifiedDate에서 반복되고 있으니, 공통 상수로 추출해두면 향후 포맷 변경 시 관리가 조금 더 수월해질 것 같습니다.Also applies to: 45-49, 84-84
📜 Review details
Configuration used: CodeRabbit 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 (4)
backend/src/main/java/moadong/club/entity/Club.java(0 hunks)backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java(3 hunks)backend/src/main/java/moadong/club/payload/dto/ClubDetailedResult.java(3 hunks)backend/src/main/java/moadong/club/service/ClubProfileService.java(2 hunks)
💤 Files with no reviewable changes (1)
- backend/src/main/java/moadong/club/entity/Club.java
🧰 Additional context used
🧠 Learnings (4)
📚 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.java
📚 Learning: 2025-03-19T05:18:07.818Z
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')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Applied to files:
backend/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/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/service/ClubProfileService.java
🔇 Additional comments (1)
backend/src/main/java/moadong/club/service/ClubProfileService.java (1)
3-3: 모집정보 수정 시점에 lastModifiedDate 갱신 위치 적절합니다
updateClubRecruitmentInfo안에서club.update(request)이후에club.getClubRecruitmentInformation().updateLastModifiedDate()를 호출해서, 모집정보가 수정될 때마다 일관되게 최근 수정 일자가 갱신되도록 연결된 점 좋습니다. 기존 리뷰 코멘트에서 제안된 형태대로 잘 반영된 것으로 보이며, 서비스 레이어 책임과도 잘 맞습니다.Also applies to: 48-48
lepitaaar
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.
수정 수고하셨습니다
#️⃣연관된 이슈
📝작업 내용
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
새로운 기능
테스트
✏️ Tip: You can customize this high-level summary in your review settings.