Conversation
개요이 PR은 Spring Boot 기반의 음식 배달 REST API를 구현합니다. Swagger 설정부터 시작해 음식점, 주소, 메뉴, 주문, 리뷰 도메인에 대한 완전한 CRUD 기능을 추가합니다. 각 도메인마다 엔티티, 레포지토리, 요청/응답 DTO, 서비스, 컨트롤러가 계층적으로 구현되어 있습니다. 변경 사항음식 배달 REST API 핵심 구현
🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 11
🤖 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/order/Orders.java`:
- Around line 41-43: Orders 엔티티의 status 필드가 잘못된 enum 타입(OrderItemStatus)으로 선언되어
중복 또는 부적절하게 보입니다; Orders 클래스의 기존 orderStatus 필드와 ERD를 확인해 어느 상태 필드만 남길지 결정한 뒤
수정하세요: 만약 Orders에 하나의 상태만 필요하면 중복된 status 필드를 제거; 만약 두 필드가 모두 필요하지만 의미가 다르면
status의 타입을 OrderStatus로 변경하거나 필드명을 명확히(예: orderItemStatus → orderItemStatus) 하여
역할을 구분하고 JPA 어노테이션(`@Enumerated`, `@Column`)은 유지하세요 (참조 심볼: Orders, status,
orderStatus, OrderItemStatus, OrderStatus).
In `@src/main/java/com/kuit/baemin/domain/review/Review.java`:
- Around line 37-38: The rating field in the Review entity lacks validation for
allowed values; update the Review class so the Integer rating field is annotated
with Jakarta Validation constraints (e.g., `@Min`(1) and `@Max`(5)) to enforce the
1–5 range, and add the corresponding imports (jakarta.validation.constraints.Min
and jakarta.validation.constraints.Max); keep the existing `@Column`(nullable =
false) intact so the DB constraint remains while validation prevents
out-of-range values at the model level.
In `@src/main/java/com/kuit/baemin/dto/request/AddressCreateReq.java`:
- Around line 13-14: Replace the `@NotNull` on the String field addressType in
AddressCreateReq with `@NotBlank` to reject empty or whitespace-only strings;
update the import to use javax.validation.constraints.NotBlank (or jakarta
equivalent used in the project) and run/adjust any validation tests to ensure
the new constraint is applied on AddressCreateReq.addressType.
In `@src/main/java/com/kuit/baemin/dto/response/AddressRes.java`:
- Line 21: address.getMember().getId() can throw NPE if member is null; update
the AddressRes mapping (where .memberId(address.getMember().getId()) is invoked)
to defensively handle a null member: check address.getMember() for null and
supply a safe value (e.g., null or 0) or use
Optional.ofNullable(address.getMember()).map(Member::getId).orElse(null) so
memberId is not dereferenced when member is absent; adjust the AddressRes
builder/constructor to accept a nullable memberId accordingly.
In `@src/main/java/com/kuit/baemin/dto/response/OrderRes.java`:
- Around line 22-31: In OrderRes.of, avoid chaining
order.getRestaurant().getName() which can NPE if getRestaurant() is null; modify
OrderRes.of to safely obtain the restaurant name (e.g., check if
order.getRestaurant() != null and use a safe fallback like null or empty string)
and set that value on the builder (reference: OrderRes.of,
Orders.getRestaurant(), Orders.getRestaurant().getName(), OrderRes.builder());
alternatively ensure the service layer always loads the restaurant before
calling OrderRes.of.
In `@src/main/java/com/kuit/baemin/dto/response/ReviewRes.java`:
- Around line 20-29: The ReviewRes.from factory uses chained calls
review.getOrder().getId() and review.getRestaurant().getName() which can throw
NPE if associated entities are null; update ReviewRes.from to defensively handle
nulls by checking review.getOrder() and review.getRestaurant() (or their
id/name) before accessing members and provide safe fallbacks (e.g., null or
optional values) so that ReviewRes.builder() receives safe values; reference the
ReviewRes.from method and Review#getOrder, Review#getRestaurant,
Review#getRating, Review#getContent to locate and update the code.
In `@src/main/java/com/kuit/baemin/repository/OrderRepository.java`:
- Line 11: 메서드 findByMemberIdAndStatus에서 잘못된 enum 타입을 사용하고 있으니 파라미터 타입을
OrderItemStatus에서 OrderStatus로 변경하고 관련 import 문도 OrderStatus로 교체하세요; 대상은
OrderRepository의 Page<Orders> findByMemberIdAndStatus(Long memberId, OrderStatus
status, Pageable pageable) 시그니처로 수정하는 것입니다.
In `@src/main/java/com/kuit/baemin/repository/ReviewRepository.java`:
- Line 13: The current ReviewRepository method findByMemberIdAndStatus(Long
memberId, ReviewStatus status) should support pagination to avoid loading all
reviews at once; change its signature to return Page<Review> and add a Pageable
parameter (e.g., Page<Review> findByMemberIdAndStatus(Long memberId,
ReviewStatus status, Pageable pageable)), and add the necessary imports for
org.springframework.data.domain.Page and
org.springframework.data.domain.Pageable so callers can pass page/size/sort.
In `@src/main/java/com/kuit/baemin/service/OrderService.java`:
- Around line 111-154: toOrderRes and calculateTotalPrice are causing duplicate
option queries; fetch OrderItemOptions once and reuse them by either (A)
changing the repository call in toOrderRes to a single eager-load method (e.g.,
replace orderItemRepository.findByOrderIdAndStatus(...) with a new
findByOrderIdAndStatusWithOptions(...) that JOIN FETCHes orderItemOptions) and
then use the loaded options both to build OrderItemRes (OrderItemRes.of) and to
compute price, or (B) compute each item's subtotal while mapping in toOrderRes
(use the options loaded there) and remove the separate option lookups from
calculateTotalPrice (or change calculateTotalPrice to accept the already-loaded
OrderItem list with options); update references to OrderItemOptionRepository
lookups (in toOrderRes and calculateTotalPrice) accordingly so options are
queried only once per order.
In `@src/main/resources/application.yml`:
- Line 10: The default configuration currently sets
spring.jpa.hibernate.ddl-auto: update in application.yml which risks unintended
schema changes in non-local environments; change configuration to remove/update
this default and move environment-specific settings into profile-specific files
(e.g., create application-local.yml with spring.jpa.hibernate.ddl-auto: update
and ensure production/other profiles use spring.jpa.hibernate.ddl-auto: validate
or none) and ensure the active profile selection is used at runtime so the
application does not apply `update` unless the "local" profile is explicitly
active.
- Around line 3-5: Remove the hardcoded DB credentials in application.yml (the
url, username, and password properties) and replace them with environment-backed
placeholders (e.g., use environment variables for DB_URL, DB_USERNAME,
DB_PASSWORD) so no plaintext secrets remain in the repo; update any code/config
that reads these properties to use the new env vars and ensure secrets are
loaded via your platform's secret manager or environment, and immediately rotate
the exposed password.
🪄 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: 992e5da6-ccc0-463c-9783-a782d6354f74
📒 Files selected for processing (51)
build.gradlesrc/main/java/com/kuit/baemin/config/SwaggerConfig.javasrc/main/java/com/kuit/baemin/controller/AddressController.javasrc/main/java/com/kuit/baemin/controller/MenuController.javasrc/main/java/com/kuit/baemin/controller/OrderController.javasrc/main/java/com/kuit/baemin/controller/RestaurantController.javasrc/main/java/com/kuit/baemin/controller/ReviewController.javasrc/main/java/com/kuit/baemin/domain/Restaurant/Restaurant.javasrc/main/java/com/kuit/baemin/domain/address/Address.javasrc/main/java/com/kuit/baemin/domain/address/AddressStatus.javasrc/main/java/com/kuit/baemin/domain/menu/Menu.javasrc/main/java/com/kuit/baemin/domain/menu/MenuOption.javasrc/main/java/com/kuit/baemin/domain/menu/MenuOptionStatus.javasrc/main/java/com/kuit/baemin/domain/menu/MenuStatus.javasrc/main/java/com/kuit/baemin/domain/menu/SelectionType.javasrc/main/java/com/kuit/baemin/domain/order/OrderItem.javasrc/main/java/com/kuit/baemin/domain/order/OrderItemOption.javasrc/main/java/com/kuit/baemin/domain/order/OrderItemOptionStatus.javasrc/main/java/com/kuit/baemin/domain/order/OrderItemStatus.javasrc/main/java/com/kuit/baemin/domain/order/OrderStatus.javasrc/main/java/com/kuit/baemin/domain/order/Orders.javasrc/main/java/com/kuit/baemin/domain/review/Review.javasrc/main/java/com/kuit/baemin/domain/review/ReviewStatus.javasrc/main/java/com/kuit/baemin/dto/request/AddressCreateReq.javasrc/main/java/com/kuit/baemin/dto/request/OrderCreateReq.javasrc/main/java/com/kuit/baemin/dto/request/OrderItemCreateReq.javasrc/main/java/com/kuit/baemin/dto/request/ReviewCreateReq.javasrc/main/java/com/kuit/baemin/dto/response/AddressRes.javasrc/main/java/com/kuit/baemin/dto/response/MenuDetailRes.javasrc/main/java/com/kuit/baemin/dto/response/MenuOptionRes.javasrc/main/java/com/kuit/baemin/dto/response/MenuRes.javasrc/main/java/com/kuit/baemin/dto/response/OrderItemOptionRes.javasrc/main/java/com/kuit/baemin/dto/response/OrderItemRes.javasrc/main/java/com/kuit/baemin/dto/response/OrderRes.javasrc/main/java/com/kuit/baemin/dto/response/RestaurantRes.javasrc/main/java/com/kuit/baemin/dto/response/ReviewRes.javasrc/main/java/com/kuit/baemin/exception/errorcode/ErrorStatus.javasrc/main/java/com/kuit/baemin/repository/AddressRepository.javasrc/main/java/com/kuit/baemin/repository/MenuOptionRepository.javasrc/main/java/com/kuit/baemin/repository/MenuRepository.javasrc/main/java/com/kuit/baemin/repository/OrderItemOptionRepository.javasrc/main/java/com/kuit/baemin/repository/OrderItemRepository.javasrc/main/java/com/kuit/baemin/repository/OrderRepository.javasrc/main/java/com/kuit/baemin/repository/RestaurantRepository.javasrc/main/java/com/kuit/baemin/repository/ReviewRepository.javasrc/main/java/com/kuit/baemin/service/AddressService.javasrc/main/java/com/kuit/baemin/service/MenuService.javasrc/main/java/com/kuit/baemin/service/OrderService.javasrc/main/java/com/kuit/baemin/service/RestaurantService.javasrc/main/java/com/kuit/baemin/service/ReviewService.javasrc/main/resources/application.yml
| @Enumerated(EnumType.STRING) | ||
| @Column(nullable = false, length = 10) | ||
| private OrderItemStatus status; |
There was a problem hiding this comment.
잘못된 enum 타입 사용.
status 필드가 OrderItemStatus 타입으로 선언되어 있는데, 이는 Orders 엔티티가 아닌 OrderItem에 적합한 상태 타입입니다. Orders 엔티티에는 이미 line 39에서 OrderStatus 타입의 orderStatus 필드가 선언되어 있습니다.
두 필드가 중복되거나, status 필드의 타입이 잘못 지정된 것으로 보입니다. ERD를 확인하여 Orders 테이블에 필요한 상태 필드가 무엇인지 검증해주세요.
🐛 제안하는 수정 방안
만약 하나의 상태 필드만 필요하다면:
`@Enumerated`(EnumType.STRING)
`@Column`(nullable = false, length = 20)
private OrderStatus orderStatus;
-@Enumerated(EnumType.STRING)
-@Column(nullable = false, length = 10)
-private OrderItemStatus status;또는 필드명을 명확히 구분해야 한다면 타입을 수정:
`@Enumerated`(EnumType.STRING)
-@Column(nullable = false, length = 10)
-private OrderItemStatus status;
+@Column(nullable = false, length = 20)
+private OrderStatus status;📝 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.
| @Enumerated(EnumType.STRING) | |
| @Column(nullable = false, length = 10) | |
| private OrderItemStatus status; | |
| `@Enumerated`(EnumType.STRING) | |
| `@Column`(nullable = false, length = 20) | |
| private OrderStatus orderStatus; |
| @Enumerated(EnumType.STRING) | |
| @Column(nullable = false, length = 10) | |
| private OrderItemStatus status; | |
| `@Enumerated`(EnumType.STRING) | |
| `@Column`(nullable = false, length = 20) | |
| private OrderStatus status; |
🤖 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/order/Orders.java` around lines 41 - 43,
Orders 엔티티의 status 필드가 잘못된 enum 타입(OrderItemStatus)으로 선언되어 중복 또는 부적절하게 보입니다;
Orders 클래스의 기존 orderStatus 필드와 ERD를 확인해 어느 상태 필드만 남길지 결정한 뒤 수정하세요: 만약 Orders에
하나의 상태만 필요하면 중복된 status 필드를 제거; 만약 두 필드가 모두 필요하지만 의미가 다르면 status의 타입을
OrderStatus로 변경하거나 필드명을 명확히(예: orderItemStatus → orderItemStatus) 하여 역할을 구분하고
JPA 어노테이션(`@Enumerated`, `@Column`)은 유지하세요 (참조 심볼: Orders, status, orderStatus,
OrderItemStatus, OrderStatus).
| @Column(nullable = false) | ||
| private Integer rating; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
평점 범위 검증 추가 권장.
rating 필드에 범위 제약이 없습니다. 일반적으로 평점은 1-5점 또는 1-10점 범위로 제한되는데, 현재는 음수나 과도하게 큰 값도 허용됩니다.
Jakarta Validation의 @Min, @Max 어노테이션을 사용하여 유효한 범위를 명시하는 것을 권장합니다.
✨ 제안하는 개선 방안 (1~5점 기준)
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
`@Column`(nullable = false)
+@Min(1)
+@Max(5)
private Integer rating;📝 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.
| @Column(nullable = false) | |
| private Integer rating; | |
| import jakarta.validation.constraints.Max; | |
| import jakarta.validation.constraints.Min; | |
| `@Column`(nullable = false) | |
| `@Min`(1) | |
| `@Max`(5) | |
| private Integer rating; |
🤖 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/review/Review.java` around lines 37 -
38, The rating field in the Review entity lacks validation for allowed values;
update the Review class so the Integer rating field is annotated with Jakarta
Validation constraints (e.g., `@Min`(1) and `@Max`(5)) to enforce the 1–5 range, and
add the corresponding imports (jakarta.validation.constraints.Min and
jakarta.validation.constraints.Max); keep the existing `@Column`(nullable = false)
intact so the DB constraint remains while validation prevents out-of-range
values at the model level.
| @NotNull(message = "주소 타입은 필수입니다.") | ||
| private String addressType; |
There was a problem hiding this comment.
String 필드에는 @NotBlank 사용을 권장합니다.
addressType은 String 타입이므로 @NotNull 대신 @NotBlank를 사용하는 것이 좋습니다. @NotNull은 빈 문자열("")과 공백만 있는 문자열을 허용하지만, @NotBlank는 이를 모두 거부합니다.
✅ 수정 제안
- `@NotNull`(message = "주소 타입은 필수입니다.")
+ `@NotBlank`(message = "주소 타입은 필수입니다.")
private String addressType;📝 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.
| @NotNull(message = "주소 타입은 필수입니다.") | |
| private String addressType; | |
| `@NotBlank`(message = "주소 타입은 필수입니다.") | |
| private String addressType; |
🤖 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/dto/request/AddressCreateReq.java` around lines
13 - 14, Replace the `@NotNull` on the String field addressType in
AddressCreateReq with `@NotBlank` to reject empty or whitespace-only strings;
update the import to use javax.validation.constraints.NotBlank (or jakarta
equivalent used in the project) and run/adjust any validation tests to ensure
the new constraint is applied on AddressCreateReq.addressType.
| public static AddressRes from(Address address) { | ||
| return AddressRes.builder() | ||
| .id(address.getId()) | ||
| .memberId(address.getMember().getId()) |
There was a problem hiding this comment.
Null 포인터 예외(NPE) 가능성이 있습니다.
address.getMember().getId() 호출 시 getMember()가 null을 반환하면 NPE가 발생합니다. JPA 연관관계에서 Member가 지연 로딩되거나 null일 수 있습니다.
🛡️ 수정 제안
방법 1: Null 체크 추가
public static AddressRes from(Address address) {
+ if (address.getMember() == null) {
+ throw new IllegalStateException("Address must have an associated member");
+ }
return AddressRes.builder()
.id(address.getId())
.memberId(address.getMember().getId())방법 2: Optional 사용
- private Long memberId;
+ private Long memberId; // null 허용으로 변경하거나 public static AddressRes from(Address address) {
return AddressRes.builder()
.id(address.getId())
- .memberId(address.getMember().getId())
+ .memberId(address.getMember() != null ? address.getMember().getId() : null)Address 엔티티의 member 필드가 @ManyToOne(optional=false)로 정의되어 있고 데이터 무결성이 보장된다면, 방법 1을 권장합니다.
🤖 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/dto/response/AddressRes.java` at line 21,
address.getMember().getId() can throw NPE if member is null; update the
AddressRes mapping (where .memberId(address.getMember().getId()) is invoked) to
defensively handle a null member: check address.getMember() for null and supply
a safe value (e.g., null or 0) or use
Optional.ofNullable(address.getMember()).map(Member::getId).orElse(null) so
memberId is not dereferenced when member is absent; adjust the AddressRes
builder/constructor to accept a nullable memberId accordingly.
| public static OrderRes of(Orders order, Integer totalPrice, List<OrderItemRes> items) { | ||
| return OrderRes.builder() | ||
| .id(order.getId()) | ||
| .restaurantName(order.getRestaurant().getName()) | ||
| .orderStatus(order.getOrderStatus()) | ||
| .totalPrice(totalPrice) | ||
| .createdAt(order.getCreatedAt()) | ||
| .items(items) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
연관 객체 탐색 시 NPE 위험
Line 25에서 order.getRestaurant().getName()와 같이 체이닝된 호출을 사용하고 있습니다. 만약 getRestaurant()가 null을 반환하면 NullPointerException이 발생할 수 있습니다. 연관 엔티티가 항상 로드된다는 보장이 없다면 null 체크를 추가하는 것이 안전합니다.
🛡️ null 안전성을 위한 수정 제안
public static OrderRes of(Orders order, Integer totalPrice, List<OrderItemRes> items) {
+ String restaurantName = order.getRestaurant() != null
+ ? order.getRestaurant().getName()
+ : null;
+
return OrderRes.builder()
.id(order.getId())
- .restaurantName(order.getRestaurant().getName())
+ .restaurantName(restaurantName)
.orderStatus(order.getOrderStatus())
.totalPrice(totalPrice)
.createdAt(order.getCreatedAt())
.items(items)
.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.
| public static OrderRes of(Orders order, Integer totalPrice, List<OrderItemRes> items) { | |
| return OrderRes.builder() | |
| .id(order.getId()) | |
| .restaurantName(order.getRestaurant().getName()) | |
| .orderStatus(order.getOrderStatus()) | |
| .totalPrice(totalPrice) | |
| .createdAt(order.getCreatedAt()) | |
| .items(items) | |
| .build(); | |
| } | |
| public static OrderRes of(Orders order, Integer totalPrice, List<OrderItemRes> items) { | |
| String restaurantName = order.getRestaurant() != null | |
| ? order.getRestaurant().getName() | |
| : null; | |
| return OrderRes.builder() | |
| .id(order.getId()) | |
| .restaurantName(restaurantName) | |
| .orderStatus(order.getOrderStatus()) | |
| .totalPrice(totalPrice) | |
| .createdAt(order.getCreatedAt()) | |
| .items(items) | |
| .build(); | |
| } |
🤖 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/dto/response/OrderRes.java` around lines 22 -
31, In OrderRes.of, avoid chaining order.getRestaurant().getName() which can NPE
if getRestaurant() is null; modify OrderRes.of to safely obtain the restaurant
name (e.g., check if order.getRestaurant() != null and use a safe fallback like
null or empty string) and set that value on the builder (reference: OrderRes.of,
Orders.getRestaurant(), Orders.getRestaurant().getName(), OrderRes.builder());
alternatively ensure the service layer always loads the restaurant before
calling OrderRes.of.
|
|
||
| public interface OrderRepository extends JpaRepository<Orders, Long> { | ||
|
|
||
| Page<Orders> findByMemberIdAndStatus(Long memberId, OrderItemStatus status, Pageable pageable); |
There was a problem hiding this comment.
잘못된 상태 타입이 사용되었습니다.
Orders 엔티티를 조회하는 메서드에서 OrderItemStatus가 아닌 OrderStatus를 사용해야 합니다. OrderItemStatus는 주문 항목(OrderItem)의 상태를 나타내는 enum이며, Orders 엔티티는 OrderStatus를 가집니다.
🐛 수정 제안
- Page<Orders> findByMemberIdAndStatus(Long memberId, OrderItemStatus status, Pageable pageable);
+ Page<Orders> findByMemberIdAndStatus(Long memberId, OrderStatus status, Pageable pageable);그리고 import 수정:
-import com.kuit.baemin.domain.order.OrderItemStatus;
+import com.kuit.baemin.domain.order.OrderStatus;📝 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.
| Page<Orders> findByMemberIdAndStatus(Long memberId, OrderItemStatus status, Pageable pageable); | |
| Page<Orders> findByMemberIdAndStatus(Long memberId, OrderStatus status, Pageable pageable); |
🤖 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/OrderRepository.java` at line 11,
메서드 findByMemberIdAndStatus에서 잘못된 enum 타입을 사용하고 있으니 파라미터 타입을 OrderItemStatus에서
OrderStatus로 변경하고 관련 import 문도 OrderStatus로 교체하세요; 대상은 OrderRepository의
Page<Orders> findByMemberIdAndStatus(Long memberId, OrderStatus status, Pageable
pageable) 시그니처로 수정하는 것입니다.
|
|
||
| boolean existsByOrderId(Long orderId); | ||
|
|
||
| List<Review> findByMemberIdAndStatus(Long memberId, ReviewStatus status); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
페이징 지원을 고려하세요.
회원이 많은 리뷰를 작성한 경우 모든 리뷰를 한 번에 조회하면 성능 문제가 발생할 수 있습니다. OrderRepository의 findByMemberIdAndStatus처럼 Pageable 파라미터를 추가하여 Page<Review>를 반환하도록 변경하는 것을 권장합니다.
♻️ 수정 제안
- List<Review> findByMemberIdAndStatus(Long memberId, ReviewStatus status);
+ Page<Review> findByMemberIdAndStatus(Long memberId, ReviewStatus status, Pageable pageable);그리고 import 추가:
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;🤖 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/ReviewRepository.java` at line 13,
The current ReviewRepository method findByMemberIdAndStatus(Long memberId,
ReviewStatus status) should support pagination to avoid loading all reviews at
once; change its signature to return Page<Review> and add a Pageable parameter
(e.g., Page<Review> findByMemberIdAndStatus(Long memberId, ReviewStatus status,
Pageable pageable)), and add the necessary imports for
org.springframework.data.domain.Page and
org.springframework.data.domain.Pageable so callers can pass page/size/sort.
| private OrderRes toOrderRes(Orders order) { | ||
| List<OrderItem> orderItems = orderItemRepository.findByOrderIdAndStatus( | ||
| order.getId(), | ||
| OrderItemStatus.ACTIVE | ||
| ); | ||
|
|
||
| List<OrderItemRes> itemResponses = orderItems.stream() | ||
| .map(item -> { | ||
| List<OrderItemOptionRes> optionResponses = | ||
| orderItemOptionRepository.findByOrderItemIdAndStatus( | ||
| item.getId(), | ||
| OrderItemOptionStatus.ACTIVE | ||
| ) | ||
| .stream() | ||
| .map(OrderItemOptionRes::from) | ||
| .toList(); | ||
|
|
||
| return OrderItemRes.of(item, optionResponses); | ||
| }) | ||
| .toList(); | ||
|
|
||
| Integer totalPrice = calculateTotalPrice(orderItems); | ||
|
|
||
| return OrderRes.of(order, totalPrice, itemResponses); | ||
| } | ||
|
|
||
| private Integer calculateTotalPrice(List<OrderItem> orderItems) { | ||
| int total = 0; | ||
|
|
||
| for (OrderItem item : orderItems) { | ||
| List<OrderItemOption> options = orderItemOptionRepository.findByOrderItemIdAndStatus( | ||
| item.getId(), | ||
| OrderItemOptionStatus.ACTIVE | ||
| ); | ||
|
|
||
| int optionTotal = options.stream() | ||
| .mapToInt(OrderItemOption::getAdditionalPrice) | ||
| .sum(); | ||
|
|
||
| total += (item.getMenuPrice() + optionTotal) * item.getQuantity(); | ||
| } | ||
|
|
||
| return total; | ||
| } |
There was a problem hiding this comment.
N+1 쿼리 중복 발행을 개선하세요.
toOrderRes 메서드(Line 111-135)와 calculateTotalPrice 메서드(Line 137-154)에서 동일한 OrderItemOption 데이터를 각각 별도로 조회하고 있습니다:
- Lines 119-126: 각
OrderItem에 대해 옵션을 조회하여 DTO 변환 - Lines 141-144: 가격 계산을 위해 동일한 옵션을 재조회
이는 불필요한 중복 쿼리를 발생시켜 성능 저하를 초래합니다.
♻️ 개선 방안
방안 1: 가격 계산 로직을 DTO 변환 시점에 통합
private OrderRes toOrderRes(Orders order) {
List<OrderItem> orderItems = orderItemRepository.findByOrderIdAndStatus(
order.getId(),
OrderItemStatus.ACTIVE
);
+ int totalPrice = 0;
List<OrderItemRes> itemResponses = orderItems.stream()
.map(item -> {
List<OrderItemOptionRes> optionResponses =
orderItemOptionRepository.findByOrderItemIdAndStatus(
item.getId(),
OrderItemOptionStatus.ACTIVE
)
.stream()
.map(OrderItemOptionRes::from)
.toList();
+ int optionTotal = optionResponses.stream()
+ .mapToInt(opt -> opt.getAdditionalPrice())
+ .sum();
+ totalPrice += (item.getMenuPrice() + optionTotal) * item.getQuantity();
+
return OrderItemRes.of(item, optionResponses);
})
.toList();
- Integer totalPrice = calculateTotalPrice(orderItems);
-
return OrderRes.of(order, totalPrice, itemResponses);
}
-
-private Integer calculateTotalPrice(List<OrderItem> orderItems) {
- int total = 0;
-
- for (OrderItem item : orderItems) {
- List<OrderItemOption> options = orderItemOptionRepository.findByOrderItemIdAndStatus(
- item.getId(),
- OrderItemOptionStatus.ACTIVE
- );
-
- int optionTotal = options.stream()
- .mapToInt(OrderItemOption::getAdditionalPrice)
- .sum();
-
- total += (item.getMenuPrice() + optionTotal) * item.getQuantity();
- }
-
- return total;
-}방안 2: Repository에서 JOIN FETCH를 사용하여 옵션을 한 번에 로드
Repository에 다음과 같은 메서드 추가:
`@Query`("SELECT oi FROM OrderItem oi " +
"LEFT JOIN FETCH oi.orderItemOptions " +
"WHERE oi.order.id = :orderId AND oi.status = :status")
List<OrderItem> findByOrderIdAndStatusWithOptions(
`@Param`("orderId") Long orderId,
`@Param`("status") OrderItemStatus status
);📝 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.
| private OrderRes toOrderRes(Orders order) { | |
| List<OrderItem> orderItems = orderItemRepository.findByOrderIdAndStatus( | |
| order.getId(), | |
| OrderItemStatus.ACTIVE | |
| ); | |
| List<OrderItemRes> itemResponses = orderItems.stream() | |
| .map(item -> { | |
| List<OrderItemOptionRes> optionResponses = | |
| orderItemOptionRepository.findByOrderItemIdAndStatus( | |
| item.getId(), | |
| OrderItemOptionStatus.ACTIVE | |
| ) | |
| .stream() | |
| .map(OrderItemOptionRes::from) | |
| .toList(); | |
| return OrderItemRes.of(item, optionResponses); | |
| }) | |
| .toList(); | |
| Integer totalPrice = calculateTotalPrice(orderItems); | |
| return OrderRes.of(order, totalPrice, itemResponses); | |
| } | |
| private Integer calculateTotalPrice(List<OrderItem> orderItems) { | |
| int total = 0; | |
| for (OrderItem item : orderItems) { | |
| List<OrderItemOption> options = orderItemOptionRepository.findByOrderItemIdAndStatus( | |
| item.getId(), | |
| OrderItemOptionStatus.ACTIVE | |
| ); | |
| int optionTotal = options.stream() | |
| .mapToInt(OrderItemOption::getAdditionalPrice) | |
| .sum(); | |
| total += (item.getMenuPrice() + optionTotal) * item.getQuantity(); | |
| } | |
| return total; | |
| } | |
| private OrderRes toOrderRes(Orders order) { | |
| List<OrderItem> orderItems = orderItemRepository.findByOrderIdAndStatus( | |
| order.getId(), | |
| OrderItemStatus.ACTIVE | |
| ); | |
| int totalPrice = 0; | |
| List<OrderItemRes> itemResponses = new ArrayList<>(); | |
| for (OrderItem item : orderItems) { | |
| List<OrderItemOptionRes> optionResponses = | |
| orderItemOptionRepository.findByOrderItemIdAndStatus( | |
| item.getId(), | |
| OrderItemOptionStatus.ACTIVE | |
| ) | |
| .stream() | |
| .map(OrderItemOptionRes::from) | |
| .toList(); | |
| int optionTotal = optionResponses.stream() | |
| .mapToInt(OrderItemOptionRes::getAdditionalPrice) | |
| .sum(); | |
| totalPrice += (item.getMenuPrice() + optionTotal) * item.getQuantity(); | |
| itemResponses.add(OrderItemRes.of(item, optionResponses)); | |
| } | |
| return OrderRes.of(order, totalPrice, itemResponses); | |
| } |
🤖 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/OrderService.java` around lines 111 -
154, toOrderRes and calculateTotalPrice are causing duplicate option queries;
fetch OrderItemOptions once and reuse them by either (A) changing the repository
call in toOrderRes to a single eager-load method (e.g., replace
orderItemRepository.findByOrderIdAndStatus(...) with a new
findByOrderIdAndStatusWithOptions(...) that JOIN FETCHes orderItemOptions) and
then use the loaded options both to build OrderItemRes (OrderItemRes.of) and to
compute price, or (B) compute each item's subtotal while mapping in toOrderRes
(use the options loaded there) and remove the separate option lookups from
calculateTotalPrice (or change calculateTotalPrice to accept the already-loaded
OrderItem list with options); update references to OrderItemOptionRepository
lookups (in toOrderRes and calculateTotalPrice) accordingly so options are
queried only once per order.
| url: jdbc:mysql://localhost:3306/baemin #jdbc:mysql://localhost:3306/{본인이 저번주차에 생성했던 DB 이름} | ||
| username: root # 본인 DB username 환경변수에 저장 | ||
| password: 26072005B! # 본인 DB 비밀번호 환경변수에 저장 |
There was a problem hiding this comment.
민감정보 하드코딩을 즉시 제거하세요 (Line 3, Line 4, Line 5).
password를 포함한 DB 자격증명이 저장소에 평문으로 커밋되어 보안 사고로 직결됩니다. 환경변수/시크릿 매니저로 치환하고, 이미 노출된 비밀번호는 즉시 회전(rotate)해야 합니다.
수정 예시
spring:
datasource:
- url: jdbc:mysql://localhost:3306/baemin `#jdbc`:mysql://localhost:3306/{본인이 저번주차에 생성했던 DB 이름}
- username: root # 본인 DB username 환경변수에 저장
- password: 26072005B! # 본인 DB 비밀번호 환경변수에 저장
+ url: ${DATASOURCE_URL}
+ username: ${DATASOURCE_USERNAME}
+ password: ${DATASOURCE_PASSWORD}
driver-class-name: com.mysql.cj.jdbc.Driver🤖 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/resources/application.yml` around lines 3 - 5, Remove the hardcoded
DB credentials in application.yml (the url, username, and password properties)
and replace them with environment-backed placeholders (e.g., use environment
variables for DB_URL, DB_USERNAME, DB_PASSWORD) so no plaintext secrets remain
in the repo; update any code/config that reads these properties to use the new
env vars and ensure secrets are loaded via your platform's secret manager or
environment, and immediately rotate the exposed password.
| jpa: | ||
| hibernate: | ||
| ddl-auto: create # 개발 중에는 create, 이후 validate로 변경 | ||
| ddl-auto: update # 개발 중에는 create, 이후 validate로 변경 |
There was a problem hiding this comment.
기본 설정의 ddl-auto: update는 환경 분리 없으면 위험합니다 (Line 10).
현재 기본 application.yml에 update가 고정되어 있으면 운영/공유 DB에서 의도치 않은 스키마 변경이 발생할 수 있습니다. 최소한 profile 별로 분리해 로컬만 update, 운영은 validate(또는 none)로 제한하세요.
수정 예시
# application.yml (공통/기본)
spring:
jpa:
hibernate:
- ddl-auto: update
+ ddl-auto: validate# application-local.yml (로컬 전용)
spring:
jpa:
hibernate:
ddl-auto: update🤖 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/resources/application.yml` at line 10, The default configuration
currently sets spring.jpa.hibernate.ddl-auto: update in application.yml which
risks unintended schema changes in non-local environments; change configuration
to remove/update this default and move environment-specific settings into
profile-specific files (e.g., create application-local.yml with
spring.jpa.hibernate.ddl-auto: update and ensure production/other profiles use
spring.jpa.hibernate.ddl-auto: validate or none) and ensure the active profile
selection is used at runtime so the application does not apply `update` unless
the "local" profile is explicitly active.
구현한 API 목록
Summary by CodeRabbit
릴리스 노트