Conversation
[feature] 지원서의 메모를 수정할 수 있다.
[feature] 지원서 제출 상태 변경 API 추가
[feautre] 지원서 관리 API
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
Warning
|
| Cohort / File(s) | Change Summary |
|---|---|
Controller 및 엔드포인트 추가backend/src/main/java/moadong/club/controller/ClubApplyController.java |
PUT /api/club/{clubId}/apply/{appId}로 지원자 정보(메모, 상태) 수정, DELETE /api/club/{clubId}/apply/{appId}로 지원자 삭제 엔드포인트 추가. 인증 필요. |
엔티티 필드 및 메서드 확장backend/src/main/java/moadong/club/entity/ClubApplication.java |
memo 필드(기본값 ""), 메모 및 상태 동시 갱신용 updateDetail 메서드 추가. |
지원자 결과 DTO 확장backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java |
필드명 questionId → id로 변경, createdAt 필드 추가, factory 메서드 수정. |
지원자 수정 요청 DTO 추가backend/src/main/java/moadong/club/payload/request/ClubApplicantEditRequest.java |
memo(최대 500자, not null), status(not null) 필드를 가진 record 타입 요청 DTO 신설. |
레포지토리 메서드 추가backend/src/main/java/moadong/club/repository/ClubApplicationRepository.java |
findByIdAndQuestionId(Optional 반환) 메서드 추가. |
서비스 로직 추가backend/src/main/java/moadong/club/service/ClubApplyService.java |
지원자 정보 수정(editApplicantDetail), 삭제(deleteApplicant) 서비스 메서드 추가. 권한 및 예외 처리 포함. |
예외 코드 추가backend/src/main/java/moadong/global/exception/ErrorCode.java |
APPLICANT_NOT_FOUND(900-2, 404) 예외 코드 추가, enum trailing comma 보정. |
Sequence Diagram(s)
sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant Entity
Client->>Controller: PUT /api/club/{clubId}/apply/{appId} (memo, status)
Controller->>Service: editApplicantDetail(clubId, appId, request, user)
Service->>Repository: find Club by clubId
Service->>Repository: find ClubApplication by appId & clubId
Service->>Entity: updateDetail(memo, status)
Service->>Repository: save(ClubApplication)
Service-->>Controller: 완료
Controller-->>Client: 성공 응답
Client->>Controller: DELETE /api/club/{clubId}/apply/{appId}
Controller->>Service: deleteApplicant(clubId, appId, user)
Service->>Repository: find Club by clubId
Service->>Repository: find ClubApplication by appId & clubId
Service->>Repository: delete(ClubApplication)
Service-->>Controller: 완료
Controller-->>Client: 성공 응답
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
- Moadong/moadong#621: 지원자 상세 수정 엔드포인트 관련 기능 확장 및 중복 코드 영역 있음.
- Moadong/moadong#620: 메모 편집 기능 추가와 중복되는 로직이 있으나, 본 PR은 삭제 및 상태 변경 등 더 확장된 기능 포함.
- Moadong/moadong#622: 본 PR과 거의 동일한 엔드포인트, 서비스, DTO, 예외 코드 추가 등 코드 변경 내용이 직접적으로 일치.
Suggested reviewers
- Zepelown
- seongwon030
- PororoAndFriends
Note
🔌 MCP (Model Context Protocol) integration is now available in Early Access!
Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.
✨ Finishing Touches
- 📝 Generate Docstrings
🧪 Generate unit tests
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
develop/be
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
🪧 Tips
Chat
There are 3 ways to chat with CodeRabbit:
- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
I pushed a fix in commit <commit_id>, please review it.Explain this complex logic.Open a follow-up GitHub issue for this discussion.
- Files and specific lines of code (under the "Files changed" tab): Tag
@coderabbitaiin a new review comment at the desired location with your query. Examples:@coderabbitai explain this code block.
- PR comments: Tag
@coderabbitaiin a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.@coderabbitai read src/utils.ts and explain its main purpose.@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
Support
Need help? Create a ticket on our support page for assistance with any issues or questions.
CodeRabbit Commands (Invoked using PR comments)
@coderabbitai pauseto pause the reviews on a PR.@coderabbitai resumeto resume the paused reviews.@coderabbitai reviewto trigger an incremental review. This is useful when automatic reviews are disabled for the repository.@coderabbitai full reviewto do a full review from scratch and review all the files again.@coderabbitai summaryto regenerate the summary of the PR.@coderabbitai generate docstringsto generate docstrings for this PR.@coderabbitai generate sequence diagramto generate a sequence diagram of the changes in this PR.@coderabbitai generate unit teststo generate unit tests for this PR.@coderabbitai resolveresolve all the CodeRabbit review comments.@coderabbitai configurationto show the current CodeRabbit configuration for the repository.@coderabbitai helpto get help.
Other keywords and placeholders
- Add
@coderabbitai ignoreanywhere in the PR description to prevent this PR from being reviewed. - Add
@coderabbitai summaryto generate the high-level summary at a specific location in the PR description. - Add
@coderabbitaianywhere in the PR title to generate the title automatically.
Documentation and Community
- Visit our Documentation for detailed information on how to use CodeRabbit.
- Join our Discord Community to get help, request features, and share feedback.
- Follow us on X/Twitter for updates and announcements.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (8)
backend/src/main/java/moadong/club/repository/ClubApplicationRepository.java (1)
14-14: 중복·일관성 검토 필요
id자체가 고유키이므로questionId까지 추가로 조건에 넣는 것이 과도할 수 있습니다. 반대로findAllByQuestionId에서는status != DRAFT조건을 명시했는데, 여기에는 빠져 있어 동일한 비즈니스 규칙이 깨집니다.
·questionId-검증이 꼭 필요하다면 서비스 레이어에서 처리하고, 레포지토리는findById만 제공해 단일 책임을 유지하는 방안을 고려해 주세요.
· 또는 쿼리 메서드에status필터를 맞춰 일관성을 유지해 주세요.backend/src/main/java/moadong/club/entity/ClubApplication.java (2)
33-35: 엔티티 레벨 길이 제약 추가 권장
memo필드는 요청 DTO에서@Size(max = 500)로 제한하지만, 엔티티에는 제약이 없습니다.
DB에 500자를 초과한 값이 저장될 가능성을 차단하려면@Size또는 스키마 제약을 엔티티에도 선언해 주세요.
42-45: null 방어 로직 고려
updateDetail에서memo,status가 그대로 할당됩니다. 서비스 단에서 validation이 있더라도 방어 코드(예:Objects.requireNonNull(status))를 두면 예기치 못한 NPE를 예방할 수 있습니다.backend/src/main/java/moadong/club/payload/request/ClubApplicantEditRequest.java (1)
7-14: 빈 문자열 허용 여부 확인 필요
memo가@NotNull만 적용되어 공백 문자열은 허용됩니다. 빈 메모도 허용 의도라면 문제없지만, 최소 한 글자 이상을 요구하려면@NotBlank로 변경해 주세요.backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java (1)
19-23: 응답 DTO가 엔티티를 직접 노출
answers타입으로ClubQuestionAnswer엔티티를 그대로 반환하면 계층 간 결합이 강화됩니다. 가벼운 응답용 DTO로 매핑해 노출 범위를 최소화하는 것을 권장합니다.backend/src/main/java/moadong/club/service/ClubApplyService.java (1)
3-3: 트랜잭션 어노테이션 일관성: Spring @transactional로 통일 권장현재
jakarta.transaction.Transactional을 사용하고 있습니다. 프로젝트 전반의 일관성과 세밀한 설정(예: propagation, isolation, timeout, readOnly 등)을 위해org.springframework.transaction.annotation.Transactional사용을 권장합니다.-import jakarta.transaction.Transactional; +import org.springframework.transaction.annotation.Transactional;backend/src/main/java/moadong/club/controller/ClubApplyController.java (2)
73-86: PUT 지원자 수정: 문구 오타(‘자원자’→‘지원자’), @Valid 사용 권장, PATCH/204 고려
- Swagger 문구에서 ‘자원자’ → ‘지원자’로 정정 제안.
- DTO 검증은
@Validated대신@Valid사용을 권장합니다(중첩 필드 검증 일관성).- 부분 수정 성격이면
PUT대신PATCH와 응답204 No Content도 고려해 주세요.오타/검증 애노테이션 최소 변경 diff:
- @Operation(summary = "지원서 변경", - description = "클럽 자원자의 지원서 정보를 수정합니다.<br>" + @Operation(summary = "지원서 변경", + description = "클럽 지원자의 지원서 정보를 수정합니다.<br>" + "appId - 지원서 아이디" ) @@ - @RequestBody @Validated ClubApplicantEditRequest request, + @RequestBody @Valid ClubApplicantEditRequest request,필요 시 상단 임포트 추가:
import jakarta.validation.Valid;
88-101: DELETE 지원자 삭제: 문구 오타 정정, 메서드명 일관성, 204 No Content 권장
- Swagger 문구의 ‘자원자’ → ‘지원자’ 정정 제안.
- 서비스 메서드가
deleteApplicant인 만큼 컨트롤러도 메서드명 일관성 유지 제안(removeApplicant→deleteApplicant).- 삭제 성공 시 바디 없이
204 No Content응답을 고려해 주세요.최소 변경 diff:
- @Operation(summary = "지원서 삭제", - description = "클럽 자원자의 지원서를 삭제합니다.<br>" + @Operation(summary = "지원서 삭제", + description = "클럽 지원자의 지원서를 삭제합니다.<br>" + "appId - 지원서 아이디" ) @@ - public ResponseEntity<?> removeApplicant(@PathVariable String clubId, + public ResponseEntity<?> deleteApplicant(@PathVariable String clubId, @PathVariable String appId, @CurrentUser CustomUserDetails user) { clubApplyService.deleteApplicant(clubId, appId, user); - return Response.ok("success delete applicant"); + return Response.ok("success delete applicant"); // 팀 컨벤션에 따라 204 No Content 고려 }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
backend/src/main/java/moadong/club/controller/ClubApplyController.java(2 hunks)backend/src/main/java/moadong/club/entity/ClubApplication.java(1 hunks)backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java(2 hunks)backend/src/main/java/moadong/club/payload/request/ClubApplicantEditRequest.java(1 hunks)backend/src/main/java/moadong/club/repository/ClubApplicationRepository.java(1 hunks)backend/src/main/java/moadong/club/service/ClubApplyService.java(2 hunks)backend/src/main/java/moadong/global/exception/ErrorCode.java(1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: lepitaaar
PR: Moadong/moadong#406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.
Learnt from: seongwon030
PR: Moadong/moadong#195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Learnt from: Zepelown
PR: Moadong/moadong#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`와 같은 별도의 클래스를 정의하고 사용해야 합니다. 이렇게 하면 요청 객체 변경이 데이터베이스 스키마나 엔티티 계층에 영향을 미치지 않습니다.
📚 Learning: 2025-05-19T05:45:52.957Z
Learnt from: lepitaaar
PR: Moadong/moadong#406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.
Applied to files:
backend/src/main/java/moadong/club/entity/ClubApplication.javabackend/src/main/java/moadong/club/repository/ClubApplicationRepository.javabackend/src/main/java/moadong/club/payload/request/ClubApplicantEditRequest.javabackend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.javabackend/src/main/java/moadong/club/service/ClubApplyService.javabackend/src/main/java/moadong/club/controller/ClubApplyController.java
📚 Learning: 2025-05-15T12:03:57.356Z
Learnt from: Zepelown
PR: Moadong/moadong#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/dto/ClubApplicantsResult.javabackend/src/main/java/moadong/club/service/ClubApplyService.java
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
PR: Moadong/moadong#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/ClubApplyService.java
⏰ 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: Deploy
🔇 Additional comments (3)
backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java (1)
40-44: 생성일 추가 👍, 하지만 정렬 기준 확인
createdAt필드가 추가되었으므로, 목록 API에서 기본 정렬(예: 최신순)이 필요한지 서비스/레포지토리 레이어 로직을 함께 점검해 주세요.backend/src/main/java/moadong/club/service/ClubApplyService.java (1)
9-9: 요청 DTO 임포트 추가 LGTM
ClubApplicantEditRequest추가 적절합니다.backend/src/main/java/moadong/club/controller/ClubApplyController.java (1)
7-7: 요청 DTO 임포트 추가 LGTM
ClubApplicantEditRequest임포트 추가 적절합니다.
| @Transactional | ||
| public void editApplicantDetail(String clubId, String appId, ClubApplicantEditRequest request, CustomUserDetails user) { | ||
| Club club = clubRepository.findById(clubId) | ||
| .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND)); | ||
|
|
||
| if (!user.getId().equals(club.getUserId())) { | ||
| throw new RestApiException(ErrorCode.USER_UNAUTHORIZED); | ||
| } | ||
|
|
||
| ClubApplication application = clubApplicationRepository.findByIdAndQuestionId(appId, clubId) | ||
| .orElseThrow(() -> new RestApiException(ErrorCode.APPLICANT_NOT_FOUND)); | ||
|
|
||
| application.updateDetail(request.memo(), request.status()); | ||
|
|
||
| clubApplicationRepository.save(application); | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
지원자 수정: 상태 전이 검증·동시성 보호(@Version)·중복 로직 추출 제안
- 권한 검증/조회 흐름은 적절합니다.
- 상태 전이 검증: 도메인 규칙(예: SCREENING → SCREENING_PASSED → …) 유효성 체크가 필요합니다. 서비스 레벨에서 허용되지 않는 전이를 차단하세요.
- 동시성: 다중 관리자가 동시에 수정 시 마지막 저장이 덮어쓸 수 있습니다.
ClubApplication엔티티에@Version(낙관적 락)을 도입하는 것을 권장합니다. - 중복 제거: 클럽 조회+권한검사, 지원서 조회 로직이
deleteApplicant와 반복됩니다. 헬퍼 메서드로 추출하면 가독성과 재사용성이 좋아집니다. - 저장 호출: JPA 영속 상태라면 dirty checking으로 flush 됩니다. 명시적
save유지/제거는 팀 컨벤션에 맞추세요.
중복 제거용 최소 변경 diff:
- Club club = clubRepository.findById(clubId)
- .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
-
- if (!user.getId().equals(club.getUserId())) {
- throw new RestApiException(ErrorCode.USER_UNAUTHORIZED);
- }
+ Club club = getAuthorizedClubOrThrow(clubId, user);
- ClubApplication application = clubApplicationRepository.findByIdAndQuestionId(appId, clubId)
- .orElseThrow(() -> new RestApiException(ErrorCode.APPLICANT_NOT_FOUND));
+ ClubApplication application = getApplicantOrThrow(appId, clubId);헬퍼 메서드(파일 내 적절한 위치에 추가):
private Club getAuthorizedClubOrThrow(String clubId, CustomUserDetails user) {
Club club = clubRepository.findById(clubId)
.orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
if (!user.getId().equals(club.getUserId())) {
throw new RestApiException(ErrorCode.USER_UNAUTHORIZED);
}
return club;
}
private ClubApplication getApplicantOrThrow(String appId, String clubId) {
return clubApplicationRepository.findByIdAndQuestionId(appId, clubId)
.orElseThrow(() -> new RestApiException(ErrorCode.APPLICANT_NOT_FOUND));
}상태/동시성 관련 확인 스크립트:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) ClubApplication에 @Version(낙관적 락) 존재 여부 확인
fd -t f 'ClubApplication.java' | xargs -I {} rg -n --context 2 '@Version|class ClubApplication' {}
# 2) ClubApplicantEditRequest의 검증 애노테이션 확인(메모/상태 Null 허용 여부)
fd -t f 'ClubApplicantEditRequest.java' | xargs -I {} sed -n '1,200p' {}
# 3) Repository 시그니처 및 의미 확인
fd -t f 'ClubApplicationRepository.java' | xargs -I {} sed -n '1,200p' {}
rg -n 'findByIdAndQuestionId\(' -A 3Length of output: 2625
지원자 수정 작업에 아래 사항 보강 필요
- 요청 DTO 검증 확인
ClubApplicantEditRequest의memo(@NotNull, @SiZe(max=500))와status(@NotNull)에 Bean Validation이 적용되어 있습니다. - 상태 전이 검증
ClubApplication.updateDetail(...)내부에 SCREENING → SCREENING_PASSED 등 도메인 전이 유효성을 체크하는 로직이 없어 서비스 레이어에서 불법 전이를 차단해야 합니다. - 동시성 보호(@Version)
ClubApplication엔티티에@Version필드가 없어 다중 관리자 동시 수정 시 데이터 손실 위험이 있습니다. - 중복 로직 추출
editApplicantDetail과deleteApplicant에서 클럽 조회·권한 검증, 지원서 조회 로직이 반복됩니다. 헬퍼 메서드로 분리해 가독성과 재사용성을 높이세요. - 저장 호출 일관성
MongoRepository는 변경 감지를 통해 저장하지만, 명시적save(...)호출 여부를 팀 컨벤션에 맞춰 통일하세요.
필요한 최소 변경 예시:
- Club club = clubRepository.findById(clubId)
- .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
-
- if (!user.getId().equals(club.getUserId())) {
- throw new RestApiException(ErrorCode.USER_UNAUTHORIZED);
- }
+ Club club = getAuthorizedClubOrThrow(clubId, user);
- ClubApplication application = clubApplicationRepository.findByIdAndQuestionId(appId, clubId)
- .orElseThrow(() -> new RestApiException(ErrorCode.APPLICANT_NOT_FOUND));
+ ClubApplication application = getApplicantOrThrow(appId, clubId);헬퍼 메서드 예시 (클래스 내 적절한 위치에 추가):
private Club getAuthorizedClubOrThrow(String clubId, CustomUserDetails user) {
Club club = clubRepository.findById(clubId)
.orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
if (!user.getId().equals(club.getUserId())) {
throw new RestApiException(ErrorCode.USER_UNAUTHORIZED);
}
return club;
}
private ClubApplication getApplicantOrThrow(String appId, String clubId) {
return clubApplicationRepository.findByIdAndQuestionId(appId, clubId)
.orElseThrow(() -> new RestApiException(ErrorCode.APPLICANT_NOT_FOUND));
}낙관적 락 적용을 위해 ClubApplication 엔티티에 다음 필드를 추가하세요:
@Version
private Long version;🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/service/ClubApplyService.java around lines
128 to 143, enhance the editApplicantDetail method by first validating the
ClubApplicantEditRequest DTO using Bean Validation annotations already present
on memo and status fields. Add explicit state transition checks in the service
layer to prevent illegal transitions like SCREENING to SCREENING_PASSED before
calling updateDetail. Introduce an @Version field in the ClubApplication entity
to enable optimistic locking and prevent concurrent update conflicts. Extract
repeated logic for club retrieval and authorization as well as applicant
retrieval into private helper methods to improve code reuse and readability.
Finally, ensure consistent use of save calls on the repository according to team
conventions, either always calling save explicitly or relying on change
detection uniformly.
| @Transactional | ||
| public void deleteApplicant(String clubId, String appId, CustomUserDetails user) { | ||
| Club club = clubRepository.findById(clubId) | ||
| .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND)); | ||
|
|
||
| if (!user.getId().equals(club.getUserId())) { | ||
| throw new RestApiException(ErrorCode.USER_UNAUTHORIZED); | ||
| } | ||
|
|
||
| ClubApplication application = clubApplicationRepository.findByIdAndQuestionId(appId, clubId) | ||
| .orElseThrow(() -> new RestApiException(ErrorCode.APPLICANT_NOT_FOUND)); | ||
|
|
||
| clubApplicationRepository.delete(application); | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
지원자 삭제: 연관 엔티티 삭제 보장, 감사 로깅, 중복 로직 추출 제안
- 연관 데이터(예:
answers)가 있는 경우 FK 제약/고아 제거 설정이 필요합니다.@OneToMany(orphanRemoval = true, cascade = CascadeType.ALL)등으로 삭제 일관성을 보장해 주세요. - 감사/감사추적 요구가 있다면 soft delete(상태 플래그)나 감사 로그 기록 고려를 권장합니다.
getAuthorizedClubOrThrow,getApplicantOrThrow로 중복 조회/검증 로직을 통합하면 유지보수성이 향상됩니다.
중복 제거용 최소 변경 diff:
- Club club = clubRepository.findById(clubId)
- .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
-
- if (!user.getId().equals(club.getUserId())) {
- throw new RestApiException(ErrorCode.USER_UNAUTHORIZED);
- }
+ Club club = getAuthorizedClubOrThrow(clubId, user);
- ClubApplication application = clubApplicationRepository.findByIdAndQuestionId(appId, clubId)
- .orElseThrow(() -> new RestApiException(ErrorCode.APPLICANT_NOT_FOUND));
+ ClubApplication application = getApplicantOrThrow(appId, clubId);연관 삭제 설정 점검 스크립트:
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# answers 매핑에 orphanRemoval / cascade 설정 확인
fd -t f 'ClubApplication.java' | xargs -I {} rg -n --context 5 'answers|@OneToMany|@ElementCollection|cascade|orphanRemoval' {}Length of output: 509
중복 로직 추출 및 연관 엔티티 삭제 설정 적용 제안
ClubApplication 삭제 시 답변(answers)이 JPA 매핑 없이 일반 컬렉션으로만 선언되어 있어, 연관 엔티티가 영속성 컨텍스트에서 함께 삭제되지 않습니다. 또한 서비스 레이어의 조회·검증 로직도 중복되고 있어 아래 사항을 적용해 주세요.
• ClubApplication.answers 필드에 JPA 매핑 추가
- @onetomany(mappedBy = "application", cascade = CascadeType.ALL, orphanRemoval = true) 등으로 답변 삭제 일관성 보장
• ClubApplyService 내 중복 조회·검증 로직 추출 - getAuthorizedClubOrThrow(clubId, user)
- getApplicantOrThrow(appId, clubId)
• 감사 트래킹(soft delete or audit log) 필요 시 별도 구현 고려
예시 최소 변경 diff:
--- a/backend/src/main/java/moadong/club/entity/ClubApplication.java
+++ b/backend/src/main/java/moadong/club/entity/ClubApplication.java
@@
- @Builder.Default
- private List<ClubQuestionAnswer> answers = new ArrayList<>();
+ @OneToMany(
+ mappedBy = "application",
+ cascade = CascadeType.ALL,
+ orphanRemoval = true
+ )
+ @Builder.Default
+ private List<ClubQuestionAnswer> answers = new ArrayList<>();--- a/backend/src/main/java/moadong/club/service/ClubApplyService.java
+++ b/backend/src/main/java/moadong/club/service/ClubApplyService.java
@@ public void deleteApplicant(String clubId, String appId, CustomUserDetails user) {
- Club club = clubRepository.findById(clubId)
- .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND));
-
- if (!user.getId().equals(club.getUserId())) {
- throw new RestApiException(ErrorCode.USER_UNAUTHORIZED);
- }
+ Club club = getAuthorizedClubOrThrow(clubId, user);
@@
- ClubApplication application = clubApplicationRepository.findByIdAndQuestionId(appId, clubId)
- .orElseThrow(() -> new RestApiException(ErrorCode.APPLICANT_NOT_FOUND));
+ ClubApplication application = getApplicantOrThrow(appId, clubId);Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/club/service/ClubApplyService.java around lines
145 to 158, the deleteApplicant method has duplicated club and applicant
retrieval and authorization logic, and the ClubApplication entity's answers
collection lacks JPA cascade settings causing orphaned data. Refactor by
extracting the club retrieval and authorization into a getAuthorizedClubOrThrow
method and the applicant retrieval into getApplicantOrThrow method to remove
duplication. Also, update the ClubApplication.answers field to use
@OneToMany(mappedBy = "application", cascade = CascadeType.ALL, orphanRemoval =
true) to ensure related answers are deleted automatically with the application.
Consider adding audit or soft delete separately if needed.
| AES_CIPHER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "900-1", "암호화 중 오류가 발생했습니다."), | ||
| APPLICANT_NOT_FOUND(HttpStatus.NOT_FOUND, "900-2", "지원서가 존재하지 않습니다."), | ||
| ; |
There was a problem hiding this comment.
오류 코드 중복 정의
APPLICATION_NOT_FOUND(800-1)와 APPLICANT_NOT_FOUND(900-2)가 동일한 메시지와 의미를 갖고 있어 혼동을 유발합니다.
하나의 코드로 통합하거나, 명확히 다른 도메인을 나타내도록 메시지·코드를 재조정해 주세요.
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/global/exception/ErrorCode.java around lines 42
to 44, the error codes APPLICATION_NOT_FOUND (800-1) and APPLICANT_NOT_FOUND
(900-2) have overlapping meanings and messages, causing confusion. Review these
two error codes and either merge them into a single unified error code or
differentiate their messages and codes clearly to reflect distinct domains or
error contexts. Update the enum definitions accordingly to avoid duplication and
ambiguity.
#️⃣연관된 이슈
📝작업 내용
지원서 메모 API
지원서 상태 변경 API
지원서 삭제 API 추가
지원자의 지원서를 응답해주는 API의 응답 명세가 변경되어 FE 배포보다 우선시되어야합니다.
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
신규 기능
버그 수정
기타