-
Notifications
You must be signed in to change notification settings - Fork 0
Cart도메인 crud기능 추가 #50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…기능추가 20251107 #17 cart domain crud기능추가
…기능추가 deleteCartItem에서 빈장바구니 정리로직추가 reqeustdto에 카멜케이스로 변경 에러코드 통일
Walkthrough쇼핑 카트 기능을 위한 새로운 REST API 엔드포인트, 서비스 계층, JPA 엔티티, 저장소, 그리고 데이터 전송 객체들을 도입합니다. 카트 항목 추가/수정, 조회, 삭제 기능과 사용자 인증 유틸리티가 포함됩니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller as CartController
participant Service as CartService
participant Repos as Repositories
participant Entities as Entities & DB
rect rgb(200, 220, 255)
Note over Client, Entities: 카트 항목 추가/수정
Client ->> Controller: POST /api/cart/items (menuId, quantity)
Controller ->> Service: updateCartItem(menuId, quantity)
Service ->> Repos: getCurrentUserId()
Repos -->> Service: userId
Service ->> Repos: findByUserId(userId)
Repos -->> Service: Optional<Cart>
alt Cart 없음
Service ->> Repos: findMenuById(menuId)
Repos -->> Service: Menu
Service ->> Entities: create Cart(user)
Service ->> Repos: save(cart)
Repos -->> Entities: persist
end
Service ->> Repos: findByCartAndMenu(cart, menu)
Repos -->> Service: Optional<CartItem>
alt CartItem 있음
Service ->> Entities: cartItem.setQuantity(quantity)
else CartItem 없음
Service ->> Entities: create CartItem(cart, menu)
Service ->> Repos: save(cartItem)
Repos -->> Entities: persist
end
Service ->> Repos: flush()
Service ->> Service: buildCartResponse()
Service -->> Controller: CartResponse
Controller -->> Client: ResponseEntity<CartResponse>
end
rect rgb(220, 255, 220)
Note over Client, Entities: 카트 조회
Client ->> Controller: GET /api/cart
Controller ->> Service: getCart()
Service ->> Repos: findByUserId(userId)
Repos -->> Service: Optional<Cart>
Service ->> Service: convertToDTO(cartItems)
Service -->> Controller: CartResponse
Controller -->> Client: ResponseEntity<CartResponse>
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 분 이 변경사항은 다중 엔티티 관계(User ↔ Cart ↔ CartItem ↔ Menu), 복잡한 서비스 로직(검증, 상태 관리, 에러 처리), 새로운 REST API 엔드포인트, 보안 통합을 포함합니다. 12개 파일 수정, 중복 패턴 없는 이질적인 변경, CartService의 높은 로직 밀도로 인해 상당한 검토 노력이 필요합니다. Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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 |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (13)
src/main/java/com/campustable/be/domain/cart/entity/Cart.java (1)
31-33: OneToOne 관계에 LAZY 로딩을 명시적으로 설정하는 것을 권장합니다.
@OneToOne관계의 기본 fetch 전략은 EAGER입니다. 불필요한 조인을 방지하고 성능을 개선하기 위해fetch = FetchType.LAZY를 명시하는 것이 좋습니다.🔎 수정 제안
- @OneToOne + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user;src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
13-13: totalPrice를 long 타입으로 변경하는 것을 고려하세요.
int타입은 최대 약 21억까지만 표현 가능합니다. 장바구니 총액이 커질 경우 오버플로우가 발생할 수 있습니다.long타입 사용을 권장합니다.🔎 수정 제안
private List<CartItemDto> items; - private int totalPrice; + private long totalPrice; private Long cartId;src/main/java/com/campustable/be/domain/cart/entity/CartItem.java (3)
31-35:@JoinColumn어노테이션 누락
@ManyToOne관계에서@JoinColumn을 명시하지 않으면 JPA가 기본 컬럼명(menu_id,cart_id)을 생성하지만, 명시적으로 지정하는 것이 스키마 일관성과 가독성에 좋습니다.🔎 제안된 수정
@ManyToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "menu_id") private Menu menu; @ManyToOne(fetch = FetchType.LAZY) +@JoinColumn(name = "cart_id") private Cart cart;
37-38:@Min검증이 자동으로 실행되지 않음엔티티 필드에
@Min어노테이션만 추가하면 영속화 시점에 자동으로 검증되지 않습니다. Bean Validation이 동작하려면hibernate.validator.fail_fast설정 또는@Valid사용이 필요하며, 현재setQuantity()호출 시 검증이 우회됩니다.서비스 레이어에서 quantity 값을 검증하거나, setter에 직접 검증 로직을 추가하는 것을 권장합니다.
🔎 setter에서 검증하는 방식
public void setQuantity(int quantity) { if (quantity < 0) { throw new IllegalArgumentException("수량은 0 이상이어야 합니다"); } this.quantity = quantity; }
22-25: 생성자에서 quantity 초기화 누락생성자에서
quantity를 초기화하지 않아 기본값 0이 설정됩니다. 서비스 코드에서setQuantity()를 별도로 호출하고 있으나, 생성자 파라미터로 quantity를 받으면 객체 생성이 더 명확해집니다.🔎 제안된 수정
-public CartItem(Cart cart, Menu menu){ +public CartItem(Cart cart, Menu menu, int quantity){ this.cart = cart; this.menu = menu; + this.quantity = quantity; }src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java (1)
6-11: 요청 DTO에 입력 검증 누락API 문서에 따르면
menuId는 필수이고quantity는 0 이상이어야 합니다. 클라이언트에서 잘못된 값을 보내면 검증 없이 서비스 레이어까지 전달됩니다.@Valid와 함께 사용할 수 있도록 검증 어노테이션을 추가하세요.🔎 제안된 수정
package com.campustable.be.domain.cart.dto; import lombok.Getter; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; @Getter public class CartRequest { + @NotNull(message = "메뉴 ID는 필수입니다") private Long menuId; + + @Min(value = 0, message = "수량은 0 이상이어야 합니다") private int quantity; }컨트롤러에서도
@Valid어노테이션을 추가해야 검증이 동작합니다:public ResponseEntity<CartResponse> addOrUpdateItems(@Valid @RequestBody CartRequest request)src/main/java/com/campustable/be/domain/cart/controller/CartControllerDocs.java (1)
69-69: 삭제 API의 응답 타입 일관성
deleteCart와deleteCartItem메서드가void를 반환하면 HTTP 상태 코드를 명시적으로 제어하기 어렵습니다. 다른 엔드포인트와 일관성을 위해ResponseEntity<Void>를 반환하고204 No Content를 명시하는 것을 권장합니다.Also applies to: 87-87
src/main/java/com/campustable/be/domain/cart/service/CartService.java (4)
56-67: 조건문 스타일 일관성Line 60에서
else브랜치에 중괄호가 없습니다. 코드 일관성과 유지보수성을 위해 항상 중괄호를 사용하는 것이 좋습니다.🔎 제안된 수정
if (cartItemOpt.isPresent()) { - if (quantity == 0){ + if (quantity == 0) { cartItemRepository.delete(cartItemOpt.get()); + } else { + cartItemOpt.get().setQuantity(quantity); } - else cartItemOpt.get().setQuantity(quantity); } else {
84-86: 중복 코드: totalPrice 계산 로직
totalPrice계산 로직이updateCartItem과getCart메서드에서 동일하게 반복됩니다. 별도의 private 메서드로 추출하면 유지보수성이 향상됩니다.🔎 제안된 수정
private int calculateTotalPrice(List<CartItemDto> cartItems) { return cartItems.stream() .mapToInt(item -> item.getPrice() * item.getQuantity()) .sum(); }Also applies to: 150-152
134-168: 읽기 전용 트랜잭션 최적화
getCart()메서드는 데이터를 조회만 하므로@Transactional(readOnly = true)를 사용하면 Hibernate의 dirty checking을 비활성화하여 성능이 향상됩니다.🔎 제안된 수정
+@Transactional(readOnly = true) public CartResponse getCart(){
75-82: 중복 코드: 빈 장바구니 삭제 로직빈 장바구니를 삭제하는 로직이
updateCartItem과deleteCartItem에서 반복됩니다. private 메서드로 추출하여 재사용하는 것을 권장합니다.🔎 제안된 수정
private void deleteCartIfEmpty(Cart cart) { if (cartItemRepository.findByCart(cart).isEmpty()) { cart.getUser().setCart(null); cartRepository.delete(cart); } }Also applies to: 111-114
src/main/java/com/campustable/be/domain/cart/controller/CartController.java (2)
46-59: DELETE 엔드포인트 응답 개선
void를 반환하면 Spring이 기본적으로 200 OK를 반환합니다. RESTful 관례상 삭제 성공 시204 No Content를 반환하는 것이 적절합니다.🔎 제안된 수정
@DeleteMapping("/{cartId}") -public void deleteCart(@PathVariable Long cartId) { +public ResponseEntity<Void> deleteCart(@PathVariable Long cartId) { cartService.deleteCart(cartId); + return ResponseEntity.noContent().build(); } @DeleteMapping("/items/{cartItemId}") -public void deleteCartItem(@PathVariable Long cartItemId) { +public ResponseEntity<Void> deleteCartItem(@PathVariable Long cartItemId) { cartService.deleteCartItem(cartItemId); + return ResponseEntity.noContent().build(); }
22-22: 사용되지 않는@Slf4j어노테이션컨트롤러에서
log객체를 사용하지 않습니다. 불필요한 어노테이션은 제거하거나, 디버깅/모니터링 목적의 로깅을 추가하세요.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
src/main/java/com/campustable/be/domain/cart/controller/CartController.javasrc/main/java/com/campustable/be/domain/cart/controller/CartControllerDocs.javasrc/main/java/com/campustable/be/domain/cart/dto/CartItemDto.javasrc/main/java/com/campustable/be/domain/cart/dto/CartRequest.javasrc/main/java/com/campustable/be/domain/cart/dto/CartResponse.javasrc/main/java/com/campustable/be/domain/cart/entity/Cart.javasrc/main/java/com/campustable/be/domain/cart/entity/CartItem.javasrc/main/java/com/campustable/be/domain/cart/repository/CartItemRepository.javasrc/main/java/com/campustable/be/domain/cart/repository/CartRepository.javasrc/main/java/com/campustable/be/domain/cart/service/CartService.javasrc/main/java/com/campustable/be/domain/user/entity/User.javasrc/main/java/com/campustable/be/global/common/SecurityUtil.javasrc/main/java/com/campustable/be/global/exception/ErrorCode.java
🧰 Additional context used
🧬 Code graph analysis (6)
src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java (2)
src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java (1)
Getter(8-24)src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
Getter(8-15)
src/main/java/com/campustable/be/domain/cart/service/CartService.java (1)
src/main/java/com/campustable/be/global/common/SecurityUtil.java (1)
Slf4j(9-33)
src/main/java/com/campustable/be/domain/cart/entity/CartItem.java (5)
src/main/java/com/campustable/be/domain/cart/entity/Cart.java (1)
Entity(20-43)src/main/java/com/campustable/be/domain/user/entity/User.java (1)
Entity(13-49)src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java (1)
Getter(6-11)src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java (1)
Getter(8-24)src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
Getter(8-15)
src/main/java/com/campustable/be/domain/cart/entity/Cart.java (5)
src/main/java/com/campustable/be/domain/cart/entity/CartItem.java (1)
Entity(16-43)src/main/java/com/campustable/be/domain/user/entity/User.java (1)
Entity(13-49)src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java (1)
Getter(6-11)src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java (1)
Getter(8-24)src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
Getter(8-15)
src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java (3)
src/main/java/com/campustable/be/domain/cart/dto/CartRequest.java (1)
Getter(6-11)src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
Getter(8-15)src/main/java/com/campustable/be/domain/auth/dto/ReissueRequest.java (1)
Setter(8-15)
src/main/java/com/campustable/be/domain/cart/dto/CartResponse.java (1)
src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java (1)
Getter(8-24)
🔇 Additional comments (6)
src/main/java/com/campustable/be/global/exception/ErrorCode.java (1)
66-71: LGTM!Cart 도메인을 위한 에러 코드가 올바르게 추가되었습니다. 기존 패턴과 일관되게 구현되었습니다.
src/main/java/com/campustable/be/domain/user/entity/User.java (1)
27-28: LGTM!User-Cart 양방향 관계가 올바르게 설정되었습니다.
cascade와orphanRemoval설정도 적절합니다.src/main/java/com/campustable/be/domain/cart/repository/CartItemRepository.java (1)
11-18: LGTM!CartItem 조회를 위한 쿼리 메서드들이 Spring Data JPA 네이밍 규칙에 따라 올바르게 정의되었습니다.
src/main/java/com/campustable/be/global/common/SecurityUtil.java (1)
14-31: LGTM!인증 정보 검증과 에러 처리가 적절하게 구현되었습니다. 로깅도 잘 되어 있고,
NumberFormatException처리도 적절합니다.src/main/java/com/campustable/be/domain/cart/repository/CartRepository.java (1)
9-14: LGTM!Cart 조회를 위한 메서드들이 적절하게 정의되었습니다.
findByUser와findByUserId모두 각각의 사용 시나리오에서 유용합니다.src/main/java/com/campustable/be/domain/cart/service/CartService.java (1)
69-69:flush()호출의 필요성 검토
@Transactional범위 내에서 명시적인flush()호출은 일반적으로 불필요합니다. 트랜잭션 커밋 시점에 자동으로 flush됩니다. 특별한 이유(예: 삭제 후 즉시 카운트 조회)가 있다면 주석으로 의도를 명시해 주세요.Also applies to: 109-109
| public ResponseEntity<CartResponse> addOrUpdateItems(@RequestBody CartRequest request) { | ||
| CartResponse response = cartService.updateCartItem(request.getMenuId(), request.getQuantity()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Valid 어노테이션 누락
CartRequest에 검증 어노테이션을 추가했다면, @Valid를 함께 사용해야 Bean Validation이 동작합니다.
🔎 제안된 수정
-public ResponseEntity<CartResponse> addOrUpdateItems(@RequestBody CartRequest request) {
+public ResponseEntity<CartResponse> addOrUpdateItems(@Valid @RequestBody CartRequest request) {import 추가:
import jakarta.validation.Valid;📝 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 ResponseEntity<CartResponse> addOrUpdateItems(@RequestBody CartRequest request) { | |
| CartResponse response = cartService.updateCartItem(request.getMenuId(), request.getQuantity()); | |
| public ResponseEntity<CartResponse> addOrUpdateItems(@Valid @RequestBody CartRequest request) { | |
| CartResponse response = cartService.updateCartItem(request.getMenuId(), request.getQuantity()); |
🤖 Prompt for AI Agents
In src/main/java/com/campustable/be/domain/cart/controller/CartController.java
around lines 30-31, the CartRequest parameter is missing the @Valid annotation
so bean validation won't run; add the jakarta.validation.Valid import and
annotate the @RequestBody parameter with @Valid (i.e., change the method
signature to accept @Valid @RequestBody CartRequest request) so validation
constraints on CartRequest are enforced.
| description = """ | ||
| ### 요청 파라미터 | ||
| - `menu_id` (Long, required): 장바구니에 담을 메뉴 ID | ||
| - `quantity` (int, required): 담을 수량 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API 문서와 DTO 필드명 불일치
문서에서 menu_id로 설명하고 있지만, 실제 CartRequest DTO는 menuId (camelCase)를 사용합니다. 커밋 메시지에서도 camelCase로 통일했다고 언급되어 있으니, 문서도 일치시켜 주세요.
🔎 제안된 수정
description = """
### 요청 파라미터
- - `menu_id` (Long, required): 장바구니에 담을 메뉴 ID
+ - `menuId` (Long, required): 장바구니에 담을 메뉴 ID
- `quantity` (int, required): 담을 수량 📝 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.
| description = """ | |
| ### 요청 파라미터 | |
| - `menu_id` (Long, required): 장바구니에 담을 메뉴 ID | |
| - `quantity` (int, required): 담을 수량 | |
| description = """ | |
| ### 요청 파라미터 | |
| - `menuId` (Long, required): 장바구니에 담을 메뉴 ID | |
| - `quantity` (int, required): 담을 수량 |
🤖 Prompt for AI Agents
In
src/main/java/com/campustable/be/domain/cart/controller/CartControllerDocs.java
around lines 14 to 17, the API doc uses snake_case `menu_id` while the
CartRequest DTO uses camelCase `menuId`; update the documentation to use
`menuId` to match the DTO (and adjust any other doc occurrences of `menu_id` in
this file), keeping parameter type and description unchanged so docs and DTO are
consistent.
| public CartItemDto(CartItem entity) { | ||
| this.menuName = entity.getMenu().getMenuName(); | ||
| this.price = entity.getMenu().getPrice(); | ||
| this.quantity = entity.getQuantity(); | ||
| this.menuUrl = entity.getMenu().getMenuUrl(); | ||
| this.cartItemId = entity.getCartItemId(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
생성자에서 null 안전성을 확보하세요.
entity.getMenu()가 null일 경우 NullPointerException이 발생합니다. Menu와 CartItem 간의 관계가 @ManyToOne으로 설정되어 있어 이론적으로 null이 가능하므로, 방어 코드를 추가하는 것이 좋습니다.
🔎 수정 제안
public CartItemDto(CartItem entity) {
+ if (entity.getMenu() == null) {
+ throw new IllegalArgumentException("CartItem must have a Menu");
+ }
this.menuName = entity.getMenu().getMenuName();
this.price = entity.getMenu().getPrice();
this.quantity = entity.getQuantity();
this.menuUrl = entity.getMenu().getMenuUrl();
this.cartItemId = entity.getCartItemId();
}🤖 Prompt for AI Agents
In src/main/java/com/campustable/be/domain/cart/dto/CartItemDto.java around
lines 17 to 23, the constructor assumes entity.getMenu() is non-null which can
cause a NullPointerException; update the constructor to defensively check that
entity and entity.getMenu() are not null and assign menuName, price, menuUrl
from the Menu only when present (otherwise set sensible defaults or null), and
still assign cartItemId and quantity from entity; you can implement this with
simple null checks (if/else) or using Optional/ternary operators to keep
assignments null-safe.
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| @Column(name = "cart_id") | ||
| private Long cart_id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Java 네이밍 규칙 위반: 필드명을 camelCase로 수정하세요.
필드명 cart_id는 Java 네이밍 규칙을 위반합니다. @Column 어노테이션이 이미 데이터베이스 컬럼명을 "cart_id"로 매핑하고 있으므로, 필드명은 cartId로 작성해야 합니다.
🔎 수정 제안
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cart_id")
- private Long cart_id;
+ private Long cartId;🤖 Prompt for AI Agents
In src/main/java/com/campustable/be/domain/cart/entity/Cart.java around lines 26
to 29, the field name cart_id violates Java camelCase conventions; rename the
private field to cartId, keep the @Column(name = "cart_id") annotation unchanged
so the DB mapping stays the same, and update all references (getters/setters,
constructors, equals/hashCode, usages) in this class and any other classes to
use cartId.
| public Cart(User user) { | ||
| this.user = user; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
양방향 관계를 올바르게 유지하도록 생성자를 수정하세요.
생성자에서 user만 설정하고 User 엔티티의 cart 필드는 설정하지 않아 양방향 관계가 불완전합니다. JPA 영속성 컨텍스트 외부에서 객체를 사용할 때 일관성 문제가 발생할 수 있습니다.
🔎 수정 제안
public Cart(User user) {
this.user = user;
+ if (user != null) {
+ user.setCart(this);
+ }
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/java/com/campustable/be/domain/cart/entity/Cart.java around lines 38
to 40, the Cart constructor sets only this.user leaving the User side of the
bidirectional relation unset; update the constructor to assign this.user = user
and also set the back-reference on the User (e.g., if user != null then
user.setCart(this)) so the bidirectional association stays consistent outside
JPA.
| if (!cartItem.getCart().getUser().getUserId().equals(userId)) { | ||
| throw new CustomException(ErrorCode.ACCESS_DENIED); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
소유권 검증 시 NPE 위험
cartItem.getCart().getUser().getUserId()와 cart.getUser().getUserId() 체인에서 중간 객체가 null이면 NullPointerException이 발생할 수 있습니다. LAZY 로딩으로 인해 연관 객체가 로드되지 않았거나 데이터 정합성 문제가 있을 경우를 대비한 방어 코드가 필요합니다.
🔎 Line 102-104 수정 제안
+ Cart cart = cartItem.getCart();
+ if (cart == null || cart.getUser() == null) {
+ throw new CustomException(ErrorCode.CART_NOT_FOUND);
+ }
+
- if (!cartItem.getCart().getUser().getUserId().equals(userId)) {
+ if (!cart.getUser().getUserId().equals(userId)) {
throw new CustomException(ErrorCode.ACCESS_DENIED);
}
-
- Cart cart = cartItem.getCart();Also applies to: 125-127
🤖 Prompt for AI Agents
In src/main/java/com/campustable/be/domain/cart/service/CartService.java around
lines 102-104 (and similarly for 125-127), the chained calls
cartItem.getCart().getUser().getUserId() (and cart.getUser().getUserId()) can
throw NPE if any intermediate object is null; replace the direct chain with
defensive null-checks (or Optional) to ensure cartItem.getCart() and its
getUser() are non-null before comparing IDs, and use a null-safe comparison
(e.g., Objects.equals or compare non-null userId values) so that if any required
object or ID is missing you treat it as access denied and throw
CustomException(ErrorCode.ACCESS_DENIED).
✨ 변경 사항
Cart도메인 crud기능 추가
✅ 테스트
Summary by CodeRabbit
릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings.