-
Notifications
You must be signed in to change notification settings - Fork 3
[feature]동아리 정보 이력 저장 로직 추가 #1037
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]동아리 정보 이력 저장 로직 추가 #1037
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | 변경 요약 |
|---|---|
의존성 \backend/build.gradle`` |
org.javers:javers-spring-boot-starter-mongo:7.10.0 의존성 추가 |
설정 빈 \backend/src/main/java/moadong/global/config/JaversConfig.java`` |
JaVers AuthorProvider 및 MongoRepository 빈 추가 (JaVers+Mongo 설정) |
엔티티 변경 \backend/src/main/java/moadong/club/entity/Club.java`, `backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java`` |
여러 필드에 @DiffIgnore 추가(socialLinks, logo, cover, 등). ClubRecruitmentInformation의 id 필드 주석 처리 |
서비스/레포지토리 통합 \backend/src/main/java/moadong/club/service/ClubProfileService.java`, `backend/src/main/java/moadong/club/repository/ClubRepository.java`` |
Javers 주입 및 엔티티 저장 후 javers.commit(author, entity) 호출 추가; 레포지토리에 JaVers 어노테이션 import 추가 |
이력 조회 API 및 DTO \backend/src/main/java/moadong/log/club/service/ClubHistoryService.java`, `backend/src/main/java/moadong/log/club/controller/ClubHistoryController.java`, `backend/src/main/java/moadong/log/club/payload/response/ClubHistoryResponse.java`` |
JaVers 쿼리로 Club 변경 이력(Shadow) 조회, ClubHistoryResponse 매핑, GET /api/clubs/{clubId}/histories 엔드포인트 추가 |
테스트·픽스처 변경 \backend/src/test/java/moadong/fixture/ClubFixture.java`, `backend/src/test/java/moadong/club/service/ClubProfileServiceDateTest.java`, `backend/src/test/java/moadong/unit/club/ClubProfileServiceTest.java`` |
Fixture에서 getId() 스텁 제거; 테스트 클래스들에 @Mock Javers javers 추가 |
Sequence Diagram(s)
sequenceDiagram
participant Client as "Client"
participant Controller as "ClubHistoryController"
participant HistoryService as "ClubHistoryService"
participant ProfileService as "ClubProfileService"
participant Javers as "JaVers"
participant MongoDB as "MongoDB"
Client->>ProfileService: updateClubInfo/updateClubRecruitmentInfo(data)
ProfileService->>ProfileService: 엔티티 필드 수정 및 저장
ProfileService->>Javers: javers.commit(author, club)
Javers->>MongoDB: 감사 레코드 저장
Javers-->>ProfileService: commit 완료
Note over Client,MongoDB: --- 이력 조회 ---
Client->>Controller: GET /api/clubs/{clubId}/histories
Controller->>HistoryService: getClubHistories(clubId)
HistoryService->>Javers: query shadows by club id
Javers->>MongoDB: Shadow 조회
MongoDB-->>Javers: Shadow 결과
HistoryService->>HistoryService: Shadow -> ClubHistoryResponse 변환
HistoryService-->>Controller: List<ClubHistoryResponse>
Controller-->>Client: 200 OK (이력 목록)
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related issues
- MOA-515 — 이 PR은 동아리 정보 이력 저장 로직(JaVers 기반) 추가 요구사항을 구현합니다.
- [feature] MOA-515 동아리 정보 이력 저장 로직 추가 #1036 — 클럽 이력·감사 기능 추가와 직접적으로 연관됩니다.
Possibly related PRs
- [feature] 상시 모집 상태 추가 및 모집 정보 변경 시 반영되도록 변경 #485 —
ClubProfileService.updateClubRecruitmentInfo관련 변경이 겹칩니다. - [feature] cover image 업로드 및 삭제 기능 구현 #537 —
ClubRecruitmentInformation.cover관련 변경과 중복 가능성이 있습니다. - [Release] BE v1.0.6 배포 #675 —
ClubProfileService관련 기능 확장과 코드 레벨 연관이 있습니다.
Suggested labels
✨ Feature, 💾 BE
Suggested reviewers
- lepitaaar
- oesnuj
- alsdddk
🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | PR 제목은 동아리 정보 이력 저장 로직을 추가하는 주요 변경사항을 명확하게 설명하고 있으며, 코드 변경사항과 일치합니다. |
| Linked Issues check | ✅ Passed | Javers 라이브러리 통합, 변경 이력 저장 및 조회 API 구현 등 연관 이슈의 모든 주요 요구사항이 충족되었습니다. |
| Out of Scope Changes check | ✅ Passed | 모든 변경사항이 동아리 정보 이력 저장 기능 구현과 직접 관련되어 있으며 범위 밖의 변경은 없습니다. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing touches
- 📝 Generate docstrings
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 Results72 tests 72 ✅ 18s ⏱️ Results for commit 5746e9d. ♻️ 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: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
backend/src/main/java/moadong/club/repository/ClubRepository.java (1)
7-12: 미사용 import를 제거하거나 어노테이션을 적용하세요.
@JaversSpringDataAuditableimport가 추가되었지만 실제로 repository에 적용되지 않았습니다. 현재ClubProfileService에서 수동으로javers.commit()을 호출하는 방식을 사용하고 있다면 이 import는 불필요합니다.두 가지 선택지가 있습니다:
- 수동 커밋 방식 유지 → import 제거
- 자동 감사 방식 사용 →
@JaversSpringDataAuditable어노테이션을 repository에 적용 (단, 이 경우ClubProfileService의 수동 커밋 코드는 불필요)🔧 옵션 1: 미사용 import 제거
-import org.javers.spring.annotation.JaversSpringDataAuditable;🔧 옵션 2: 자동 감사 방식 적용
`@Repository` +@JaversSpringDataAuditable public interface ClubRepository extends MongoRepository<Club, String> {backend/src/main/java/moadong/club/service/ClubProfileService.java (1)
38-51:@Transactional어노테이션이 누락되었습니다.
updateClubInfo메서드(Line 29)에는@Transactional어노테이션이 있지만,updateClubRecruitmentInfo메서드에는 없습니다. 일관성 있는 트랜잭션 처리를 위해 추가하는 것이 좋습니다.🔧 제안된 수정
+ `@Transactional` public void updateClubRecruitmentInfo(ClubRecruitmentInfoUpdateRequest request, CustomUserDetails user) {
🤖 Fix all issues with AI agents
In `@backend/src/main/java/moadong/global/config/JaversConfig.java`:
- Around line 15-20: The AuthorProvider bean can NPE when
SecurityContextHolder.getContext().getAuthentication() is null; update the
authorProvider() method (AuthorProvider bean) to null- and unauthenticated-safe
by retrieving the Authentication into a local variable, check authentication !=
null and authentication.isAuthenticated() (and optionally that getName() is
non-null), and return authentication.getName() when valid, otherwise return a
safe fallback string like "anonymous" or "system" so getName() is never invoked
on a null Authentication.
In
`@backend/src/main/java/moadong/log/club/controller/ClubHistoryController.java`:
- Around line 20-24: Add method- or class-level access control to prevent
unauthenticated access to audit data: annotate the ClubHistoryController class
or its getClubHistories(...) method with `@PreAuthorize`("isAuthenticated()") so
only authenticated users can call the endpoint; ensure the security
method-authorization support is enabled in the application (consistent with how
UserController, ClubImageController, and ClubApplyAdminController are
protected).
In
`@backend/src/main/java/moadong/log/club/payload/response/ClubHistoryResponse.java`:
- Around line 39-41: ClubHistoryResponse.from(Shadow<Club> shadow) calls
shadow.get() which can be null for delete commits; add a null check in that
method and handle deleted snapshots explicitly (e.g., return a
ClubHistoryResponse indicating deletion built from shadow.getCommitMetadata(),
or return null/Optional if that fits your API), ensuring you don't dereference
the null Club; update logic around ClubHistoryResponse.from, Shadow.get(), and
usage sites to handle the deletion response consistently.
🧹 Nitpick comments (7)
backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java (1)
30-31: 주석 처리된 코드를 제거하세요.주석 처리된
@Id필드는 데드 코드입니다. 코드베이스에 주석 처리된 코드를 남겨두는 것보다 완전히 제거하는 것이 좋습니다.🔧 제안된 수정
-// `@Id` -// private String id; -backend/src/main/java/moadong/global/config/JaversConfig.java (1)
11-11: 사용되지 않는 import 제거 필요
PlatformTransactionManager가 import 되었지만 클래스 내에서 사용되지 않습니다.♻️ 수정 제안
-import org.springframework.transaction.PlatformTransactionManager;backend/src/main/java/moadong/log/club/service/ClubHistoryService.java (3)
11-11: 사용되지 않는 import 제거
@Transactional어노테이션과Collectors클래스가 import 되었지만 사용되지 않습니다.♻️ 수정 제안
-import org.springframework.transaction.annotation.Transactional;-import java.util.stream.Collectors;Also applies to: 14-14
22-33: clubId 유효성 검증 누락
clubId가 null이거나 빈 문자열인 경우에 대한 검증이 없습니다. 또한 존재하지 않는 clubId에 대해 빈 리스트를 반환하는 것이 의도된 동작인지 확인이 필요합니다.♻️ 입력 검증 추가 제안
public List<ClubHistoryResponse> getClubHistories(String clubId) { + if (clubId == null || clubId.isBlank()) { + throw new IllegalArgumentException("clubId must not be null or empty"); + } + JqlQuery query = QueryBuilder.byInstanceId(clubId, Club.class) .withChildValueObjects() .limit(20) .build();
25-25: 하드코딩된 limit 값
limit(20)이 하드코딩되어 있습니다. 추후 페이지네이션 지원이나 설정 변경이 필요할 수 있으므로, 상수나 설정값으로 분리하는 것을 고려해 보세요.backend/src/main/java/moadong/log/club/payload/response/ClubHistoryResponse.java (1)
25-25: Enum 변환 방식 불일치
state필드는String으로 변환하면서(Line 56),recruitmentStatus는ClubRecruitmentStatusenum 그대로 노출하고 있습니다. API 응답의 일관성을 위해 둘 다 같은 방식으로 처리하는 것이 좋습니다.♻️ 일관된 처리 제안
- private ClubRecruitmentStatus recruitmentStatus; + private String recruitmentStatus;- .recruitmentStatus(hasInfo ? recruitmentInfo.getClubRecruitmentStatus() : null) + .recruitmentStatus(hasInfo && recruitmentInfo.getClubRecruitmentStatus() != null + ? recruitmentInfo.getClubRecruitmentStatus().name() : null)Also applies to: 31-31
backend/src/main/java/moadong/log/club/controller/ClubHistoryController.java (1)
14-17: Swagger/OpenAPI 문서화 추가 권장프로젝트의 모든 다른 컨트롤러들이
@Tag와@Operation어노테이션을 사용하고 있으므로, 일관성을 위해 이 컨트롤러에도 추가하기를 권장합니다.
📜 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 (9)
backend/build.gradlebackend/src/main/java/moadong/club/entity/Club.javabackend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.javabackend/src/main/java/moadong/club/repository/ClubRepository.javabackend/src/main/java/moadong/club/service/ClubProfileService.javabackend/src/main/java/moadong/global/config/JaversConfig.javabackend/src/main/java/moadong/log/club/controller/ClubHistoryController.javabackend/src/main/java/moadong/log/club/payload/response/ClubHistoryResponse.javabackend/src/main/java/moadong/log/club/service/ClubHistoryService.java
🧰 Additional context used
🧠 Learnings (4)
📚 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/log/club/service/ClubHistoryService.javabackend/src/main/java/moadong/club/service/ClubProfileService.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/ClubRecruitmentInformation.javabackend/src/main/java/moadong/club/service/ClubProfileService.java
📚 Learning: 2025-11-08T18:45:09.295Z
Learnt from: lepitaaar
Repo: Moadong/moadong PR: 809
File: backend/src/main/java/moadong/global/config/RabbitMQConfig.java:13-42
Timestamp: 2025-11-08T18:45:09.295Z
Learning: The Moadong project uses manually-configured RabbitMQ infrastructure (queues, exchanges, bindings) on the server side rather than declaring them as Spring beans in RabbitMQConfig. The configuration class only provides ConnectionFactory and RabbitTemplate beans.
Applied to files:
backend/src/main/java/moadong/global/config/JaversConfig.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
🪛 GitHub Actions: PR Test
backend/src/main/java/moadong/log/club/payload/response/ClubHistoryResponse.java
[error] 1-1: Gradle test execution failed. Command './gradlew --info test' exited with non-zero status.
backend/src/main/java/moadong/log/club/service/ClubHistoryService.java
[error] 1-1: Gradle test execution failed. Command './gradlew --info test' exited with non-zero status.
backend/build.gradle
[error] 1-1: Gradle test execution failed. Command './gradlew --info test' exited with non-zero status.
backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java
[error] 1-1: Gradle test execution failed. Command './gradlew --info test' exited with non-zero status.
backend/src/main/java/moadong/club/repository/ClubRepository.java
[error] 1-1: Gradle test execution failed. Command './gradlew --info test' exited with non-zero status.
backend/src/main/java/moadong/log/club/controller/ClubHistoryController.java
[error] 1-1: Gradle test execution failed. Command './gradlew --info test' exited with non-zero status.
backend/src/main/java/moadong/global/config/JaversConfig.java
[error] 1-1: Gradle test execution failed. Command './gradlew --info test' exited with non-zero status.
backend/src/main/java/moadong/club/service/ClubProfileService.java
[error] 1-1: Gradle test execution failed. Command './gradlew --info test' exited with non-zero status.
backend/src/main/java/moadong/club/entity/Club.java
[error] 1-1: Gradle test execution failed. Command './gradlew --info test' exited with non-zero status.
🔇 Additional comments (8)
backend/src/main/java/moadong/club/entity/Club.java (1)
16-16: LGTM!
socialLinks필드에@DiffIgnore어노테이션을 추가하여 JaVers 감사(audit) 추적에서 제외한 것은 적절합니다. 소셜 링크 변경은 동아리 핵심 정보 이력에 포함될 필요가 없습니다.Also applies to: 45-46
backend/src/main/java/moadong/club/entity/ClubRecruitmentInformation.java (1)
35-74:@DiffIgnore어노테이션 적용이 적절합니다.바이너리/URL 데이터(logo, cover, feedImages), 자동 생성 필드(lastModifiedDate), 민감한 정보(presidentTelephoneNumber)를 감사 추적에서 제외하는 것은 합리적입니다.
다만,
clubRecruitmentStatus제외가 의도적인지 확인이 필요합니다. 모집 상태 변경은 동아리 이력에서 중요한 정보일 수 있습니다.backend/src/main/java/moadong/club/service/ClubProfileService.java (1)
34-35: JaVers 감사 통합이 올바르게 구현되었습니다.저장 후
javers.commit()을 호출하여 변경 이력을 기록하고,user.getUsername()을 author로 사용하는 패턴이 적절합니다.backend/build.gradle (1)
65-65: JaVers 의존성이 올바르게 추가되었습니다.
javers-spring-boot-starter-mongo:7.10.0은 Spring Boot 3.3.8과 완전 호환되며, 프로젝트의 MongoDB 설정과 잘 맞습니다. JaVers 7.10.0은 Spring Boot 3 이상과 공식적으로 호환되므로 버전 호환성 문제는 없습니다.파이프라인 테스트 실패가 발생한 경우, JaVers 의존성 자체보다는 테스트 환경의 MongoDB 연결 설정이나 JaVers Bean 초기화 관련 설정을 확인해 주세요.
backend/src/main/java/moadong/global/config/JaversConfig.java (1)
22-26: MongoRepository 설정 확인JaVers MongoRepository 빈 설정이 적절합니다.
javers-spring-boot-starter-mongo를 사용하면 자동 구성이 가능하지만, 명시적 빈 정의도 유효한 접근입니다.backend/src/main/java/moadong/log/club/service/ClubHistoryService.java (1)
35-38: 타입 캐스팅 처리 적절함JaVers의
findShadows반환 타입이 raw type이므로 unchecked cast 억제가 필요합니다. 현재 구현이 적절합니다.backend/src/main/java/moadong/log/club/payload/response/ClubHistoryResponse.java (2)
43-77: Null-safe 매핑 로직 잘 구현됨중첩 객체(
recruitmentInfo)에 대한 null 방어 로직과Collections.emptyList()기본값 처리가 적절합니다.
64-70: 제거: 이 코드는 문제가 없습니다
toLocalDateTime()은ZonedDateTime메서드이며, 시스템 기본 시간대를 사용하지 않습니다. 이 메서드는 단순히 시간대 정보를 제거하고LocalDateTime을 반환합니다. 데이터는 이미ZoneId.of("Asia/Seoul")로 명시적으로 생성되므로 시간대 불일치 문제는 없습니다. API 응답 객체로 변환할 때LocalDateTime을 사용하는 것도 합리적입니다.Likely an incorrect or invalid review comment.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| @GetMapping("/{clubId}/histories") | ||
| public ResponseEntity<List<ClubHistoryResponse>> getClubHistories(@PathVariable String clubId) { | ||
| List<ClubHistoryResponse> histories = clubHistoryService.getClubHistories(clubId); | ||
| return ResponseEntity.ok(histories); | ||
| } |
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:
# First, locate and read the ClubHistoryController.java file
fd -name "ClubHistoryController.java" --type f -x cat -n {}Repository: Moadong/moadong
Length of output: 289
🏁 Script executed:
# Search for access control patterns in the codebase
rg -n '@PreAuthorize|@Secured|@RolesAllowed' --type java -A 2 -B 2Repository: Moadong/moadong
Length of output: 11244
🏁 Script executed:
# Check for Spring Security configuration files
fd -name "*Security*" --type f | head -20Repository: Moadong/moadong
Length of output: 289
🏁 Script executed:
# Look for any authentication/authorization in the controller directory
fd -name "*.java" backend/src/main/java/moadong/log/club/controller --type f -x rg -l '@PreAuthorize|@Secured|@GetMapping|@PostMapping'Repository: Moadong/moadong
Length of output: 289
🏁 Script executed:
# Search for ClubHistoryController.java file
find . -name "ClubHistoryController.java" -type fRepository: Moadong/moadong
Length of output: 137
🏁 Script executed:
# Search for any references to ClubHistoryController
rg "ClubHistoryController" --type javaRepository: Moadong/moadong
Length of output: 172
🏁 Script executed:
# Search for getClubHistories method across the codebase
rg "getClubHistories" --type java -B 5 -A 10Repository: Moadong/moadong
Length of output: 2871
🏁 Script executed:
# Read the entire ClubHistoryController.java file with line numbers
cat -n ./backend/src/main/java/moadong/log/club/controller/ClubHistoryController.javaRepository: Moadong/moadong
Length of output: 1193
🏁 Script executed:
# Check if there's any class-level annotation by reading the first 30 lines
head -30 ./backend/src/main/java/moadong/log/club/controller/ClubHistoryController.javaRepository: Moadong/moadong
Length of output: 1018
🏁 Script executed:
# Search for ClubHistoryResponse to see what data is exposed
rg "ClubHistoryResponse" --type java -B 3 -A 10Repository: Moadong/moadong
Length of output: 10338
인증 없는 감사 데이터 노출 - 보안 위험
엔드포인트에 접근 제어 어노테이션이 없어 누구나 클럽의 변경 이력 정보에 접근할 수 있습니다. 응답에 포함된 modifiedBy와 modifiedAt 같은 감사 메타데이터가 인증되지 않은 사용자에게 노출됩니다.
클래스 또는 메서드에 @PreAuthorize("isAuthenticated()") 어노테이션을 추가하여 인증 필요 조건을 설정해야 합니다. 이는 프로젝트의 다른 컨트롤러들(UserController, ClubImageController, ClubApplyAdminController 등)에서 표준으로 사용하는 패턴입니다.
🤖 Prompt for AI Agents
In `@backend/src/main/java/moadong/log/club/controller/ClubHistoryController.java`
around lines 20 - 24, Add method- or class-level access control to prevent
unauthenticated access to audit data: annotate the ClubHistoryController class
or its getClubHistories(...) method with `@PreAuthorize`("isAuthenticated()") so
only authenticated users can call the endpoint; ensure the security
method-authorization support is enabled in the application (consistent with how
UserController, ClubImageController, and ClubApplyAdminController are
protected).
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.
당장은 문제없을꺼같긴한데 인증 메서드 필요하긴하겠네요
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.
@lepitaaar 네, 동의해주셔서 감사합니다! PR 설명에 언급하신 "개발자 전용 비밀번호로 API 접근 제한 추가" TODO와 관련하여, 필요하시면 @PreAuthorize("isAuthenticated()") 또는 커스텀 권한 체크 로직을 적용하는 코드 변경을 지금 계획하는 것을 도와드릴 수 있습니다.
나중에 추가하실 계획이시면 그것도 괜찮습니다. 필요하시면 말씀해주세요! 😊
🧠 Learnings used
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.
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')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
| public static ClubHistoryResponse from(Shadow<Club> shadow) { | ||
| Club club = shadow.get(); | ||
| var metadata = shadow.getCommitMetadata(); |
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.
shadow.get() null 체크 필요
shadow.get()은 삭제 커밋의 경우 null을 반환할 수 있습니다. null 체크를 추가하거나, 삭제된 스냅샷에 대한 처리 로직이 필요합니다.
🐛 Null 방어 로직 추가 제안
public static ClubHistoryResponse from(Shadow<Club> shadow) {
Club club = shadow.get();
+ if (club == null) {
+ // 삭제된 경우 메타데이터만 포함하여 반환하거나 예외 처리
+ var metadata = shadow.getCommitMetadata();
+ return ClubHistoryResponse.builder()
+ .version(metadata.getId().getMajorId())
+ .modifiedAt(metadata.getCommitDate())
+ .modifiedBy(metadata.getAuthor())
+ .tags(Collections.emptyList())
+ .build();
+ }
var metadata = shadow.getCommitMetadata();🤖 Prompt for AI Agents
In
`@backend/src/main/java/moadong/log/club/payload/response/ClubHistoryResponse.java`
around lines 39 - 41, ClubHistoryResponse.from(Shadow<Club> shadow) calls
shadow.get() which can be null for delete commits; add a null check in that
method and handle deleted snapshots explicitly (e.g., return a
ClubHistoryResponse indicating deletion built from shadow.getCommitMetadata(),
or return null/Optional if that fits your API), ensuring you don't dereference
the null Club; update logic around ClubHistoryResponse.from, Shadow.get(), and
usage sites to handle the deletion response consistently.
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.
달라진 필드만 저장하는 shadow객체라서 db내에서 바로 보이진않네요. 그래도 로깅하는작업 좋은거같습니다 수고하셨습니다~
| // @Id | ||
| // private String id; |
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.
사실 id가 필요없었죠 좋습니다
| @GetMapping("/{clubId}/histories") | ||
| public ResponseEntity<List<ClubHistoryResponse>> getClubHistories(@PathVariable String clubId) { | ||
| List<ClubHistoryResponse> histories = clubHistoryService.getClubHistories(clubId); | ||
| return ResponseEntity.ok(histories); | ||
| } |
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.
당장은 문제없을꺼같긴한데 인증 메서드 필요하긴하겠네요
#️⃣연관된 이슈
#1036
📝작업 내용
동아리 정보 이력 저장 로직 추가
TODO: 개발자만 사용할 수 있도록 전용 비밀번호 추가할 것
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
외부에서 사용하지 못하도록 하고 싶은데 어떻게 구현하면 좋을까요?
프로퍼티에서 비밀번호 불러오는 식?
🫡 참고사항
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.