Skip to content

feat : 퀴즈 삭제 API 추가#88

Merged
c5ln merged 1 commit into
developfrom
feat/delete-quiz
Aug 25, 2025
Merged

feat : 퀴즈 삭제 API 추가#88
c5ln merged 1 commit into
developfrom
feat/delete-quiz

Conversation

@c5ln
Copy link
Copy Markdown
Member

@c5ln c5ln commented Aug 25, 2025

feat : 퀴즈 삭제 API 추가

Summary by CodeRabbit

  • 신기능
    • 관리자가 퀴즈를 ID로 삭제할 수 있는 기능을 추가했습니다. 성공/실패 결과가 즉시 제공되며, 삭제된 퀴즈는 목록과 상세 화면에서 더 이상 표시되지 않습니다. 잘못된 ID 요청에 대해서는 적절한 오류가 반환됩니다. 기존 퀴즈 생성 기능에는 영향이 없으며, 새 기능은 기존 관리 화면 흐름과 호환됩니다.

@c5ln c5ln requested review from lejuho and xoruddl August 25, 2025 11:08
@c5ln c5ln self-assigned this Aug 25, 2025
@c5ln c5ln added the Feature 기능 추가 시 label Aug 25, 2025
@c5ln c5ln linked an issue Aug 25, 2025 that may be closed by this pull request
4 tasks
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 25, 2025

Walkthrough

관리자 API에 퀴즈 삭제 엔드포인트(DELETE /quiz/{quizId})가 추가되었고, 서비스 계층에 해당 삭제 로직이 구현되었습니다. 존재하지 않는 퀴즈 ID일 경우 예외를 던지며, 삭제 성공 시 boolean을 반환합니다. 기존 생성 엔드포인트는 변경 없습니다.

Changes

Cohort / File(s) Change Summary
Admin Controller
src/main/java/com/example/cp_main_be/domain/admin/presentation/AdminController.java
DELETE /quiz/{quizId} 엔드포인트 추가: deleteQuiz(Long quizId)adminService.deleteQuiz(quizId) 호출 후 ApiResponse<Boolean> 반환.
Admin Service
src/main/java/com/example/cp_main_be/domain/admin/service/AdminService.java
deleteQuiz(Long quizId) 추가: quizRepository.findById로 조회, 없으면 QuizNotFoundException 발생, 있으면 삭제 후 true 반환. 관련 예외 import 추가.

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
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

  • [Feat] 퀴즈 삭제 API #86 — 퀴즈 삭제 API 추가 및 서비스 삭제 로직 구현이 이 이슈의 목표와 직접적으로 일치합니다.

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 Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/delete-quiz

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.

❤️ Share
🪧 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.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in 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 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/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@c5ln c5ln merged commit 6f8c20f into develop Aug 25, 2025
8 of 11 checks passed
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a 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 (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.

📥 Commits

Reviewing files that changed from the base of the PR and between 728b157 and 541fda1.

📒 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 정렬/공백/줄바꿈 등이 자동 반영됩니다.

Comment on lines +95 to +101
@Transactional
public boolean deleteQuiz(Long quizId) {
Quiz quiz = quizRepository.findById(quizId)
.orElseThrow(() -> new QuizNotFoundException("퀴즈가 존재하지 않습니다."));
quizRepository.delete(quiz);
return true;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature 기능 추가 시

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 퀴즈 삭제 API

1 participant