feat : 퀴즈 삭제 API 추가#88
Conversation
Walkthrough관리자 API에 퀴즈 삭제 엔드포인트(DELETE /quiz/{quizId})가 추가되었고, 서비스 계층에 해당 삭제 로직이 구현되었습니다. 존재하지 않는 퀴즈 ID일 경우 예외를 던지며, 삭제 성공 시 boolean을 반환합니다. 기존 생성 엔드포인트는 변경 없습니다. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant Controller as AdminController
participant Service as AdminService
participant Repo as QuizRepository
Client->>Controller: DELETE /quiz/{quizId}
Controller->>Service: deleteQuiz(quizId)
Service->>Repo: findById(quizId)
alt Quiz exists
Repo-->>Service: Quiz
Service->>Repo: delete(quiz)
Repo-->>Service: void
Service-->>Controller: true
Controller-->>Client: 200 OK { success: true }
else Quiz not found
Repo-->>Service: Optional.empty
Service-->>Controller: throw QuizNotFoundException
Controller-->>Client: Error (예외 매핑에 따름)
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (7)
src/main/java/com/example/cp_main_be/domain/admin/service/AdminService.java (3)
95-101: 삭제 API 반환 설계: boolean 대신 204 No Content 권장삭제 성공 시 굳이 boolean을 바디로 반환할 필요가 없습니다. REST 관례에 맞춰 204 No Content로 응답하면 네트워크 비용과 문서 복잡도를 줄일 수 있습니다. 서비스는 void를 반환하도록 단순화하세요.
적용 예시(서비스/컨트롤러 동시 변경):
- @Transactional - public boolean deleteQuiz(Long quizId) { + public void deleteQuiz(Long quizId) { Quiz quiz = quizRepository.findById(quizId) - .orElseThrow(() -> new QuizNotFoundException("퀴즈가 존재하지 않습니다.")); + .orElseThrow(() -> new QuizNotFoundException("퀴즈(ID: " + quizId + ")가 존재하지 않습니다.")); quizRepository.delete(quiz); - return true; }이 변경에 맞춘 컨트롤러 수정은 해당 컨트롤러 코멘트를 참고하세요.
97-98: 예외 메시지에 식별자 포함 제안운영 트러블슈팅을 위해 예외 메시지에 quizId를 포함하면 로그 가독성이 개선됩니다.
- .orElseThrow(() -> new QuizNotFoundException("퀴즈가 존재하지 않습니다.")); + .orElseThrow(() -> new QuizNotFoundException("퀴즈(ID: " + quizId + ")가 존재하지 않습니다."));
95-95: 클래스 레벨 @transactional 이미 존재 — 메서드 레벨 중복은 제거 가능본 클래스는 이미 @transactional이 선언되어 있으므로(라인 30), 동일한 설정이라면 메서드 레벨의 @transactional은 중복입니다. 특별한 속성 변경(readOnly, propagation 등)이 없다면 제거해도 됩니다.
- @Transactional public boolean deleteQuiz(Long quizId) {src/main/java/com/example/cp_main_be/domain/admin/presentation/AdminController.java (4)
74-79: 삭제 엔드포인트는 204 No Content가 적합 — 바디의 boolean 제거현재 boolean 바디를 반환하고 있습니다. REST 관례상 삭제 성공 시 204 No Content가 더 적합합니다. 또한 파일 내 다른 엔드포인트는 ResponseEntity를 사용하는 경우가 많으므로 일관성도 좋아집니다.
- @DeleteMapping("/quiz/{quizId}") - @Operation(summary = "퀴즈 삭제 API") - public ApiResponse<Boolean> deleteQuiz(@PathVariable Long quizId) { - Boolean success = adminService.deleteQuiz(quizId); - return ApiResponse.ok(success); - } + @DeleteMapping("/quiz/{quizId}") + @Operation(summary = "퀴즈 삭제 API") + public ResponseEntity<Void> deleteQuiz(@PathVariable Long quizId) { + adminService.deleteQuiz(quizId); + return ResponseEntity.noContent().build(); + }서비스 시그니처도 boolean → void로 변경되어야 합니다(서비스 파일 코멘트 참고).
74-79: 접근 제어 어노테이션 추가 권장관리자 전용 API라면 메서드(또는 클래스) 레벨에 권한 체크를 명시하세요.
예: Spring Security 사용 시
@PreAuthorize("hasRole('ADMIN')")운영 환경에서 오남용을 줄이고 문서화에도 도움이 됩니다.
74-79: 스웨거 응답 스펙 보강(404, 204 명시)삭제 성공(204)과 미존재(404)를 명확히 문서화하면 클라이언트 연동이 수월합니다.
예:
@ApiResponses({ @ApiResponse(responseCode = "204", description = "퀴즈 삭제 성공"), @ApiResponse(responseCode = "404", description = "퀴즈를 찾을 수 없음") })
74-79: 응답 포맷 일관성본 파일 내 일부 엔드포인트는 ResponseEntity<ApiResponse<...>>를, 일부는 ApiResponse만 반환합니다. 팀 규약에 맞춰 삭제 API도 동일한 패턴으로 맞추는 것을 권장합니다. 위 204 제안과 함께 ResponseEntity를 사용하면 일관성이 향상됩니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/main/java/com/example/cp_main_be/domain/admin/presentation/AdminController.java(1 hunks)src/main/java/com/example/cp_main_be/domain/admin/service/AdminService.java(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/example/cp_main_be/domain/admin/service/AdminService.java (1)
src/main/java/com/example/cp_main_be/global/exception/QuizNotFoundException.java (1)
QuizNotFoundException(3-7)
🪛 GitHub Actions: Spring Boot CI/CD with AWS
src/main/java/com/example/cp_main_be/domain/admin/service/AdminService.java
[error] 94-104: Spotless formatting check failed. The file had format violations. Run './gradlew spotlessApply' to fix formatting issues in this file.
🔇 Additional comments (2)
src/main/java/com/example/cp_main_be/domain/admin/service/AdminService.java (2)
20-20: 도메인 전용 예외 도입은 적절합니다존재하지 않는 퀴즈 삭제 시 명확한 비즈니스 예외를 던지도록 한 점 좋습니다. 글로벌 예외 핸들러에서 404 매핑만 일관되게 처리되는지 확인만 부탁드립니다.
95-101: CI 실패: Spotless 포맷팅 위반 해결 필요파이프라인 로그에 따르면 94–104 라인에서 Spotless 실패가 발생했습니다. 로컬에서 아래 명령으로 자동 정렬 실행 후 커밋해주세요.
- ./gradlew spotlessApply
- 이후 ./gradlew spotlessCheck 로 재확인
포맷팅 규칙에 따라 import 정렬/공백/줄바꿈 등이 자동 반영됩니다.
| @Transactional | ||
| public boolean deleteQuiz(Long quizId) { | ||
| Quiz quiz = quizRepository.findById(quizId) | ||
| .orElseThrow(() -> new QuizNotFoundException("퀴즈가 존재하지 않습니다.")); | ||
| quizRepository.delete(quiz); | ||
| return true; | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
연관 엔티티(QuizOptions 등) 삭제 보장 필요 — FK 제약 위반/고아 레코드 위험
현재 Quiz만 삭제합니다. Quiz ↔ QuizOptions, DailyMissionMaster ↔ Quiz 등 연관관계에서
- JPA 매핑에 orphanRemoval = true 또는 CascadeType.REMOVE가 없다면 FK 제약 위반이 발생할 수 있고,
- DB FK에 ON DELETE CASCADE가 없다면 자식 레코드가 남을 수 있습니다.
엔티티 매핑 또는 DB 제약을 점검/보완해주세요. 없다면 다음과 같이 매핑을 권장합니다(참고용, 엔티티 파일에서 적용).
// in Quiz.java
@OneToMany(mappedBy = "quiz", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<QuizOptions> options;또는 서비스 계층에서 명시적으로 자식부터 삭제하는 전략을 택할 수 있습니다.
아래 스크립트로 매핑 상태를 빠르게 점검해볼 수 있습니다.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Quiz 엔티티 정의 위치 =="
rg -nP --type=java -C3 'class\s+Quiz\b'
echo "== Quiz -> QuizOptions @OneToMany 매핑 확인 =="
rg -nP --type=java -n -C2 '@OneToMany\([^)]*\)\s+private\s+List<\s*QuizOptions\s*>'
echo "== QuizOptions에서 @ManyToOne(Quiz) 매핑 확인 =="
rg -nP --type=java -n -C3 'class\s+QuizOptions\b'
rg -nP --type=java -n -C3 '@ManyToOne[^\n]*\n\s*private\s+Quiz\b'
echo "== orphanRemoval 또는 CascadeType.REMOVE 존재 여부 =="
rg -nP --type=java -n 'orphanRemoval\s*=\s*true|CascadeType\.REMOVE|CascadeType\.ALL'
echo "== SQL 마이그레이션에서 FK 및 ON DELETE CASCADE 확인(있다면) =="
fd -t f -e sql | xargs -r rg -n 'QuizOptions|quiz_options|FOREIGN KEY|ON DELETE'Length of output: 979
추가 검증을 위해 오류 발생 시에도 계속 진행하도록 스크립트를 수정했습니다. 아래 스크립트를 실행한 후 결과를 공유해주세요.
🏁 Script executed:
#!/bin/bash
echo "== Quiz.java 내 @OneToMany 매핑 확인 =="
rg -nP --type=java -n -C2 '@OneToMany' src/main/java/com/example/cp_main_be/domain/mission/quiz/domain/Quiz.java || echo " → 매핑 없음"
echo
echo "== Quiz.java 내 orphanRemoval/CascadeType 설정 확인 =="
rg -nP --type=java -n 'orphanRemoval\s*=\s*true|CascadeType\.REMOVE|CascadeType\.ALL' src/main/java/com/example/cp_main_be/domain/mission/quiz/domain/Quiz.java || echo " → 설정 없음"
echo
echo "== QuizOptions.java 내 @ManyToOne 매핑 확인 =="
rg -nP --type=java -n -C2 '@ManyToOne' src/main/java/com/example/cp_main_be/domain/mission/quiz/domain/QuizOptions.java || echo " → 매핑 없음"
echo
echo "== QuizOptions.java 내 orphanRemoval/CascadeType 설정 확인 =="
rg -nP --type=java -n 'orphanRemoval\s*=\s*true|CascadeType\.REMOVE|CascadeType\.ALL' src/main/java/com/example/cp_main_be/domain/mission/quiz/domain/QuizOptions.java || echo " → 설정 없음"
echo
echo "== SQL 마이그레이션에서 quiz_options FK 및 ON DELETE CASCADE 확인 =="
fd -t f -e sql | xargs -r rg -n -C1 'FOREIGN KEY.*quiz_options|ON DELETE CASCADE' || echo " → 관련 정의 없음"Length of output: 1463
추가로 DailyMissionMaster ↔ Quiz 매핑 상태도 확인할 수 있도록 아래 스크립트를 실행해주세요.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== DailyMissionMaster 클래스 위치 =="
rg -nP --type=java -C2 'class\s+DailyMissionMaster\b' src || echo " → DailyMissionMaster 파일 없음"
echo
echo "== DailyMissionMaster -> Quiz @ManyToOne 매핑 확인 =="
rg -nP --type=java -n -C2 '@ManyToOne.*\bQuiz\b' $(rg -lP 'DailyMissionMaster' -g '*.java') || echo " → 매핑 없음"
echo
echo "== DailyMissionMaster 내 orphanRemoval/CascadeType 설정 확인 =="
rg -nP --type=java -n 'orphanRemoval\s*=\s*true|CascadeType\.REMOVE|CascadeType\.ALL' $(rg -lP 'DailyMissionMaster' -g '*.java') || echo " → 설정 없음"
echo
echo "== Quiz.java 내 @OneToMany → DailyMissionMaster 매핑 확인 =="
rg -nP --type=java -n -C2 '@OneToMany.*\bDailyMissionMaster\b' src/main/java/com/example/cp_main_be/domain/mission/quiz/domain/Quiz.java || echo " → 매핑 없음"
echo
echo "== SQL 마이그레이션에서 daily_mission_master FK 및 ON DELETE CASCADE 확인 =="
fd -t f -e sql | xargs -r rg -n -C1 'FOREIGN KEY.*daily_mission_master|ON DELETE CASCADE' || echo " → 관련 정의 없음"Length of output: 5081
연관 엔티티 삭제 매핑 추가 필요
현재 deleteQuiz 메서드는 Quiz 엔티티만 삭제하며, 다음과 같은 위험이 있습니다.
- JPA에서
QuizOptions(Quiz ↔ QuizOptions) 연관관계에cascade또는orphanRemoval설정이 없어, 부모 삭제 시 FK 제약 위반 또는 자식 레코드 고아 상태 발생 - DB 마이그레이션에
quiz_options테이블의 FK에ON DELETE CASCADE설정이 없음 - (
DailyMissionMaster ↔ Quiz관계가 실제로 존재한다면) 해당 매핑도 누락되어 동일한 위험
점검 결과
- Quiz.java:
@OneToMany매핑 정의 없음 - QuizOptions.java:
@ManyToOne만 있고 cascade/orphanRemoval 설정 없음 - SQL 마이그레이션:
FOREIGN KEY … quiz_options/ON DELETE CASCADE정의 없음 - DailyMissionMaster.java ↔ Quiz 매핑 없음 (필요 시 검토)
조치 방안
src/main/java/com/example/cp_main_be/domain/mission/quiz/domain/Quiz.java에 자식 엔티티 매핑 추가 (예시)@OneToMany(mappedBy = "quiz", cascade = CascadeType.REMOVE, orphanRemoval = true) private List<QuizOptions> options;
- 서비스 계층에서 자식부터 명시적으로 삭제하거나
- DB 스키마에 FK 제약과
ON DELETE CASCADE설정 추가 - DailyMissionMaster ↔ Quiz 관계가 실제로 필요하다면, 유사하게 JPA 매핑/DB 제약 보완
🤖 Prompt for AI Agents
In src/main/java/com/example/cp_main_be/domain/admin/service/AdminService.java
around lines 95–101, deleteQuiz currently deletes only the Quiz entity which can
leave orphaned QuizOptions or violate FK constraints; update the domain and/or
schema to cascade deletions: add a OneToMany mapping in
src/main/java/com/example/cp_main_be/domain/mission/quiz/domain/Quiz.java for
QuizOptions with mappedBy set to the Quiz field and set cascade =
CascadeType.REMOVE and orphanRemoval = true (or CascadeType.ALL if appropriate),
or alternatively modify the service to explicitly delete child QuizOptions
before deleting the Quiz; add/adjust the DB migration to set the foreign key on
quiz_options to ON DELETE CASCADE; and if DailyMissionMaster has a relation to
Quiz, add the corresponding JPA mapping and DB FK cascade or handle deletions
similarly.
feat : 퀴즈 삭제 API 추가
Summary by CodeRabbit