Skip to content

7주차 미션 / 서버 4조 최준서 - #2

Open
evan7484 wants to merge 1 commit into
Konkuk-KUIT:mainfrom
evan7484:junseochoi
Open

evan7484 wants to merge 1 commit into
Konkuk-KUIT:mainfrom
evan7484:junseochoi

Conversation

@evan7484

@evan7484 evan7484 commented May 14, 2026

Copy link
Copy Markdown

구현한 API 목록

Domain Method URI 설명
member GET /members/{memberId} 회원 단건 조회
store GET /stores 가게 목록 조회
store GET /stores/{storeId} 가게 상세 조회
menu GET /stores/{storeId}/menus?page=0&size=10 특정 가게의 메뉴 목록 조회
address POST /members/{memberId}/addresses 회원 배달 주소 등록
cart POST /cart-items 장바구니 메뉴 추가
cart PATCH /cart-items/{cartId} 장바구니 항목 수량 변경
cart DELETE /cart-items/{cartId} 장바구니 항목 삭제

기타 특이사항

ErrorStatus를 정의할 떄 얼마나 자세하게 많이 해야하는지 감이 잘 안 잡혔습니다.

swagger: https://share-subzero-ecologist.ngrok-free.dev/swagger-ui/index.html
image

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • 사용자가 장바구니에서 음식을 추가, 수정, 삭제할 수 있습니다.
    • 매장과 메뉴를 검색하고 페이징으로 조회할 수 있습니다.
    • 배송 주소를 등록하고 관리할 수 있습니다.
    • API 문서가 Swagger를 통해 이용 가능합니다.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown

개요

이 PR은 OpenAPI/Swagger 지원을 추가하고 세 가지 핵심 기능을 구현합니다: 장바구니 CRUD 작업, 회원 배송 주소 관리, 그리고 가게 및 메뉴 조회 기능입니다. 각 기능은 도메인 엔티티, 데이터 전송 객체, 저장소, 서비스 및 REST 컨트롤러로 완벽하게 구성되어 있습니다.

변경 사항

OpenAPI 설정 및 검증 예외 처리

계층 / 파일 요약
OpenAPI 의존성 및 설정
build.gradle, src/main/java/com/kuit/baemin/config/OpenApiConfig.java
Springdoc OpenAPI WebMVC UI 의존성을 추가하고 전역 OpenAPI 메타데이터(API 제목, 설명, 버전)를 정의하는 설정 클래스를 도입합니다.
제약조건 위반 예외 처리
src/main/java/com/kuit/baemin/exception/handler/GlobalExceptionHandler.java
Jakarta 검증 제약조건 위반을 처리하는 전역 예외 핸들러 메서드를 추가하여 모든 위반 메시지를 집계하고 HTTP 400 응답으로 반환합니다.
에러 상태 메시지 현지화
src/main/java/com/kuit/baemin/exception/errorcode/ErrorStatus.java
ErrorStatus 열거형의 메시지 문자열을 유니코드 이스케이프 텍스트로 변환하고 섹션 주석을 영어로 통일합니다.

장바구니 기능

계층 / 파일 요약
장바구니 도메인 모델
src/main/java/com/kuit/baemin/domain/cart/CartItem.java, src/main/java/com/kuit/baemin/domain/cart/CartItemStatus.java
CartItem 엔티티는 멤버, 레스토랑, 메뉴 참조와 수량, 상태를 저장하며, 총 가격 계산, 수량 업데이트, 소프트 삭제 메서드를 제공합니다. CartItemStatus로 활성/삭제 상태를 관리합니다.
장바구니 DTO 및 예외
src/main/java/com/kuit/baemin/dto/request/CartItemCreateReq.java, src/main/java/com/kuit/baemin/dto/request/CartItemUpdateReq.java, src/main/java/com/kuit/baemin/dto/response/CartItemRes.java, src/main/java/com/kuit/baemin/exception/CartException.java
생성/업데이트 요청 DTO는 멤버, 가게, 메뉴, 수량 필드를 검증하고, 응답 DTO는 장바구니 항목 정보를 매핑하며, CartException은 장바구니 관련 오류를 처리합니다.
장바구니 저장소 및 서비스
src/main/java/com/kuit/baemin/repository/CartItemRepository.java, src/main/java/com/kuit/baemin/service/CartService.java
CartItemRepository는 ID와 상태 조합으로 조회하고, CartService는 활성 멤버/레스토랑/메뉴 검증 후 항목을 생성하며 수량 업데이트와 소프트 삭제를 처리합니다.
장바구니 컨트롤러
src/main/java/com/kuit/baemin/controller/CartController.java
/cart-items 경로 아래 POST(생성), PATCH(업데이트), DELETE(삭제) 엔드포인트를 제공하며 요청 검증, Swagger 문서화, ApiResponse 래핑을 포함합니다.
멤버 저장소 상태 필터링
src/main/java/com/kuit/baemin/repository/MemberRepository.java
MemberRepository에 findByIdAndStatus 메서드를 추가하여 장바구니 서비스에서 활성 멤버만 검증할 수 있도록 지원합니다.

주소 관리 기능

계층 / 파일 요약
주소 도메인 모델
src/main/java/com/kuit/baemin/domain/address/Address.java, src/main/java/com/kuit/baemin/domain/address/AddressStatus.java
Address 엔티티는 멤버와의 ManyToOne 관계, 주소 문자열, 이름, 활성/삭제 상태를 포함하며 BaseEntity를 확장합니다.
주소 DTO 및 저장소
src/main/java/com/kuit/baemin/dto/request/AddressCreateReq.java, src/main/java/com/kuit/baemin/dto/response/AddressRes.java, src/main/java/com/kuit/baemin/repository/AddressRepository.java
생성 요청 DTO는 주소와 주소명을 검증하고, 응답 DTO는 주소 정보를 매핑하며, AddressRepository는 CRUD 작업을 지원합니다.
멤버 서비스 주소 통합
src/main/java/com/kuit/baemin/service/MemberService.java
getMember에 읽기 전용 트랜잭션과 활성 상태 필터링을 추가하고, createAddress 메서드를 통해 새 주소를 활성 상태로 생성하고 저장합니다.
멤버 컨트롤러 주소 엔드포인트
src/main/java/com/kuit/baemin/controller/MemberController.java
@validated 어노테이션을 추가하고 getMember에 @Positive 검증을 추가하며, POST /members/{memberId}/addresses 엔드포인트로 주소 생성 기능을 제공합니다.

가게 및 메뉴 조회 기능

계층 / 파일 요약
레스토랑 및 메뉴 도메인 모델
src/main/java/com/kuit/baemin/domain/Restaurant/Restaurant.java, src/main/java/com/kuit/baemin/domain/menu/Menu.java, src/main/java/com/kuit/baemin/domain/menu/MenuStatus.java
Restaurant 엔티티에 카테고리, 배송 가격/시간, 평점, 리뷰 수 등의 필드를 추가하고, Menu 엔티티는 레스토랑과 MenuStatus를 포함하여 메뉴를 관리합니다.
가게 응답 DTO
src/main/java/com/kuit/baemin/dto/response/StoreRes.java, src/main/java/com/kuit/baemin/dto/response/StoreDetailRes.java, src/main/java/com/kuit/baemin/dto/response/StoreListRes.java
StoreRes는 기본 가게 정보, StoreDetailRes는 배송 및 리뷰 정보를 포함한 상세 정보, StoreListRes는 페이지 처리된 목록을 제공하며 모두 Restaurant 엔티티로부터 매핑됩니다.
메뉴 응답 DTO
src/main/java/com/kuit/baemin/dto/response/MenuRes.java, src/main/java/com/kuit/baemin/dto/response/MenuListRes.java
MenuRes는 메뉴 상세 정보, MenuListRes는 페이지 처리된 메뉴 목록을 정의하며 Menu 엔티티로부터 매핑됩니다.
가게 및 메뉴 저장소
src/main/java/com/kuit/baemin/repository/RestaurantRepository.java, src/main/java/com/kuit/baemin/repository/MenuRepository.java
RestaurantRepository는 상태별 페이지 처리 및 상태별 단일 조회를 지원하고, MenuRepository는 레스토랑과 상태 조합으로 페이지 처리 및 단일 조회를 지원합니다.
가게 서비스 및 예외
src/main/java/com/kuit/baemin/service/StoreService.java, src/main/java/com/kuit/baemin/exception/StoreException.java
StoreService는 활성 가게의 페이지 처리된 목록, 상세 정보, 메뉴 목록을 조회하고, StoreException으로 가게 미발견 오류를 처리합니다.
가게 REST 컨트롤러
src/main/java/com/kuit/baemin/controller/StoreController.java
/stores 경로 아래 GET 엔드포인트로 가게 목록, 가게 상세, 가게별 메뉴 조회를 제공하며 페이지 처리, 검증, Swagger 문서화를 포함합니다.

예상 코드 리뷰 노력

🎯 3 (Moderate) | ⏱️ ~25 분

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning PR 제목이 변경 내용의 주요 부분과 무관하며, 저자의 이름과 주차 번호만 포함하고 있습니다. API 엔드포인트 구현이나 실제 변경 사항을 설명하지 않습니다. 제목을 '서버 API 엔드포인트 구현 (회원, 가게, 메뉴, 장바구니, 주소)' 같은 형식으로 변경하여 주요 변경 사항을 명확히 반영하도록 수정해주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 13.16% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/kuit/baemin/domain/cart/CartItem.java`:
- Around line 59-61: The updateQuantity method in CartItem currently assigns
quantity directly, allowing null/zero/negative values to corrupt cart
invariants; update CartItem.updateQuantity(Integer quantity) to validate and
enforce the invariant (e.g., reject null and require quantity > 0) and throw a
clear runtime exception (IllegalArgumentException or custom DomainException)
when validation fails; also ensure any other mutators/constructors in CartItem
that set quantity follow the same check so the invariant is enforced at the
entity level.

In `@src/main/java/com/kuit/baemin/domain/menu/Menu.java`:
- Around line 44-51: The Menu entity uses wrapper types for required fields
which risks NPEs and DB constraint violations; change the fields price and
popularity in class Menu from Integer and Boolean to primitive int and boolean
respectively, update the builder/constructors/setters/getters (e.g., any
Menu.Builder or Menu(...) constructor and
setPrice/setPopularity/getPrice/isPopularity methods) to accept/return
primitives and ensure callers set these required values (or provide sensible
defaults) so null cannot be assigned before persistence.

In `@src/main/java/com/kuit/baemin/domain/Restaurant/Restaurant.java`:
- Around line 29-30: The field name 'type' in the Restaurant class is ambiguous;
rename it to a clear, domain-specific name (e.g., restaurantType, businessType,
or deliveryType) in the Restaurant entity and update its corresponding
accessor/mutator methods (getX/setX), any JPA annotations if you want to
preserve the DB column name (adjust `@Column` if necessary), and all usages across
the codebase (repositories, DTOs, services, tests) to the new identifier to keep
compile-time references consistent; ensure any JSON serialization names or
database migrations are handled after renaming.
- Around line 29-60: The nullable=false fields in Restaurant (fields: type,
minDeliveryPrice, deliveryTip, minDeliveryTime, maxDeliveryTime, rating,
reviewCount, dibsCount) currently use wrapper types (Integer/Double) which risks
nulls via the builder and NPEs; update these fields in the Restaurant entity to
use primitive types (int for Integer fields and double for rating) or
alternatively keep wrappers but remove nullable=false and handle nullability in
business logic—also adjust the builder/factory that constructs Restaurant
(ensure it supplies default primitive values or performs non-null validation) so
the chosen approach is consistent across the class.

In `@src/main/java/com/kuit/baemin/exception/handler/GlobalExceptionHandler.java`:
- Around line 44-53: Add a dedicated ErrorStatus entry for ConstraintViolation
(e.g., CONSTRAINT_VIOLATION or VALIDATION_CONSTRAINT_FAILED) and update the
GlobalExceptionHandler.handleConstraintViolation(ConstraintViolationException)
to return that new ErrorStatus's code instead of ErrorStatus.BAD_REQUEST;
specifically add the enum constant to ErrorStatus and change the
ApiResponse.onFailure(...) call in handleConstraintViolation to use
ErrorStatus.YOUR_NEW_CONST.getCode() (and keep the existing message and payload
behavior). Ensure the new enum follows the project's ErrorStatus
ordering/formatting conventions.

In `@src/main/java/com/kuit/baemin/repository/MenuRepository.java`:
- Line 14: The repository method findAllByRestaurantAndStatus in MenuRepository
causes N+1 when MenuRes.from(menu) accesses menu.getRestaurant().getId() because
restaurant is LAZY; update the repository to eagerly fetch restaurant either by
annotating findAllByRestaurantAndStatus with `@EntityGraph`(attributePaths =
"restaurant") or by replacing it with an explicit `@Query` that uses a fetch join
for restaurant so the Menu entities returned include their Restaurant and avoid
per-row selects.

In `@src/main/java/com/kuit/baemin/service/CartService.java`:
- Around line 37-43: The service currently trusts client-supplied member IDs and
looks up CartItem by ID only, causing ownership bypass; update the controller to
inject the authenticated user (use `@AuthenticationPrincipal` or read
SecurityContext) and change CartService methods createCartItem, updateCartItem,
and deleteCartItem to use the authenticated member's ID instead of
req.getMemberId(); additionally add/replace repository queries with a new
CartItemRepository method like findByIdAndMemberAndStatus(cartId, member,
CartItemStatus.ACTIVE) and use it in updateCartItem/deleteCartItem to verify
ownership before modifying or deleting items.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 977e1f65-e104-4101-bbb3-0cde44c17450

📥 Commits

Reviewing files that changed from the base of the PR and between e0f25b5 and c625789.

📒 Files selected for processing (34)
  • build.gradle
  • src/main/java/com/kuit/baemin/config/OpenApiConfig.java
  • src/main/java/com/kuit/baemin/controller/CartController.java
  • src/main/java/com/kuit/baemin/controller/MemberController.java
  • src/main/java/com/kuit/baemin/controller/StoreController.java
  • src/main/java/com/kuit/baemin/domain/Restaurant/Restaurant.java
  • src/main/java/com/kuit/baemin/domain/address/Address.java
  • src/main/java/com/kuit/baemin/domain/address/AddressStatus.java
  • src/main/java/com/kuit/baemin/domain/cart/CartItem.java
  • src/main/java/com/kuit/baemin/domain/cart/CartItemStatus.java
  • src/main/java/com/kuit/baemin/domain/menu/Menu.java
  • src/main/java/com/kuit/baemin/domain/menu/MenuStatus.java
  • src/main/java/com/kuit/baemin/dto/request/AddressCreateReq.java
  • src/main/java/com/kuit/baemin/dto/request/CartItemCreateReq.java
  • src/main/java/com/kuit/baemin/dto/request/CartItemUpdateReq.java
  • src/main/java/com/kuit/baemin/dto/response/AddressRes.java
  • src/main/java/com/kuit/baemin/dto/response/CartItemRes.java
  • src/main/java/com/kuit/baemin/dto/response/MenuListRes.java
  • src/main/java/com/kuit/baemin/dto/response/MenuRes.java
  • src/main/java/com/kuit/baemin/dto/response/StoreDetailRes.java
  • src/main/java/com/kuit/baemin/dto/response/StoreListRes.java
  • src/main/java/com/kuit/baemin/dto/response/StoreRes.java
  • src/main/java/com/kuit/baemin/exception/CartException.java
  • src/main/java/com/kuit/baemin/exception/StoreException.java
  • src/main/java/com/kuit/baemin/exception/errorcode/ErrorStatus.java
  • src/main/java/com/kuit/baemin/exception/handler/GlobalExceptionHandler.java
  • src/main/java/com/kuit/baemin/repository/AddressRepository.java
  • src/main/java/com/kuit/baemin/repository/CartItemRepository.java
  • src/main/java/com/kuit/baemin/repository/MemberRepository.java
  • src/main/java/com/kuit/baemin/repository/MenuRepository.java
  • src/main/java/com/kuit/baemin/repository/RestaurantRepository.java
  • src/main/java/com/kuit/baemin/service/CartService.java
  • src/main/java/com/kuit/baemin/service/MemberService.java
  • src/main/java/com/kuit/baemin/service/StoreService.java

Comment on lines +59 to +61
public void updateQuantity(Integer quantity) {
this.quantity = quantity;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

수량 도메인 불변식을 엔티티에서 직접 보장해주세요.

Line 59~Line 60은 값을 그대로 대입해서 null, 0, 음수가 들어와도 저장 가능합니다. 요청 DTO 검증이 우회되면 장바구니 데이터 무결성이 깨집니다.

수정 예시
 public void updateQuantity(Integer quantity) {
+    if (quantity == null || quantity <= 0) {
+        throw new IllegalArgumentException("수량은 1 이상이어야 합니다.");
+    }
     this.quantity = quantity;
 }
📝 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 updateQuantity(Integer quantity) {
this.quantity = quantity;
}
public void updateQuantity(Integer quantity) {
if (quantity == null || quantity <= 0) {
throw new IllegalArgumentException("수량은 1 이상이어야 합니다.");
}
this.quantity = quantity;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/kuit/baemin/domain/cart/CartItem.java` around lines 59 -
61, The updateQuantity method in CartItem currently assigns quantity directly,
allowing null/zero/negative values to corrupt cart invariants; update
CartItem.updateQuantity(Integer quantity) to validate and enforce the invariant
(e.g., reject null and require quantity > 0) and throw a clear runtime exception
(IllegalArgumentException or custom DomainException) when validation fails; also
ensure any other mutators/constructors in CartItem that set quantity follow the
same check so the invariant is enforced at the entity level.

Comment on lines +44 to +51
@Column(nullable = false)
private Integer price;

@Column(length = 500)
private String menuPictureUrl;

@Column(nullable = false)
private Boolean popularity;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

nullable=false 제약조건에 래퍼 타입 사용으로 인한 NPE 위험

pricepopularity 필드가 nullable = false로 선언되었지만 래퍼 타입(Integer, Boolean)을 사용하고 있습니다.

이로 인해 발생 가능한 문제:

  • 빌더 패턴 사용 시 필드 미설정으로 인한 null 할당
  • DB 제약조건 위반 또는 런타임 NPE 발생

primitive 타입으로 변경하는 것을 권장합니다.

🔧 primitive 타입 사용 제안
-    `@Column`(nullable = false)
-    private Integer price;
+    `@Column`(nullable = false)
+    private int price;

-    `@Column`(nullable = false)
-    private Boolean popularity;
+    `@Column`(nullable = false)
+    private boolean popularity;
📝 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
@Column(nullable = false)
private Integer price;
@Column(length = 500)
private String menuPictureUrl;
@Column(nullable = false)
private Boolean popularity;
`@Column`(nullable = false)
private int price;
`@Column`(length = 500)
private String menuPictureUrl;
`@Column`(nullable = false)
private boolean popularity;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/kuit/baemin/domain/menu/Menu.java` around lines 44 - 51,
The Menu entity uses wrapper types for required fields which risks NPEs and DB
constraint violations; change the fields price and popularity in class Menu from
Integer and Boolean to primitive int and boolean respectively, update the
builder/constructors/setters/getters (e.g., any Menu.Builder or Menu(...)
constructor and setPrice/setPopularity/getPrice/isPopularity methods) to
accept/return primitives and ensure callers set these required values (or
provide sensible defaults) so null cannot be assigned before persistence.

Comment on lines +29 to +30
@Column(nullable = false)
private Integer type;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

필드명 'type'의 의미가 불명확합니다

type 필드가 무엇을 나타내는지 명확하지 않습니다. 레스토랑 분류인지, 배달 타입인지 등을 알 수 없습니다.

필드명을 더 명확하게 변경하는 것을 권장합니다 (예: restaurantType, businessType, deliveryType 등).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/kuit/baemin/domain/Restaurant/Restaurant.java` around lines
29 - 30, The field name 'type' in the Restaurant class is ambiguous; rename it
to a clear, domain-specific name (e.g., restaurantType, businessType, or
deliveryType) in the Restaurant entity and update its corresponding
accessor/mutator methods (getX/setX), any JPA annotations if you want to
preserve the DB column name (adjust `@Column` if necessary), and all usages across
the codebase (repositories, DTOs, services, tests) to the new identifier to keep
compile-time references consistent; ensure any JSON serialization names or
database migrations are handled after renaming.

Comment on lines +29 to +60
@Column(nullable = false)
private Integer type;

@Column(length = 500)
private String storePictureUrl;

@Column(nullable = false, length = 20)
private String phone;

@Column(nullable = false, length = 500)
private String content;

@Column(nullable = false)
private Integer minDeliveryPrice;

@Column(nullable = false)
private Integer deliveryTip;

@Column(nullable = false)
private Integer minDeliveryTime;

@Column(nullable = false)
private Integer maxDeliveryTime;

@Column(nullable = false)
private Double rating;

@Column(nullable = false)
private Integer reviewCount;

@Column(nullable = false)
private Integer dibsCount;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

nullable=false 제약조건에 래퍼 타입 사용으로 인한 NPE 위험

type, minDeliveryPrice, deliveryTip, minDeliveryTime, maxDeliveryTime, rating, reviewCount, dibsCount 필드들이 nullable = false로 선언되어 있지만 래퍼 타입(Integer, Double)을 사용하고 있습니다.

이는 다음 문제를 야기할 수 있습니다:

  • 빌더 사용 시 필드를 설정하지 않으면 null이 할당되어 DB 삽입 시 제약조건 위반
  • 엔티티 사용 중 예기치 않은 NPE 발생 가능

primitive 타입(int, double)으로 변경하거나, @Column 제약과 실제 타입 간 일관성을 유지하세요.

🔧 primitive 타입 사용 제안
-    `@Column`(nullable = false)
-    private Integer type;
+    `@Column`(nullable = false)
+    private int type;

-    `@Column`(nullable = false)
-    private Integer minDeliveryPrice;
+    `@Column`(nullable = false)
+    private int minDeliveryPrice;

-    `@Column`(nullable = false)
-    private Integer deliveryTip;
+    `@Column`(nullable = false)
+    private int deliveryTip;

-    `@Column`(nullable = false)
-    private Integer minDeliveryTime;
+    `@Column`(nullable = false)
+    private int minDeliveryTime;

-    `@Column`(nullable = false)
-    private Integer maxDeliveryTime;
+    `@Column`(nullable = false)
+    private int maxDeliveryTime;

-    `@Column`(nullable = false)
-    private Double rating;
+    `@Column`(nullable = false)
+    private double rating;

-    `@Column`(nullable = false)
-    private Integer reviewCount;
+    `@Column`(nullable = false)
+    private int reviewCount;

-    `@Column`(nullable = false)
-    private Integer dibsCount;
+    `@Column`(nullable = false)
+    private int dibsCount;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/kuit/baemin/domain/Restaurant/Restaurant.java` around lines
29 - 60, The nullable=false fields in Restaurant (fields: type,
minDeliveryPrice, deliveryTip, minDeliveryTime, maxDeliveryTime, rating,
reviewCount, dibsCount) currently use wrapper types (Integer/Double) which risks
nulls via the builder and NPEs; update these fields in the Restaurant entity to
use primitive types (int for Integer fields and double for rating) or
alternatively keep wrappers but remove nullable=false and handle nullability in
business logic—also adjust the builder/factory that constructs Restaurant
(ensure it supplies default primitive values or performs non-null validation) so
the chosen approach is consistent across the class.

Comment on lines +44 to +53
@ResponseStatus(BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public ApiResponse<?> handleConstraintViolation(ConstraintViolationException e) {
String message = e.getConstraintViolations()
.stream()
.map(violation -> violation.getMessage())
.collect(Collectors.joining(", "));
log.warn("[Validation error] {}", message);
return ApiResponse.onFailure(ErrorStatus.BAD_REQUEST.getCode(), message, null);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

ConstraintViolationException에 전용 ErrorStatus 코드를 매핑해주세요.

현재 BAD_REQUEST 공통 코드를 재사용해서 @Valid 실패와 @Validated 실패를 코드 기준으로 구분할 수 없습니다. 전용 에러 코드를 ErrorStatus에 추가하고 이 핸들러에서 그 코드를 반환하는 방식으로 분리하는 게 좋습니다.

🔧 제안 패치
-        return ApiResponse.onFailure(ErrorStatus.BAD_REQUEST.getCode(), message, null);
+        return ApiResponse.onFailure(ErrorStatus.CONSTRAINT_VIOLATION.getCode(), message, null);
+    CONSTRAINT_VIOLATION(HttpStatus.BAD_REQUEST, "COMMON4001", "요청 파라미터 검증에 실패했습니다."),

As per coding guidelines src/main/java/**/exception/**: 새 예외 코드를 ErrorStatus enum에 추가했는지 확인.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/kuit/baemin/exception/handler/GlobalExceptionHandler.java`
around lines 44 - 53, Add a dedicated ErrorStatus entry for ConstraintViolation
(e.g., CONSTRAINT_VIOLATION or VALIDATION_CONSTRAINT_FAILED) and update the
GlobalExceptionHandler.handleConstraintViolation(ConstraintViolationException)
to return that new ErrorStatus's code instead of ErrorStatus.BAD_REQUEST;
specifically add the enum constant to ErrorStatus and change the
ApiResponse.onFailure(...) call in handleConstraintViolation to use
ErrorStatus.YOUR_NEW_CONST.getCode() (and keep the existing message and payload
behavior). Ensure the new enum follows the project's ErrorStatus
ordering/formatting conventions.


