Skip to content

MOSU-98 feat: 학교 수정/삭제 기능 구현#102

Merged
jbh010204 merged 5 commits intodevelopfrom
feature/mosu-98
Jul 16, 2025
Merged

MOSU-98 feat: 학교 수정/삭제 기능 구현#102
jbh010204 merged 5 commits intodevelopfrom
feature/mosu-98

Conversation

@jbh010204
Copy link
Member

@jbh010204 jbh010204 commented Jul 16, 2025

✨ 구현한 기능

📢 논의하고 싶은 내용

🎸 기타

Summary by CodeRabbit

  • New Features
    • Added the ability to update and delete school information through new API endpoints.
    • Introduced validation and documentation for school update requests.
  • Bug Fixes
    • Improved error handling for cases where a school is not found during update or delete operations.

@coderabbitai
Copy link

coderabbitai bot commented Jul 16, 2025

Walkthrough

This change introduces update and delete capabilities for school entities, adding corresponding service methods, controller endpoints, and a validated DTO for school edits. The domain entity gains an update method to apply changes from the DTO. The controller now exposes PUT and DELETE endpoints for modifying and removing schools.

Changes

File(s) Change Summary
.../application/school/SchoolService.java Added deleteSchool and update transactional methods with error handling for missing schools.
.../domain/school/SchoolJpaEntity.java Added updateInfo method to update entity fields from SchoolEditRequest.
.../presentation/school/SchoolController.java Added PUT /school/{id} and DELETE /school/{id} endpoints for update and delete operations.
.../presentation/school/dto/SchoolEditRequest.java Introduced new validated DTO record for school edit requests with Swagger annotations.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant SchoolController
    participant SchoolService
    participant SchoolJpaEntity
    participant SchoolRepository

    Client->>SchoolController: PUT /school/{id} (SchoolEditRequest)
    SchoolController->>SchoolService: update(id, SchoolEditRequest)
    SchoolService->>SchoolRepository: findById(id)
    SchoolRepository-->>SchoolService: SchoolJpaEntity
    SchoolService->>SchoolJpaEntity: updateInfo(SchoolEditRequest)
    SchoolService-->>SchoolController: (void)
    SchoolController-->>Client: 200 OK

    Client->>SchoolController: DELETE /school/{id}
    SchoolController->>SchoolService: deleteSchool(id)
    SchoolService->>SchoolRepository: existsById(id)
    alt School exists
        SchoolService->>SchoolRepository: deleteById(id)
    else School not found
        SchoolService-->>SchoolController: throw CustomRuntimeException
    end
    SchoolController-->>Client: 200 OK
Loading

Possibly related PRs

  • MOSU-94 feat: 학교 필드 추가 #95: The main PR’s addition of the updateInfo method in SchoolJpaEntity uses the Area.from and Lunch.from methods introduced in the retrieved PR, showing a direct code-level dependency on the enum conversion logic added there.
  • MOSU-57 feat: 학교 조회 기능 구현 #63: The main PR extends the existing SchoolService by adding update and delete methods along with corresponding controller endpoints and entity update logic, while the retrieved PR only introduces the initial SchoolService class with a read-only getSchools() method and a basic controller for school retrieval, so their changes are related as the main PR builds upon the service introduced in the retrieved PR.
  • MOSU-57 chore: API 연동을 위한 Redis 비밀번호 제거 #65: The main PR adds update and delete methods to the existing SchoolService class, modifies SchoolJpaEntity with an updateInfo method, and extends SchoolController with corresponding endpoints, while the retrieved PR only introduces the initial SchoolService class with a getSchools method and a basic SchoolController with a getSchools endpoint; thus, the main PR builds directly upon and extends the same classes and methods introduced in the retrieved PR.

Suggested labels

우선순위 중

Suggested reviewers

  • wlgns12370
  • polyglot-k

Poem

A bunny hopped through code so bright,
Adding update, delete—oh what a delight!
Schools can now change, or even depart,
With DTOs and endpoints, it’s a fresh new start.
Hooray for the code, neat and concise—
This rabbit approves, it’s simply nice!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5e3543 and 919c870.

📒 Files selected for processing (2)
  • src/main/java/life/mosu/mosuserver/application/school/SchoolService.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/life/mosu/mosuserver/application/school/SchoolService.java
  • src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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 @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 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.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

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.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @jbh010204, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request implements new functionality for managing school records by introducing API endpoints and corresponding service and domain logic to allow for the update and deletion of existing school entities. It enhances the current school management capabilities by providing a structured way to modify school details and remove records from the system.

Highlights

  • School Management API Endpoints: New REST API endpoints have been added to SchoolController to support updating and deleting school records. Specifically, a PUT /{id} endpoint for updates and a DELETE /{id} endpoint for deletions are now available.
  • School Service Business Logic: The SchoolService now includes deleteSchool(Long id) and update(Long schoolId, SchoolEditRequest request) methods. These methods encapsulate the business logic for removing and modifying school entities, including validation to ensure the school exists before performing the operation.
  • School Entity Update Method: A new updateInfo(SchoolEditRequest request) method has been added to the SchoolJpaEntity. This method allows the entity to update its own fields based on the provided SchoolEditRequest DTO, promoting encapsulation and domain-driven design principles.
  • New School Edit Request DTO: A new data transfer object, SchoolEditRequest.java, has been introduced. This DTO defines the structure and validation rules for the data required when updating an existing school record, including fields for school name, address, exam date, lunch details, and capacity.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces functionality to update and delete school information. The changes include new controller endpoints, service methods, and an update method in the entity. The review focuses on improving efficiency in the delete operation, highlighting a critical security issue with disabled authorization, and suggesting improvements for validation consistency in the new DTO.

@jbh010204 jbh010204 changed the title Feature/mosu 98 MOSU-98 feat: 학교 수정/삭제 기능 구현 Jul 16, 2025
Copy link

@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: 5

🧹 Nitpick comments (2)
src/main/java/life/mosu/mosuserver/application/school/SchoolService.java (1)

36-43: Optimize delete operation to avoid double database call.

The current implementation makes two database calls - one to check existence and another to delete. This can be optimized to a single operation.

Consider using a single delete operation and checking the result:

@Transactional
public void deleteSchool(Long id) {
-    if (!schoolJpaRepository.existsById(id)) {
-        throw new CustomRuntimeException(ErrorCode.SCHOOL_NOT_FOUND);
-    }
-
-    schoolJpaRepository.deleteById(id);
+    SchoolJpaEntity school = schoolJpaRepository.findById(id)
+            .orElseThrow(() -> new CustomRuntimeException(ErrorCode.SCHOOL_NOT_FOUND));
+    
+    schoolJpaRepository.delete(school);
}

Or use a custom repository method that returns the number of deleted entities and check if it's zero.

src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolEditRequest.java (1)

37-39: Add validation message for consistency.

The deadlineTime field is missing a validation message for consistency with other fields.

-@NotNull
+@NotNull(message = "신청 마감 일시는 필수입니다.")
@Schema(description = "신청 마감 일시", example = "2025-05-30T10:00:00")
LocalDateTime deadlineTime,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 447e374 and a5e3543.

📒 Files selected for processing (4)
  • src/main/java/life/mosu/mosuserver/application/school/SchoolService.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaEntity.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolEditRequest.java (1 hunks)
🔇 Additional comments (3)
src/main/java/life/mosu/mosuserver/application/school/SchoolService.java (1)

45-52: Well-implemented update method with proper error handling.

The update method correctly:

  • Uses @Transactional for proper transaction management
  • Handles entity not found cases with appropriate exceptions
  • Leverages JPA dirty checking for automatic persistence
  • Follows clean architecture principles
src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolEditRequest.java (1)

11-46: Well-structured DTO with comprehensive validation.

The record effectively uses:

  • Proper validation annotations with descriptive messages
  • Comprehensive Swagger documentation
  • Appropriate data types for each field
  • Clean record syntax
