Skip to content

MOSU-77 feat: 관리자 학교 등록#82

Merged
wlgns12370 merged 5 commits intodevelopfrom
feature/mosu-77
Jul 11, 2025
Merged

MOSU-77 feat: 관리자 학교 등록#82
wlgns12370 merged 5 commits intodevelopfrom
feature/mosu-77

Conversation

@wlgns12370
Copy link
Contributor

@wlgns12370 wlgns12370 commented Jul 10, 2025

✨ 구현한 기능

  • 관리자 학교 등록 기능

📢 논의하고 싶은 내용

  • x

🎸 기타

  • x

Summary by CodeRabbit

  • New Features
    • Added a POST endpoint for school registration, allowing users to create new schools via the API.
    • Introduced input validation and API documentation for school registration requests.
    • Implemented automatic initialization of sample school data when the application starts (if no schools exist).
  • Improvements
    • Enhanced object creation for school entities with a builder pattern for more flexible instantiation.
  • Bug Fixes
    • Ensured transactional handling is applied specifically to read and write operations for school data.

@coderabbitai
Copy link

coderabbitai bot commented Jul 10, 2025

Walkthrough

This update introduces a new school registration feature, including a POST endpoint, DTO, and service logic. It refactors repository usage by removing SchoolRepository in favor of SchoolJpaRepository, adds a database initializer for dummy schools, and applies the builder pattern to the SchoolJpaEntity constructor. Transactional annotations are refined for clarity.

Changes

Files/Paths Change Summary
.../SchoolController.java Added POST endpoint for school registration.
.../SchoolService.java Added registerSchool method; moved transactional annotation to method level.
.../SchoolRegistrationRequest.java New DTO record for school registration with validation and entity conversion method.
.../SchoolJpaEntity.java Added Lombok @Builder to constructor.
.../SchoolJpaRepository.java Added countBySchoolNameGroupBy() method for application count projections.
.../SchoolQuotaCacheManager.java Changed repository field/type from SchoolRepository to SchoolJpaRepository.
.../SchoolRepository.java Deleted file; custom query method moved to SchoolJpaRepository.
.../SchoolInitializer.java New component to initialize dummy school data at startup.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant SchoolController
    participant SchoolService
    participant SchoolJpaRepository

    Client->>SchoolController: POST /school (SchoolRegistrationRequest)
    SchoolController->>SchoolService: registerSchool(request)
    SchoolService->>SchoolRegistrationRequest: toEntity()
    SchoolService->>SchoolJpaRepository: save(SchoolJpaEntity)
    SchoolJpaRepository-->>SchoolService: (persisted entity)
    SchoolService-->>SchoolController: void
    SchoolController-->>Client: 201 Created (ApiResponseWrapper)
Loading

Possibly related PRs

  • mosu-dev/mosu-server#63: Extends SchoolService with retrieval; this PR adds registration to the same service.
  • mosu-dev/mosu-server#74: Introduced SchoolQuotaCacheManager using SchoolRepository; this PR updates it to use SchoolJpaRepository.

Suggested reviewers

  • polyglot-k

Poem

In the garden of code where new schools bloom,
A POST request dances, dispelling the gloom.
With DTO seeds and builder delight,
Repositories shift in the soft server light.
Dummy schools awaken, the database cheers—
Hopping forward, new learning appears! 🐇✨

✨ 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 @wlgns12370, 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 introduces the core functionality for administrators to register new schools within the system. It encompasses the full stack from a new API endpoint and request DTO to the service layer logic for data persistence, including updates to the SchoolJpaEntity for easier creation. Furthermore, it adds an initializer to populate initial school data and includes a minor repository refactoring along with a new query for school application statistics.

Highlights

  • New School Registration API: I've implemented a new API endpoint (POST /schools) in SchoolController.java that allows administrators to register new schools. This endpoint accepts a SchoolRegistrationRequest in the request body.
  • School Registration Service Logic: The SchoolService.java now includes a registerSchool() method. This method handles the business logic for persisting new SchoolJpaEntity instances based on the provided registration request, ensuring proper data storage.
  • New School Registration DTO: A new DTO, SchoolRegistrationRequest.java, has been introduced. This record defines the structure for incoming school registration data, including validation annotations for schoolName, area, address, examDate, and capacity, and provides a toEntity() method for conversion to a SchoolJpaEntity.
  • Initial School Data Population: A new SchoolInitializer.java component has been added. This component uses a @PostConstruct method to pre-populate the database with dummy school data if no schools are present, which is beneficial for development and testing environments.
  • Entity Builder Pattern Adoption: The SchoolJpaEntity.java has been updated with Lombok's @Builder annotation on both the class and its constructor. This facilitates a more readable and flexible way to construct SchoolJpaEntity instances, as seen in the new SchoolRegistrationRequest.toEntity() method and SchoolInitializer.
  • Repository Refactoring and New Query: In SchoolQuotaCacheManager.java, SchoolRepository has been consistently renamed to SchoolJpaRepository. Additionally, SchoolJpaRepository.java now includes a new custom JPQL query, countBySchoolNameGroupBy(), designed to retrieve school application counts, which could be used for reporting or dashboard features.
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.

@wlgns12370 wlgns12370 self-assigned this Jul 10, 2025
@wlgns12370 wlgns12370 linked an issue Jul 10, 2025 that may be closed by this pull request
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 a school registration feature. Key improvements include adding request validation in the controller, using the Area enum in the request DTO, preventing duplicate school registrations, and restricting the data initializer to non-production environments. These changes will enhance the reliability and data integrity of the new feature.


@PostMapping
public ResponseEntity<ApiResponseWrapper<Void>> create(
@RequestBody SchoolRegistrationRequest request

Choose a reason for hiding this comment

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

critical

Add @Valid annotation to the request parameter to enforce validation constraints defined in SchoolRegistrationRequest.

Suggested change
@RequestBody SchoolRegistrationRequest request
@Valid @RequestBody SchoolRegistrationRequest request

Comment on lines +28 to +30
public void registerSchool(SchoolRegistrationRequest request) {
schoolJpaRepository.save(request.toEntity());
}

Choose a reason for hiding this comment

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

high

Consider checking for an existing school with the same name, area, and exam date before saving to prevent duplicate entries and maintain data integrity.

    public void registerSchool(SchoolRegistrationRequest request) {
        if (schoolJpaRepository.findBySchoolNameAndAreaAndExamDate(request.schoolName(), Area.valueOf(request.area()), request.examDate()).isPresent()) {
            throw new IllegalArgumentException("A school with the same name, area, and exam date already exists.");
        }
        schoolJpaRepository.save(request.toEntity());
    }

Comment on lines +1 to +65
package life.mosu.mosuserver.global.initializer;

import static life.mosu.mosuserver.domain.school.Area.DAECHI;
import static life.mosu.mosuserver.domain.school.Area.NOWON;

import jakarta.annotation.PostConstruct;
import java.time.LocalDate;
import life.mosu.mosuserver.domain.school.AddressJpaVO;
import life.mosu.mosuserver.domain.school.SchoolJpaEntity;
import life.mosu.mosuserver.domain.school.SchoolJpaRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@RequiredArgsConstructor
public class SchoolInitializer {

private final SchoolJpaRepository schoolRepository;

@PostConstruct
public void init() {
if (schoolRepository.count() > 0) {
log.info("이미 학교 더미 데이터가 존재하여 초기화를 건너뜁니다.");
return;
}

SchoolJpaEntity school1 = SchoolJpaEntity.builder()
.schoolName("모수고등학교")
.area(DAECHI)
.address(new AddressJpaVO("06164", "서울특별시", "강남구 테헤란로 123"))
.examDate(LocalDate.of(2025, 11, 20))
.capacity(300L)
.build();
schoolRepository.save(school1);

SchoolJpaEntity school2 = SchoolJpaEntity.builder()
.schoolName("대치고등학교")
.area(DAECHI)
.address(new AddressJpaVO("06283", "서울특별시", "강남구 대치동 학원가 100"))
.examDate(LocalDate.of(2025, 12, 5))
.capacity(150L)
.build();
schoolRepository.save(school2);

SchoolJpaEntity school3 = SchoolJpaEntity.builder()
.schoolName("부산명문고")
.area(DAECHI)
.address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
.examDate(LocalDate.of(2025, 11, 25))
.capacity(250L)
.build();
schoolRepository.save(school3);

SchoolJpaEntity school4 = SchoolJpaEntity.builder()
.schoolName("노원스터디센터")
.area(NOWON)
.address(new AddressJpaVO("01777", "서울특별시", "노원구 동일로 1400"))
.examDate(LocalDate.of(2026, 1, 10))
.capacity(80L)
.build();
schoolRepository.save(school4);
}
} No newline at end of file

Choose a reason for hiding this comment

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

high

Restrict this initializer to run only in non-production environments to avoid inserting test data into production. Use @Profile annotation to achieve this.

@Profile("!prod")
@Slf4j
@Component
@RequiredArgsConstructor
public class SchoolInitializer {


@Schema(description = "지역 (예: DAECHI, MOKDONG, NOWON)", example = "DAECHI")
@NotNull(message = "지역은 필수 입니다.")
String area,

Choose a reason for hiding this comment

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

high

Use the Area enum directly for the area field to avoid potential IllegalArgumentException.

Suggested change
String area,
Area area,

Comment on lines +29 to +64
SchoolJpaEntity school1 = SchoolJpaEntity.builder()
.schoolName("모수고등학교")
.area(DAECHI)
.address(new AddressJpaVO("06164", "서울특별시", "강남구 테헤란로 123"))
.examDate(LocalDate.of(2025, 11, 20))
.capacity(300L)
.build();
schoolRepository.save(school1);

SchoolJpaEntity school2 = SchoolJpaEntity.builder()
.schoolName("대치고등학교")
.area(DAECHI)
.address(new AddressJpaVO("06283", "서울특별시", "강남구 대치동 학원가 100"))
.examDate(LocalDate.of(2025, 12, 5))
.capacity(150L)
.build();
schoolRepository.save(school2);

SchoolJpaEntity school3 = SchoolJpaEntity.builder()
.schoolName("부산명문고")
.area(DAECHI)
.address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
.examDate(LocalDate.of(2025, 11, 25))
.capacity(250L)
.build();
schoolRepository.save(school3);

SchoolJpaEntity school4 = SchoolJpaEntity.builder()
.schoolName("노원스터디센터")
.area(NOWON)
.address(new AddressJpaVO("01777", "서울특별시", "노원구 동일로 1400"))
.examDate(LocalDate.of(2026, 1, 10))
.capacity(80L)
.build();
schoolRepository.save(school4);
}

Choose a reason for hiding this comment

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

medium

Use saveAll() to persist the school entities in a single batch operation for better efficiency.

        List<SchoolJpaEntity> schools = List.of(
                SchoolJpaEntity.builder()
                        .schoolName("모수고등학교")
                        .area(DAECHI)
                        .address(new AddressJpaVO("06164", "서울특별시", "강남구 테헤란로 123"))
                        .examDate(LocalDate.of(2025, 11, 20))
                        .capacity(300L)
                        .build(),
                SchoolJpaEntity.builder()
                        .schoolName("대치고등학교")
                        .area(DAECHI)
                        .address(new AddressJpaVO("06283", "서울특별시", "강남구 대치동 학원가 100"))
                        .examDate(LocalDate.of(2025, 12, 5))
                        .capacity(150L)
                        .build(),
                SchoolJpaEntity.builder()
                        .schoolName("부산명문고")
                        .area(DAECHI)
                        .address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
                        .examDate(LocalDate.of(2025, 11, 25))
                        .capacity(250L)
                        .build(),
                SchoolJpaEntity.builder()
                        .schoolName("노원스터디센터")
                        .area(NOWON)
                        .address(new AddressJpaVO("01777", "서울특별시", "노원구 동일로 1400"))
                        .examDate(LocalDate.of(2026, 1, 10))
                        .capacity(80L)
                        .build()
        );
        schoolRepository.saveAll(schools);

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: 4

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

24-31: Consider adding validation annotation for request body.

The POST endpoint implementation follows REST conventions well. However, consider adding @Valid annotation to the request parameter to ensure proper validation of the SchoolRegistrationRequest DTO.

 @PostMapping
 public ResponseEntity<ApiResponseWrapper<Void>> create(
-        @RequestBody SchoolRegistrationRequest request
+        @RequestBody @Valid SchoolRegistrationRequest request
 ) {

Don't forget to add the import:

+import jakarta.validation.Valid;
src/main/java/life/mosu/mosuserver/application/school/SchoolService.java (1)

27-30: Consider adding validation and return value for school registration.

The registerSchool method implementation is straightforward but could be enhanced:

  1. Validation: Consider checking for duplicate schools before saving
  2. Return value: Consider returning the created school ID or entity for confirmation
  3. Error handling: Database constraint violations should be properly handled
 @Transactional
-public void registerSchool(SchoolRegistrationRequest request) {
-    schoolJpaRepository.save(request.toEntity());
+public SchoolJpaEntity registerSchool(SchoolRegistrationRequest request) {
+    // Optional: Add duplicate check
+    // if (schoolJpaRepository.findBySchoolNameAndAreaAndExamDate(...).isPresent()) {
+    //     throw new IllegalArgumentException("School already exists");
+    // }
+    return schoolJpaRepository.save(request.toEntity());
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 856615e and d904cbc.

📒 Files selected for processing (8)
  • src/main/java/life/mosu/mosuserver/application/school/SchoolQuotaCacheManager.java (2 hunks)
  • 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/domain/school/SchoolJpaRepository.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/domain/school/SchoolRepository.java (0 hunks)
  • src/main/java/life/mosu/mosuserver/global/initializer/SchoolInitializer.java (1 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/school/SchoolController.java (2 hunks)
  • src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolRegistrationRequest.java (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/java/life/mosu/mosuserver/domain/school/SchoolRepository.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/life/mosu/mosuserver/global/initializer/SchoolInitializer.java (1)
src/main/java/life/mosu/mosuserver/global/runner/ApplicationSchoolPreWarmRunner.java (1)
  • RequiredArgsConstructor (9-19)
⏰ 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: build-and-deploy
🔇 Additional comments (6)
src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaEntity.java (1)

45-45: LGTM! Builder pattern implementation is well-placed.

The @Builder annotation on the constructor enables flexible entity creation, which aligns well with the new school registration functionality where DTOs need to convert to entities.

src/main/java/life/mosu/mosuserver/application/school/SchoolQuotaCacheManager.java (1)

6-6: LGTM! Repository interface refactoring is consistent.

The migration from SchoolRepository to SchoolJpaRepository is properly implemented with both import and field declaration updated accordingly.

Also applies to: 19-19

src/main/java/life/mosu/mosuserver/domain/school/SchoolJpaRepository.java (1)

11-17: LGTM! Well-structured JPQL query implementation.

The countBySchoolNameGroupBy() method is properly implemented with:

  • Correct LEFT JOIN to include schools without applications
  • Proper GROUP BY clause including both ID and name
  • Appropriate projection mapping to SchoolApplicationProjection
src/main/java/life/mosu/mosuserver/application/school/SchoolService.java (1)

19-19: LGTM! Improved transactional annotation granularity.

Moving from class-level to method-level @Transactional annotations provides better control over transaction boundaries and makes the read-only nature of getSchools() explicit.

src/main/java/life/mosu/mosuserver/global/initializer/SchoolInitializer.java (1)

22-27: LGTM! Good defensive programming practice.

The conditional check prevents duplicate initialization when the application restarts, which is a solid defensive programming approach.

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

14-36: LGTM! Comprehensive validation and documentation.

The record definition includes appropriate validation annotations and detailed Swagger documentation, providing good API contract definition.

Comment on lines +47 to +54
SchoolJpaEntity school3 = SchoolJpaEntity.builder()
.schoolName("부산명문고")
.area(DAECHI)
.address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
.examDate(LocalDate.of(2025, 11, 25))
.capacity(250L)
.build();
schoolRepository.save(school3);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix logical inconsistency in school location data.

The school "부산명문고" (Busan school) has an address in Busan ("부산광역시 해운대구") but is assigned to the DAECHI area, which is inconsistent.

Apply this diff to fix the area assignment:

-        SchoolJpaEntity school3 = SchoolJpaEntity.builder()
-                .schoolName("부산명문고")
-                .area(DAECHI)
-                .address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
-                .examDate(LocalDate.of(2025, 11, 25))
-                .capacity(250L)
-                .build();
+        SchoolJpaEntity school3 = SchoolJpaEntity.builder()
+                .schoolName("부산명문고")
+                .area(BUSAN) // or create appropriate enum value
+                .address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
+                .examDate(LocalDate.of(2025, 11, 25))
+                .capacity(250L)
+                .build();
📝 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
SchoolJpaEntity school3 = SchoolJpaEntity.builder()
.schoolName("부산명문고")
.area(DAECHI)
.address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
.examDate(LocalDate.of(2025, 11, 25))
.capacity(250L)
.build();
schoolRepository.save(school3);
SchoolJpaEntity school3 = SchoolJpaEntity.builder()
.schoolName("부산명문고")
.area(BUSAN)
.address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
.examDate(LocalDate.of(2025, 11, 25))
.capacity(250L)
.build();
schoolRepository.save(school3);
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/global/initializer/SchoolInitializer.java
around lines 47 to 54, the school "부산명문고" has an address in Busan but is
incorrectly assigned to the DAECHI area. Change the area assignment from DAECHI
to the correct Busan-related area constant to ensure consistency between the
area and address fields.

Comment on lines +22 to +64
@PostConstruct
public void init() {
if (schoolRepository.count() > 0) {
log.info("이미 학교 더미 데이터가 존재하여 초기화를 건너뜁니다.");
return;
}

SchoolJpaEntity school1 = SchoolJpaEntity.builder()
.schoolName("모수고등학교")
.area(DAECHI)
.address(new AddressJpaVO("06164", "서울특별시", "강남구 테헤란로 123"))
.examDate(LocalDate.of(2025, 11, 20))
.capacity(300L)
.build();
schoolRepository.save(school1);

SchoolJpaEntity school2 = SchoolJpaEntity.builder()
.schoolName("대치고등학교")
.area(DAECHI)
.address(new AddressJpaVO("06283", "서울특별시", "강남구 대치동 학원가 100"))
.examDate(LocalDate.of(2025, 12, 5))
.capacity(150L)
.build();
schoolRepository.save(school2);

SchoolJpaEntity school3 = SchoolJpaEntity.builder()
.schoolName("부산명문고")
.area(DAECHI)
.address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
.examDate(LocalDate.of(2025, 11, 25))
.capacity(250L)
.build();
schoolRepository.save(school3);

SchoolJpaEntity school4 = SchoolJpaEntity.builder()
.schoolName("노원스터디센터")
.area(NOWON)
.address(new AddressJpaVO("01777", "서울특별시", "노원구 동일로 1400"))
.examDate(LocalDate.of(2026, 1, 10))
.capacity(80L)
.build();
schoolRepository.save(school4);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider adding transaction management for data consistency.

The initialization method performs multiple repository saves without explicit transaction management, which could lead to partial data if an error occurs during initialization.

Consider adding @Transactional annotation:

+import org.springframework.transaction.annotation.Transactional;

 @PostConstruct
+@Transactional
 public void init() {
📝 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
@PostConstruct
public void init() {
if (schoolRepository.count() > 0) {
log.info("이미 학교 더미 데이터가 존재하여 초기화를 건너뜁니다.");
return;
}
SchoolJpaEntity school1 = SchoolJpaEntity.builder()
.schoolName("모수고등학교")
.area(DAECHI)
.address(new AddressJpaVO("06164", "서울특별시", "강남구 테헤란로 123"))
.examDate(LocalDate.of(2025, 11, 20))
.capacity(300L)
.build();
schoolRepository.save(school1);
SchoolJpaEntity school2 = SchoolJpaEntity.builder()
.schoolName("대치고등학교")
.area(DAECHI)
.address(new AddressJpaVO("06283", "서울특별시", "강남구 대치동 학원가 100"))
.examDate(LocalDate.of(2025, 12, 5))
.capacity(150L)
.build();
schoolRepository.save(school2);
SchoolJpaEntity school3 = SchoolJpaEntity.builder()
.schoolName("부산명문고")
.area(DAECHI)
.address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
.examDate(LocalDate.of(2025, 11, 25))
.capacity(250L)
.build();
schoolRepository.save(school3);
SchoolJpaEntity school4 = SchoolJpaEntity.builder()
.schoolName("노원스터디센터")
.area(NOWON)
.address(new AddressJpaVO("01777", "서울특별시", "노원구 동일로 1400"))
.examDate(LocalDate.of(2026, 1, 10))
.capacity(80L)
.build();
schoolRepository.save(school4);
}
// at the top of the file, among your other imports:
import org.springframework.transaction.annotation.Transactional;
@PostConstruct
@Transactional
public void init() {
if (schoolRepository.count() > 0) {
log.info("이미 학교 더미 데이터가 존재하여 초기화를 건너뜁니다.");
return;
}
SchoolJpaEntity school1 = SchoolJpaEntity.builder()
.schoolName("모수고등학교")
.area(DAECHI)
.address(new AddressJpaVO("06164", "서울특별시", "강남구 테헤란로 123"))
.examDate(LocalDate.of(2025, 11, 20))
.capacity(300L)
.build();
schoolRepository.save(school1);
SchoolJpaEntity school2 = SchoolJpaEntity.builder()
.schoolName("대치고등학교")
.area(DAECHI)
.address(new AddressJpaVO("06283", "서울특별시", "강남구 대치동 학원가 100"))
.examDate(LocalDate.of(2025, 12, 5))
.capacity(150L)
.build();
schoolRepository.save(school2);
SchoolJpaEntity school3 = SchoolJpaEntity.builder()
.schoolName("부산명문고")
.area(DAECHI)
.address(new AddressJpaVO("48057", "부산광역시", "해운대구 센텀시티로 50"))
.examDate(LocalDate.of(2025, 11, 25))
.capacity(250L)
.build();
schoolRepository.save(school3);
SchoolJpaEntity school4 = SchoolJpaEntity.builder()
.schoolName("노원스터디센터")
.area(NOWON)
.address(new AddressJpaVO("01777", "서울특별시", "노원구 동일로 1400"))
.examDate(LocalDate.of(2026, 1, 10))
.capacity(80L)
.build();
schoolRepository.save(school4);
}
🤖 Prompt for AI Agents
In src/main/java/life/mosu/mosuserver/global/initializer/SchoolInitializer.java
around lines 22 to 64, the init method performs multiple save operations without
transaction management, risking partial data persistence if an error occurs. Add
the @Transactional annotation to the init method to ensure all save operations
are executed within a single transaction, maintaining data consistency by
rolling back all changes if any save fails.

Comment on lines +20 to +22
@Schema(description = "지역 (예: DAECHI, MOKDONG, NOWON)", example = "DAECHI")
@NotNull(message = "지역은 필수 입니다.")
String area,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add validation for area enum values.

The area field accepts any String but gets converted to an enum using valueOf(), which can throw IllegalArgumentException for invalid values.

Consider adding custom validation:

+import jakarta.validation.constraints.Pattern;

 @Schema(description = "지역 (예: DAECHI, MOKDONG, NOWON)", example = "DAECHI")
 @NotNull(message = "지역은 필수 입니다.")
+@Pattern(regexp = "DAECHI|MOKDONG|NOWON", message = "지역은 DAECHI, MOKDONG, NOWON 중 하나여야 합니다.")
 String area,
📝 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 = "지역 (예: DAECHI, MOKDONG, NOWON)", example = "DAECHI")
@NotNull(message = "지역은 필수 입니다.")
String area,
// add this import alongside the other jakarta.validation imports
import jakarta.validation.constraints.Pattern;
@Schema(description = "지역 (예: DAECHI, MOKDONG, NOWON)", example = "DAECHI")
@NotNull(message = "지역은 필수 입니다.")
@Pattern(regexp = "DAECHI|MOKDONG|NOWON", message = "지역은 DAECHI, MOKDONG, NOWON 중 하나여야 합니다.")
String area,
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolRegistrationRequest.java
around lines 20 to 22, the area field is a String that is later converted to an
enum using valueOf(), which can throw an exception if the value is invalid. To
fix this, add a custom validator annotation to the area field that checks if the
String matches one of the allowed enum values before conversion. Implement a
validator class that validates the input against the enum constants and apply
this annotation to the area field to ensure only valid enum values are accepted.

Comment on lines +38 to +47
public SchoolJpaEntity toEntity() {
AddressJpaVO address = address().toValueObject();
return SchoolJpaEntity.builder()
.schoolName(schoolName)
.area(Area.valueOf(area))
.address(address)
.examDate(examDate)
.capacity(capacity)
.build();
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix variable name shadowing in toEntity() method.

The local variable address shadows the record field address(), which can lead to confusion and potential bugs.

Apply this diff to use a distinct variable name:

 public SchoolJpaEntity toEntity() {
-    AddressJpaVO address = address().toValueObject();
+    AddressJpaVO addressVO = address().toValueObject();
     return SchoolJpaEntity.builder()
             .schoolName(schoolName)
             .area(Area.valueOf(area))
-            .address(address)
+            .address(addressVO)
             .examDate(examDate)
             .capacity(capacity)
             .build();
 }
📝 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 SchoolJpaEntity toEntity() {
AddressJpaVO address = address().toValueObject();
return SchoolJpaEntity.builder()
.schoolName(schoolName)
.area(Area.valueOf(area))
.address(address)
.examDate(examDate)
.capacity(capacity)
.build();
}
public SchoolJpaEntity toEntity() {
AddressJpaVO addressVO = address().toValueObject();
return SchoolJpaEntity.builder()
.schoolName(schoolName)
.area(Area.valueOf(area))
.address(addressVO)
.examDate(examDate)
.capacity(capacity)
.build();
}
🤖 Prompt for AI Agents
In
src/main/java/life/mosu/mosuserver/presentation/school/dto/SchoolRegistrationRequest.java
between lines 38 and 47, the local variable named 'address' in the toEntity()
method shadows the record field 'address()', causing confusion. Rename the local
variable to a distinct name such as 'addressVO' to clearly differentiate it from
the record field and avoid shadowing issues.

@wlgns12370 wlgns12370 merged commit 91a81bf into develop Jul 11, 2025
2 checks passed
@wlgns12370 wlgns12370 deleted the feature/mosu-77 branch July 11, 2025 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[🛠 기능 구현] 관리자 학교 등록

1 participant