public interface MenuRepository extends JpaRepository<Menu, Long> {

Page<Menu> findAllByRestaurantAndStatus(Restaurant restaurant, MenuStatus status, Pageable pageable);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Menu 엔티티의 Restaurant 관계 매핑 확인
rg -n -A 5 -B 2 '@ManyToOne|@OneToOne' --glob 'Menu.java'

# MenuRes DTO에서 Restaurant 필드 접근 여부 확인
rg -n -A 10 'class MenuRes|from.*Menu' --glob 'MenuRes.java'

Repository: Konkuk-KUIT/KUIT7_REST-API

Length of output: 2633


🏁 Script executed:

cat -n src/main/java/com/kuit/baemin/repository/MenuRepository.java

Repository: Konkuk-KUIT/KUIT7_REST-API

Length of output: 832


@EntityGraph를 통한 N+1 문제 해결 필요

findAllByRestaurantAndStatus는 현재 최적화 전략이 없어 N+1 문제가 발생합니다. MenuRes.from(menu) 메서드가 menu.getRestaurant().getId()를 호출할 때, restaurant 필드가 LAZY-loaded이므로 페이지의 각 Menu 엔티티마다 추가 쿼리가 발생합니다.

다음과 같이 수정하세요:

`@EntityGraph`(attributePaths = "restaurant")
Page<Menu> findAllByRestaurantAndStatus(Restaurant restaurant, MenuStatus status, Pageable pageable);

또는 @Query와 fetch join을 사용하면 더 명시적입니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/kuit/baemin/repository/MenuRepository.java` at line 14, The
repository method findAllByRestaurantAndStatus in MenuRepository causes N+1 when
MenuRes.from(menu) accesses menu.getRestaurant().getId() because restaurant is
LAZY; update the repository to eagerly fetch restaurant either by annotating
findAllByRestaurantAndStatus with `@EntityGraph`(attributePaths = "restaurant") or
by replacing it with an explicit `@Query` that uses a fetch join for restaurant so
the Menu entities returned include their Restaurant and avoid per-row selects.

Comment on lines +37 to +43
public CartItemRes createCartItem(CartItemCreateReq req) {
Member member = memberRepository.findByIdAndStatus(req.getMemberId(), MemberStatus.ACTIVE)
.orElseThrow(() -> new MemberException(ErrorStatus.MEMBER_NOT_FOUND));
Restaurant restaurant = restaurantRepository.findByIdAndStatus(req.getStoreId(), RestaurantStatus.ACTIVE)
.orElseThrow(() -> new StoreException(ErrorStatus.STORE_NOT_FOUND));
Menu menu = menuRepository.findByIdAndRestaurantAndStatus(req.getMenuId(), restaurant, MenuStatus.ACTIVE)
.orElseThrow(() -> new CartException(ErrorStatus.MENU_NOT_IN_STORE));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== CartController 엔드포인트 시그니처 확인 =="
fd -i 'CartController.java$' src/main/java | while read -r f; do
  echo "---- $f ----"
  sed -n '1,260p' "$f"
done

echo
echo "== 인증 주체 사용 흔적 확인 (`@AuthenticationPrincipal` / Principal / SecurityContext) =="
rg -n -C2 --type=java '@AuthenticationPrincipal|Principal|SecurityContextHolder|getAuthentication\(' src/main/java

echo
echo "== CartService 호출부 및 메서드 시그니처 확인 =="
rg -n -C3 --type=java 'createCartItem\s*\(|updateCartItem\s*\(|deleteCartItem\s*\(' src/main/java

echo
echo "== Security 설정 파일 확인 =="
fd -i 'Security*.java$' src/main/java | while read -r f; do
  echo "---- $f ----"
  sed -n '1,260p' "$f"
done

Repository: Konkuk-KUIT/KUIT7_REST-API

Length of output: 2241


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== CartService 전체 구현 확인 =="
fd -i 'CartService.java$' src/main/java | xargs cat -n

echo
echo "== 컨트롤러의 권한 검증 어노테이션 확인 =="
rg -n --type=java '@PreAuthorize|@Secured|@RolesAllowed' src/main/java/com/kuit/baemin/controller/CartController.java || echo "No `@PreAuthorize/`@Secured found"

echo
echo "== Security/WebSecurityConfig 파일 확인 =="
fd -i 'Security.*Config.java$' src/main/java | xargs cat -n

echo
echo "== 인증 관련 설정 확인 (모든 Security 관련 파일) =="
find src/main/java -name '*Security*.java' -o -name '*Config.java' | head -10 | xargs ls -la 2>/dev/null || echo "No security files"

Repository: Konkuk-KUIT/KUIT7_REST-API

Length of output: 3985


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== WebSecurityConfig 파일 전체 저장소 검색 =="
find . -name '*SecurityConfig*.java' -o -name '*WebConfig*.java' 2>/dev/null | head -20

echo
echo "== application.yml/properties에서 security 설정 확인 =="
find . -name 'application*.yml' -o -name 'application*.properties' 2>/dev/null | xargs cat -n || echo "No application config found"

echo
echo "== CartItem 엔티티 확인 (memberId 필드 있는지, CartItemRepository 쿼리 확인) =="
fd -i 'CartItem.java$' src/main/java | xargs cat -n

echo
echo "== CartItemRepository 인터페이스 확인 =="
fd -i 'CartItemRepository.java$' src/main/java | xargs cat -n

echo
echo "== SecurityContextHolder 사용 여부 (서비스 내부에서 인증 객체 접근) =="
rg -n 'SecurityContextHolder|Authentication' src/main/java/com/kuit/baemin/service/ || echo "No SecurityContextHolder usage"

Repository: Konkuk-KUIT/KUIT7_REST-API

Length of output: 3823


장바구니 항목 수정/삭제 시 소유자 검증이 없어 권한 우회가 발생합니다.

updateCartItem()deleteCartItem()cartId만으로 조회하고 member 소유권을 검증하지 않습니다. Line 58, 67의 findByIdAndStatus(cartId, CartItemStatus.ACTIVE) 쿼리가 member 조건을 포함하지 않으므로, 인증된 모든 사용자가 다른 사용자의 장바구니 항목을 수정하거나 삭제할 수 있습니다.

또한 createCartItem()은 Line 38에서 클라이언트 요청의 req.getMemberId()를 직접 사용하고 있어, 인증 주체와의 일치 여부를 확인하지 않습니다. 컨트롤러에 @AuthenticationPrincipal 주입도 없습니다.

필수 수정:

  1. CartController에서 인증 주체를 @AuthenticationPrincipal 또는 SecurityContext를 통해 주입
  2. CartService 메서드들이 클라이언트 전달값 대신 인증 주체의 ID를 사용하도록 변경
  3. CartItemRepository의 쿼리를 findByIdAndMemberAndStatus() 형태로 확장하여 멤버 소유권 검증 포함
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/kuit/baemin/service/CartService.java` around lines 37 - 43,
The service currently trusts client-supplied member IDs and looks up CartItem by
ID only, causing ownership bypass; update the controller to inject the
authenticated user (use `@AuthenticationPrincipal` or read SecurityContext) and
change CartService methods createCartItem, updateCartItem, and deleteCartItem to
use the authenticated member's ID instead of req.getMemberId(); additionally
add/replace repository queries with a new CartItemRepository method like
findByIdAndMemberAndStatus(cartId, member, CartItemStatus.ACTIVE) and use it in
updateCartItem/deleteCartItem to verify ownership before modifying or deleting
items.

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