Skip to content

Conversation

@Jjiggu
Copy link
Collaborator

@Jjiggu Jjiggu commented Jun 2, 2025

작업 요약

  • Store Create
  • Store 전체 Read
  • Store 하나 Read
  • Store Update
  • Store Delete

Issue Link

#8

문제점 및 어려움

x

해결 방안

Reference

Summary by CodeRabbit

  • 신규 기능

    • 매장(Store) 생성, 조회, 수정, 삭제를 위한 REST API 엔드포인트가 추가되었습니다.
    • 매장 정보를 위한 데이터 전송 객체(DTO) 및 JPA 엔티티가 도입되었습니다.
    • 매장 관리 서비스 및 데이터 저장소가 구현되었습니다.
    • 생성일 자동 기록을 위한 공통 엔티티 기반 클래스가 도입되었습니다.
  • 버그 수정

    • 기존 TODO 관련 컨트롤러, 서비스, 저장소가 제거되었습니다.
  • 문서화

    • OpenAPI 스키마 및 엔티티 설명이 추가되었습니다.
  • 테스트

    • 매장 컨트롤러, 서비스, 저장소에 대한 단위 테스트 및 통합 테스트가 추가되었습니다.
  • 기타

    • 의존성 및 검증, API 문서화, 테스트 관련 라이브러리가 추가되었습니다.
    • JPA 감사 기능이 활성화되었습니다.

Jjiggu added 26 commits May 30, 2025 20:20
- Introduce abstract BaseTimeEntity class with createdAt field
- Apply JPA @MappedSuperclass, @EntityListeners for auditing
- Support JSON (de)serialization for LocalDateTime (createdAt)
- Add Swagger, Lombok annotations for documentation and builder pattern
- Refactor createStore to return response from saved entity, not just from toEntity()
- Ensures returned response contains values updated by persistence (e.g. id, timestamps)
- Improves consistency with JPA entity lifecycle
@Jjiggu Jjiggu self-assigned this Jun 2, 2025
@Jjiggu Jjiggu added the enhancement New feature or request label Jun 2, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jun 2, 2025

"""

Walkthrough

이번 변경 사항은 Store(매장) 관리 기능을 대대적으로 도입하고, 관련된 엔티티, DTO, 서비스, 컨트롤러, 리포지토리 계층을 새로 추가하였습니다. 또한 JPA 감사(Auditing) 기능이 활성화되었고, 기존 TODO 관련 코드는 삭제되었습니다. 테스트 코드와 빌드 설정도 함께 보완되었습니다.

Changes

파일/경로 그룹 변경 요약
build.gradle 의존성 추가: Lombok, Spring Boot Validation, SpringDoc OpenAPI, Spring Security Test, H2 등
src/main/java/com/example/gtable/GTableApplication.java @EnableJpaAuditing 추가, 메인 메서드 포맷 변경
src/main/java/com/example/gtable/TODO/TodoController.java
src/main/java/com/example/gtable/TODO/TodoRepository.java
src/main/java/com/example/gtable/TODO/TodoService.java
TODO 관련 컨트롤러, 서비스, 리포지토리 삭제
src/main/java/com/example/gtable/global/entity/BaseTimeEntity.java JPA 감사용 추상 엔티티 클래스 신설, 생성일 자동 관리
src/main/java/com/example/gtable/store/controller/StoreController.java Store REST 컨트롤러 신규 추가
src/main/java/com/example/gtable/store/dto/StoreCreateRequest.java
src/main/java/com/example/gtable/store/dto/StoreCreateResponse.java
src/main/java/com/example/gtable/store/dto/StoreReadDto.java
src/main/java/com/example/gtable/store/dto/StoreReadResponse.java
src/main/java/com/example/gtable/store/dto/StoreUpdateRequest.java
Store 관련 요청/응답 DTO 일괄 추가
src/main/java/com/example/gtable/store/model/Store.java Store JPA 엔티티 신설, 생성일, 활성/삭제 플래그 등 포함
src/main/java/com/example/gtable/store/repository/StoreRepository.java Store 엔티티용 JPA 리포지토리 인터페이스 추가
src/main/java/com/example/gtable/store/service/StoreService.java
src/main/java/com/example/gtable/store/service/StoreServiceImpl.java
Store 서비스 인터페이스 및 구현체 추가
src/test/java/com/example/gtable/store/controller/StoreControllerTest.java
src/test/java/com/example/gtable/store/repository/StoreRepositoryTest.java
src/test/java/com/example/gtable/store/service/StoreServiceTest.java
Store 기능 단위/통합 테스트 신설

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant StoreController
    participant StoreService
    participant StoreRepository
    participant Store

    Client->>StoreController: POST /stores (StoreCreateRequest)
    StoreController->>StoreService: createStore(request)
    StoreService->>StoreRepository: save(Store)
    StoreRepository-->>StoreService: Store
    StoreService-->>StoreController: StoreCreateResponse
    StoreController-->>Client: 201 Created + ApiUtils.success(response)
Loading
sequenceDiagram
    participant Client
    participant StoreController
    participant StoreService
    participant StoreRepository

    Client->>StoreController: GET /stores/all-stores
    StoreController->>StoreService: getAllStores()
    StoreService->>StoreRepository: findAllByDeletedFalse()
    StoreRepository-->>StoreService: List<Store>
    StoreService-->>StoreController: StoreReadResponse
    StoreController-->>Client: 200 OK + ApiUtils.success(response)
Loading

Poem

🎉
새로운 Store의 탄생을 축하해요!
컨트롤러와 서비스, DTO도 총출동,
감사 시간도 자동으로 기록되고,
테스트도 꼼꼼하게 준비했네요.
TODO는 안녕, Store는 반가워요!
앞으로의 성장도 응원할게요.
🏪✨

"""


📜 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 37b26e1 and 005397c.

📒 Files selected for processing (7)
  • build.gradle (1 hunks)
  • src/main/java/com/example/gtable/store/dto/StoreCreateRequest.java (1 hunks)
  • src/main/java/com/example/gtable/store/dto/StoreCreateResponse.java (1 hunks)
  • src/main/java/com/example/gtable/store/dto/StoreUpdateRequest.java (1 hunks)
  • src/main/java/com/example/gtable/store/model/Store.java (1 hunks)
  • src/main/java/com/example/gtable/store/service/StoreServiceImpl.java (1 hunks)
  • src/test/java/com/example/gtable/store/repository/StoreRepositoryTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/main/java/com/example/gtable/store/dto/StoreCreateResponse.java
  • src/test/java/com/example/gtable/store/repository/StoreRepositoryTest.java
  • src/main/java/com/example/gtable/store/dto/StoreUpdateRequest.java
  • build.gradle
  • src/main/java/com/example/gtable/store/dto/StoreCreateRequest.java
  • src/main/java/com/example/gtable/store/service/StoreServiceImpl.java
  • src/main/java/com/example/gtable/store/model/Store.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

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

🧹 Nitpick comments (12)
src/main/java/com/example/gtable/global/entity/BaseTimeEntity.java (1)

26-26: @Schema 설명을 더 정확하게 개선해보세요

"시간 관련 VO"보다는 "생성 시간 추적을 위한 기본 엔티티"와 같이 더 구체적이고 정확한 설명을 사용하시면 좋겠습니다.

-@Schema(description = "시간 관련 VO")
+@Schema(description = "생성 시간 추적을 위한 기본 엔티티")
src/test/java/com/example/gtable/store/repository/StoreRepositoryTest.java (1)

35-47: 저장 및 조회 테스트 로직이 훌륭하지만 createdAt 검증이 누락되었습니다

모든 필드에 대한 검증이 꼼꼼하게 되어 있어서 좋습니다. 다만 BaseTimeEntity의 핵심 기능인 createdAt 필드에 대한 검증도 추가하시면 더 완전한 테스트가 될 것 같습니다.

 assertThat(found.get().getIsActive()).isTrue();
+assertThat(found.get().getCreatedAt()).isNotNull();
src/main/java/com/example/gtable/store/dto/StoreReadResponse.java (1)

12-23: 페이지네이션 응답 DTO 설계가 잘 되어 있습니다!

StoreReadResponse는 리스트와 hasNext 플래그를 포함한 깔끔한 페이지네이션 응답 구조를 가지고 있습니다.

다만 fromEntity 메서드명이 약간 혼란스러울 수 있습니다. 실제로는 엔티티에서 변환하는 것이 아니라 DTO 리스트를 래핑하는 역할이므로, 더 명확한 이름을 고려해 보세요:

-    public static StoreReadResponse fromEntity(List<StoreReadDto> storeReadDtos, boolean hasNext) {
+    public static StoreReadResponse of(List<StoreReadDto> storeReadDtos, boolean hasNext) {
src/main/java/com/example/gtable/store/repository/StoreRepository.java (1)

11-17: 소프트 삭제를 지원하는 깔끔한 레포지토리 설계입니다!

@Repository 어노테이션과 JpaRepository 상속을 통한 표준적인 설계가 잘 되어 있습니다. deletedFalse 조건을 포함한 커스텀 쿼리 메서드들이 소프트 삭제 패턴을 적절히 지원하고 있습니다.

일관성을 위해 추가로 고려해 볼 메서드가 있습니다:

+    boolean existsByStoreIdAndDeletedFalse(Long storeId);

이 메서드는 업데이트나 삭제 전 존재 여부 확인에 유용할 수 있습니다.

src/main/java/com/example/gtable/store/dto/StoreCreateRequest.java (1)

29-39: 엔티티 변환 메서드가 잘 구현되어 있습니다!

toEntity() 메서드에서 isActivedeleted 필드를 false로 초기화하는 것이 적절합니다. 새로 생성되는 매장의 기본 상태를 명확히 설정하고 있습니다.

더 명확한 의미 전달을 위해 상수 사용을 고려해 보세요:

+    private static final boolean DEFAULT_ACTIVE_STATUS = false;
+    private static final boolean DEFAULT_DELETED_STATUS = false;
+
     public Store toEntity() {
         return Store.builder()
             .departmentId(departmentId)
             .name(name)
             .location(location)
             .description(description)
             .storeImageUrl(storeImageUrl)
-            .isActive(false)
-            .deleted(false)
+            .isActive(DEFAULT_ACTIVE_STATUS)
+            .deleted(DEFAULT_DELETED_STATUS)
             .build();
     }
build.gradle (1)

32-32: Lombok 의존성 중복이 있는 것 같습니다.

Lombok이 이미 compileOnlyannotationProcessor로 선언되어 있는데, implementation으로도 추가되었습니다. 일반적으로 Lombok은 컴파일 타임에만 필요하므로 implementation 선언이 불필요할 수 있습니다.

-    implementation 'org.projectlombok:lombok'
src/main/java/com/example/gtable/store/dto/StoreUpdateRequest.java (1)

8-18: 유효성 검증 어노테이션 추가를 고려해보세요.

DTO 구조는 잘 설계되었습니다. Lombok 어노테이션들이 적절히 사용되었고, Builder 패턴을 통해 가독성 좋은 객체 생성이 가능합니다. 다만 데이터 무결성을 위해 다음과 같은 유효성 검증을 추가하는 것을 권장합니다:

+import jakarta.validation.constraints.Size;
+import jakarta.validation.constraints.Pattern;

 @Getter
 @AllArgsConstructor
 @NoArgsConstructor
 @Builder
 public class StoreUpdateRequest {
+	@Size(max = 100, message = "매장명은 100자를 초과할 수 없습니다")
 	private String name;
+	@Size(max = 200, message = "위치는 200자를 초과할 수 없습니다")
 	private String location;
+	@Size(max = 500, message = "설명은 500자를 초과할 수 없습니다")
 	private String description;
+	@Pattern(regexp = "^https?://.*", message = "올바른 URL 형식이어야 합니다")
 	private String storeImageUrl;
 	private Boolean isActive;
 	private Boolean deleted;
 }
src/main/java/com/example/gtable/store/service/StoreServiceImpl.java (1)

44-44: 페이징 처리를 위한 hasNext 로직을 구현해주세요.

현재 hasNext가 항상 false로 하드코딩되어 있습니다. 실제 페이징 기능을 구현하거나, 페이징이 필요 없다면 해당 필드를 제거하는 것을 고려해보세요.

-boolean hasNext = false;
+// 페이징이 필요한 경우:
+boolean hasNext = stores.size() >= pageSize;
+
+// 또는 페이징이 불필요한 경우 StoreReadResponse에서 hasNext 필드 제거 고려
src/main/java/com/example/gtable/store/model/Store.java (2)

47-58: 커스텀 생성자와 SuperBuilder의 충돌 가능성을 검토해주세요.

SuperBuilder와 커스텀 생성자가 동시에 존재하면 혼란을 야기할 수 있습니다. SuperBuilder를 사용한다면 커스텀 생성자는 불필요할 수 있습니다.

다음 중 하나의 방식을 선택하는 것을 권장합니다:

방법 1: SuperBuilder만 사용

-	public Store(LocalDateTime createdAt, Long storeId, Long departmentId, String name, String location,
-		String description, String storeImageUrl, Boolean isActive, Boolean deleted) {
-		super(createdAt);
-		this.storeId = storeId;
-		this.departmentId = departmentId;
-		this.name = name;
-		this.location = location;
-		this.description = description;
-		this.storeImageUrl = storeImageUrl;
-		this.isActive = isActive;
-		this.deleted = deleted;
-	}

방법 2: @Builder.Default 사용으로 기본값 처리

+	@Builder.Default
 	@Column(name = "is_active", nullable = false)
 	private Boolean isActive = false;

+	@Builder.Default
 	@Column
 	private Boolean deleted = false;

31-31: departmentId 필드에 대한 연관관계 매핑을 고려해보세요.

현재 departmentId가 단순 Long 타입으로 선언되어 있는데, 실제 Department 엔티티와의 연관관계가 있다면 JPA 연관관계 매핑을 사용하는 것이 더 좋습니다.

-	private Long departmentId;
+	@ManyToOne(fetch = FetchType.LAZY)
+	@JoinColumn(name = "department_id")
+	private Department department;

만약 Department 엔티티가 아직 없다면 현재 구조를 유지하는 것이 적절합니다.

src/main/java/com/example/gtable/store/controller/StoreController.java (2)

31-41: ResponseEntity 타입을 더 구체적으로 지정해보세요.

현재 ResponseEntity<?>를 사용하고 있는데, 더 구체적인 타입을 사용하면 API 문서화와 타입 안전성이 향상됩니다.

-	public ResponseEntity<?> createStore(@Valid @RequestBody StoreCreateRequest request) {
+	public ResponseEntity<ApiUtils.ApiResult<StoreCreateResponse>> createStore(@Valid @RequestBody StoreCreateRequest request) {

이렇게 하면 클라이언트와 개발자 모두에게 더 명확한 API 스펙을 제공할 수 있습니다.


23-26: OpenAPI 문서화 어노테이션 추가를 고려해보세요.

API 문서화를 위해 Swagger/OpenAPI 어노테이션을 추가하는 것을 권장합니다.

+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;

+@Tag(name = "Store", description = "매장 관리 API")
 @RestController
 @RequestMapping("/stores")
 @RequiredArgsConstructor
 public class StoreController {

그리고 각 메서드에도 @operation 어노테이션을 추가해주세요:

+	@Operation(summary = "매장 생성", description = "새로운 매장을 생성합니다")
 	@PostMapping
 	public ResponseEntity<?> createStore(@Valid @RequestBody StoreCreateRequest request) {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e6c5c46 and 37b26e1.

📒 Files selected for processing (19)
  • build.gradle (1 hunks)
  • src/main/java/com/example/gtable/GTableApplication.java (1 hunks)
  • src/main/java/com/example/gtable/TODO/TodoController.java (0 hunks)
  • src/main/java/com/example/gtable/TODO/TodoRepository.java (0 hunks)
  • src/main/java/com/example/gtable/TODO/TodoService.java (0 hunks)
  • src/main/java/com/example/gtable/global/entity/BaseTimeEntity.java (1 hunks)
  • src/main/java/com/example/gtable/store/controller/StoreController.java (1 hunks)
  • src/main/java/com/example/gtable/store/dto/StoreCreateRequest.java (1 hunks)
  • src/main/java/com/example/gtable/store/dto/StoreCreateResponse.java (1 hunks)
  • src/main/java/com/example/gtable/store/dto/StoreReadDto.java (1 hunks)
  • src/main/java/com/example/gtable/store/dto/StoreReadResponse.java (1 hunks)
  • src/main/java/com/example/gtable/store/dto/StoreUpdateRequest.java (1 hunks)
  • src/main/java/com/example/gtable/store/model/Store.java (1 hunks)
  • src/main/java/com/example/gtable/store/repository/StoreRepository.java (1 hunks)
  • src/main/java/com/example/gtable/store/service/StoreService.java (1 hunks)
  • src/main/java/com/example/gtable/store/service/StoreServiceImpl.java (1 hunks)
  • src/test/java/com/example/gtable/store/controller/StoreControllerTest.java (1 hunks)
  • src/test/java/com/example/gtable/store/repository/StoreRepositoryTest.java (1 hunks)
  • src/test/java/com/example/gtable/store/service/StoreServiceTest.java (1 hunks)
💤 Files with no reviewable changes (3)
  • src/main/java/com/example/gtable/TODO/TodoRepository.java
  • src/main/java/com/example/gtable/TODO/TodoService.java
  • src/main/java/com/example/gtable/TODO/TodoController.java
🔇 Additional comments (14)
src/main/java/com/example/gtable/global/entity/BaseTimeEntity.java (3)

21-27: 클래스 정의와 어노테이션이 잘 구성되어 있습니다

JPA 감사 기능을 위한 기본 엔티티 클래스가 깔끔하게 구현되었네요. @MappedSuperclass, @EntityListeners, @SuperBuilder 등의 어노테이션이 적절히 사용되어 있습니다.


29-33: createdAt 필드 설정이 훌륭합니다

@CreatedDate 어노테이션과 updatable = false 설정, 그리고 Jackson 직렬화 설정이 모두 적절하게 구성되어 있어서 좋습니다.


35-37:

Details

❓ Verification inconclusive

생성자와 JPA 감사 기능 간의 충돌 가능성을 확인해주세요

@CreatedDate 어노테이션이 자동으로 생성 시간을 설정하는데, 생성자에서 createdAt을 직접 설정하면 예상치 못한 동작이 발생할 수 있습니다. 실제 사용 시 어떤 값이 우선되는지 테스트해보시고, 필요하다면 생성자를 제거하거나 JPA 감사 기능을 우선하도록 조정해주세요.

실제 동작을 확인하기 위해 다음 스크립트로 테스트 케이스를 찾아보겠습니다:


🏁 Script executed:

#!/bin/bash
# BaseTimeEntity를 사용하는 테스트 케이스 찾기
rg -A 10 -B 5 "BaseTimeEntity|createdAt.*LocalDateTime\.now" --type java

Length of output: 10477


생성자와 JPA 감사 기능 충돌 여부 확인 필요

BaseTimeEntity

  • @CreatedDate가 붙은 createdAt 필드
  • public BaseTimeEntity(LocalDateTime createdAt) 생성자

가 동시에 존재합니다.
@SuperBuilder로 생성한 엔티티에 createdAt을 직접 주입할 때, AuditingEntityListener가 이 값을 덮어쓰는지(또는 null일 때만 설정하는지) 실제 저장 시점을 통해 반드시 검증하세요.

점검 및 대응 방안 예시:

  • 통합 테스트를 추가해 “builder로 주입한 createdAt 값” vs. “JPA 감사 기능이 설정한 값” 중 어떤 값이 DB에 남는지 확인
  • 충돌이 발생한다면
    • 생성자를 제거하고 Auditing에만 위임
    • 또는 AuditingEntityListener가 null일 때만 동작하도록 설정 조정
src/main/java/com/example/gtable/GTableApplication.java (1)

5-7: JPA 감사 기능 활성화가 완벽합니다

@EnableJpaAuditing 어노테이션을 추가하여 BaseTimeEntity의 자동 시간 추적 기능을 활성화하셨네요. 이 변경으로 Store 엔티티들의 생성 시간이 자동으로 관리될 것입니다. 매우 깔끔한 구현입니다!

src/test/java/com/example/gtable/store/repository/StoreRepositoryTest.java (1)

15-20: 테스트 클래스 구조가 깔끔합니다

@DataJpaTest 어노테이션을 사용하여 JPA 계층만 테스트하는 구조가 좋고, 의존성 주입도 적절하게 되어 있습니다.

src/main/java/com/example/gtable/store/dto/StoreReadDto.java (2)

11-23: DTO 클래스 구조가 매우 훌륭합니다

Lombok 어노테이션을 적절히 사용하여 보일러플레이트 코드를 줄이고, 필요한 모든 필드가 잘 정의되어 있습니다. 특히 Boolean 타입 사용으로 null 처리도 고려되어 있어서 좋네요.


25-37: 정적 팩토리 메서드가 완벽하게 구현되었습니다

fromEntity 메서드가 Store 엔티티의 모든 필드를 빠짐없이 DTO로 매핑하고 있어서 훌륭합니다. 이런 방식의 변환 메서드는 유지보수성과 가독성을 크게 향상시키는 우수한 패턴입니다. 매우 깔끔한 구현이에요!

src/main/java/com/example/gtable/store/dto/StoreCreateResponse.java (1)

27-39: 팩토리 메서드 패턴 구현이 잘 되어 있습니다!

fromEntity 정적 팩토리 메서드가 깔끔하게 구현되어 있어 엔티티에서 DTO로의 변환이 명확하고 재사용 가능합니다. 빌더 패턴과의 조합도 좋습니다.

src/main/java/com/example/gtable/store/dto/StoreCreateRequest.java (1)

17-21: 검증 어노테이션이 적절히 적용되어 있습니다!

@NotNull@NotBlank를 통한 필수 필드 검증이 잘 설정되어 있습니다. departmentIdname 필드에 대한 검증이 비즈니스 요구사항을 잘 반영하고 있습니다.

src/test/java/com/example/gtable/store/controller/StoreControllerTest.java (1)

26-84: 테스트 코드 구조가 매우 잘 작성되었습니다!

컨트롤러 테스트의 전반적인 구조가 훌륭합니다. MockMvc를 사용한 단위 테스트 접근법이 적절하고, 성공 케이스(201 Created)와 검증 실패 케이스(400 Bad Request) 모두를 다루고 있어 테스트 커버리지가 좋습니다. 특히 StoreService를 모킹하여 컨트롤러 로직만을 격리해서 테스트하는 방식이 매우 적절합니다.

src/test/java/com/example/gtable/store/service/StoreServiceTest.java (1)

20-71: 서비스 계층 테스트가 매우 꼼꼼하게 작성되었습니다!

MockitoExtension을 활용한 테스트 구조가 깔끔하고, StoreRepository를 적절히 모킹하여 서비스 로직만을 테스트하고 있습니다. 특히 응답 객체의 모든 필드에 대한 검증이 빠짐없이 이루어져 있고, verify()를 통해 리포지토리 호출이 정확히 한 번 발생했는지까지 확인하는 부분이 매우 세심합니다.

build.gradle (1)

33-34: 새로운 의존성들이 잘 추가되었습니다!

spring-boot-starter-validation, springdoc-openapi, awaitility, h2 등의 의존성들이 Store CRUD 기능과 테스트를 위해 적절히 추가되었습니다. 특히 검증 기능과 API 문서화, 그리고 테스트 환경 구성을 위한 의존성들이 잘 선택되었습니다.

Also applies to: 41-42

src/main/java/com/example/gtable/store/service/StoreService.java (1)

9-21: 서비스 인터페이스가 매우 깔끔하게 설계되었습니다!

CRUD 기능을 위한 5개 메소드가 일관성 있게 정의되어 있고, 메소드명이 직관적이어서 각 기능을 명확히 알 수 있습니다. 특히 적절한 DTO 타입을 사용하여 요청과 응답을 분리한 점과, 각 메소드의 반환 타입이 해당 기능에 맞게 잘 설계된 점이 인상적입니다. 단일 책임 원칙을 잘 따르고 있는 깔끔한 인터페이스입니다.

src/main/java/com/example/gtable/store/controller/StoreController.java (1)

30-89: 전체적인 API 설계가 잘 되어 있습니다!

HTTP 메서드와 상태 코드가 RESTful 원칙에 따라 적절히 사용되었고, 일관된 응답 구조를 유지하고 있습니다. 특히 다음 부분들이 좋습니다:

  • POST 요청에 대한 201 Created 상태 코드 사용
  • PATCH를 통한 부분 업데이트 구현
  • 논리적 삭제 패턴 적용
  • @Valid를 통한 요청 데이터 검증
  • ApiUtils.success()를 통한 일관된 응답 래핑

이러한 패턴을 다른 컨트롤러에서도 동일하게 적용하면 API 일관성이 더욱 향상될 것입니다.

@Jjiggu Jjiggu linked an issue Jun 2, 2025 that may be closed by this pull request
2 tasks
@Jjiggu Jjiggu merged commit da7f0da into develop Jun 2, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[백엔드] 주점 CRUD 개발

2 participants