src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java (1)

45-53: Well-implemented REST endpoints with proper HTTP semantics.

The endpoints correctly:

  • Use appropriate HTTP methods (PUT for update, DELETE for delete)
  • Return proper HTTP status codes
  • Follow consistent response structure with ApiResponseWrapper
  • Handle path parameters appropriately
  • Provide meaningful success messages

Also applies to: 55-62

import java.time.LocalDate;
import java.time.LocalDateTime;
import life.mosu.mosuserver.domain.application.Lunch;
import life.mosu.mosuserver.presentation.school.dto.SchoolEditRequest;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Architectural violation: Domain layer depends on presentation layer.

The domain entity should not import DTOs from the presentation layer. This violates the dependency inversion principle and creates tight coupling between layers.

Consider creating a domain-specific update command or moving the update logic to the service layer:

-import life.mosu.mosuserver.presentation.school.dto.SchoolEditRequest;

Move the update logic to the service layer or create a domain command object instead.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaEntity.java at line
16, remove the import of SchoolEditRequest from the presentation layer to avoid
domain layer depending on presentation. Refactor by either moving the update
logic that uses this DTO into the service layer or create a domain-specific
command object within the domain layer to handle updates, ensuring the domain
entity does not directly depend on presentation DTOs.

Comment on lines +71 to +80
public void updateInfo(SchoolEditRequest request) {
this.schoolName = request.schoolName();
this.area = Area.from(request.area());
this.address = request.schoolAddress().toValueObject();
this.examDate = request.examDate();
this.lunch = Lunch.from(request.lunch());
this.lunchPrice = request.lunchPrice();
this.deadlineTime = request.deadlineTime();
this.capacity = request.capacity();
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing validation and business logic in update method.

The updateInfo method performs direct field assignment without any validation, null checks, or business logic. This could lead to invalid entity states.

Consider adding validation and business logic:

public void updateInfo(SchoolEditRequest request) {
+    if (request == null) {
+        throw new IllegalArgumentException("Request cannot be null");
+    }
+    
+    // Add business validation here
+    if (request.capacity() != null && request.capacity() <= 0) {
+        throw new IllegalArgumentException("Capacity must be positive");
+    }
+    
    this.schoolName = request.schoolName();
    this.area = Area.from(request.area());
    this.address = request.schoolAddress().toValueObject();
    this.examDate = request.examDate();
    this.lunch = Lunch.from(request.lunch());
    this.lunchPrice = request.lunchPrice();
    this.deadlineTime = request.deadlineTime();
    this.capacity = request.capacity();
}

Alternatively, consider moving this logic to the service layer to maintain better separation of concerns.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void updateInfo(SchoolEditRequest request) {
this.schoolName = request.schoolName();
this.area = Area.from(request.area());
this.address = request.schoolAddress().toValueObject();
this.examDate = request.examDate();
this.lunch = Lunch.from(request.lunch());
this.lunchPrice = request.lunchPrice();
this.deadlineTime = request.deadlineTime();
this.capacity = request.capacity();
}
public void updateInfo(SchoolEditRequest request) {
if (request == null) {
throw new IllegalArgumentException("Request cannot be null");
}
// Add business validation here
if (request.capacity() != null && request.capacity() <= 0) {
throw new IllegalArgumentException("Capacity must be positive");
}
this.schoolName = request.schoolName();
this.area = Area.from(request.area());
this.address = request.schoolAddress().toValueObject();
this.examDate = request.examDate();
this.lunch = Lunch.from(request.lunch());
this.lunchPrice = request.lunchPrice();
this.deadlineTime = request.deadlineTime();
this.capacity = request.capacity();
}
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaEntity.java around
lines 71 to 80, the updateInfo method directly assigns fields from the request
without validation or business logic, risking invalid entity states. To fix
this, add appropriate null checks and validation for each field before
assignment, and enforce any business rules relevant to the entity's state.
Alternatively, refactor to move validation and business logic to the service
layer, keeping the entity update method focused on state changes only.

Comment on lines +29 to +35
@Schema(description = "도시락 메뉴")
@NotNull
String lunch,

@Schema(description = "도시락 가격")
@NotNull
Integer lunchPrice,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve validation for lunch and lunchPrice fields.

The lunch field should use @NotBlank instead of @NotNull for string validation, and lunchPrice should have @Positive constraint to prevent negative values.

Apply these validation improvements:

@Schema(description = "도시락 메뉴")
-@NotNull
+@NotBlank(message = "도시락 메뉴는 필수입니다.")
String lunch,

@Schema(description = "도시락 가격")
-@NotNull
+@NotNull(message = "도시락 가격은 필수입니다.")
+@Positive(message = "도시락 가격은 양수여야 합니다.")
Integer lunchPrice,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Schema(description = "도시락 메뉴")
@NotNull
String lunch,
@Schema(description = "도시락 가격")
@NotNull
Integer lunchPrice,
@Schema(description = "도시락 메뉴")
@NotBlank(message = "도시락 메뉴는 필수입니다.")
String lunch,
@Schema(description = "도시락 가격")
@NotNull(message = "도시락 가격은 필수입니다.")
@Positive(message = "도시락 가격은 양수여야 합니다.")
Integer lunchPrice,
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolEditRequest.java
around lines 29 to 35, replace the @NotNull annotation on the lunch field with
@NotBlank to ensure the string is not empty or just whitespace. For the
lunchPrice field, add the @Positive annotation to enforce that the price must be
a positive integer, preventing negative values. This improves validation by
ensuring lunch is a meaningful string and lunchPrice is a valid positive number.

}

@PutMapping("/{id}")
// @PreAuthorize("isAuthenticated() and hasRole('ADMIN')")
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Security vulnerability: Authentication and authorization disabled.

The security annotations are commented out, which means these endpoints are accessible without authentication or authorization. This is a critical security issue for update and delete operations.

Uncomment and verify the security configuration:

@PutMapping("/{id}")
-// @PreAuthorize("isAuthenticated() and hasRole('ADMIN')")
+@PreAuthorize("isAuthenticated() and hasRole('ADMIN')")
public ResponseEntity<ApiResponseWrapper<Void>> update(
@DeleteMapping("/{id}")
-// @PreAuthorize("isAuthenticated() and hasRole('ADMIN')")
+@PreAuthorize("isAuthenticated() and hasRole('ADMIN')")
public ResponseEntity<ApiResponseWrapper<Void>> delete(

Ensure that the security configuration is properly set up to handle these annotations.

Also applies to: 56-56

🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java
at lines 46 and 56, the @PreAuthorize annotations are commented out, disabling
authentication and authorization for critical endpoints. Uncomment these
annotations to enforce security checks, and verify that the overall security
configuration supports these annotations to properly restrict access to update
and delete operations.

Comment on lines +47 to +49
public ResponseEntity<ApiResponseWrapper<Void>> update(
@RequestBody SchoolEditRequest request,
@PathVariable Long id
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Missing request validation annotation.

The @Valid annotation is missing from the request parameter, which means the validation constraints in SchoolEditRequest won't be enforced.

Add validation annotation:

public ResponseEntity<ApiResponseWrapper<Void>> update(
-        @RequestBody SchoolEditRequest request,
+        @RequestBody @Valid SchoolEditRequest request,
        @PathVariable Long id
) {

Don't forget to add the import for @Valid:

+import jakarta.validation.Valid;
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java
around lines 47 to 49, the @Valid annotation is missing on the SchoolEditRequest
parameter in the update method, so validation constraints won't be enforced. Add
the @Valid annotation before the @RequestBody annotation on the request
parameter and import javax.validation.Valid to enable validation of the incoming
request.

@jbh010204 jbh010204 merged commit f39d280 into develop Jul 16, 2025
2 checks passed
@jbh010204 jbh010204 deleted the feature/mosu-98 branch July 16, 2025 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant