Skip to content

Conversation

@codrin2
Copy link
Member

@codrin2 codrin2 commented Sep 10, 2025

Issue Number

#35

As-Is

  • Jackson 직렬화/역직렬화 설정 부재로 매번 수동 변환 처리 발생
  • 장바구니 조회 시 응답 시간 지연

To-Be

  • Jackson 커스텀 Serializer/Deserializer 적용으로 DTO단에서 id가 Long or String 타입으로 매핑
  • toString, toLong() 수동 변환 제거
  • 코드 단순화 및 직렬화/역직렬화 일관성 확보
  • Cart의 User, Product 연관관계 제거

✅ Check List

  • Have all tests passed?
  • Have all commits been pushed?
  • Did you verify the target branch for the merge?
  • Did you assign the appropriate assignee(s)?
  • Did you set the correct label(s)?

📸 Test Screenshot

Additional Description

@codrin2 codrin2 self-assigned this Sep 10, 2025
@codrin2 codrin2 linked an issue Sep 10, 2025 that may be closed by this pull request
@coderabbitai
Copy link

coderabbitai bot commented Sep 10, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

프로젝트 전반에서 식별자 타입을 String → Long으로 일괄 변환하고, JSON 출력은 Long을 문자열로 직렬화하도록 커스텀 Serializer를 도입했습니다. 장바구니 도메인은 엔티티 연관을 제거하고 FK ID(primitive)로 전환했으며, CartFacade가 UserService 및 ProductQueryService를 통해 검증/조회 후 상품 정보와 CartItem을 번들링해 반환합니다.

Changes

Cohort / File(s) Summary of changes
Core ID 직렬화 인프라
src/main/kotlin/com/dh/baro/core/Cursor.kt, src/main/kotlin/com/dh/baro/core/LongToStringSerializer.kt
Long→String JSON 직렬화기 추가 및 적용; Cursor.id 타입 String→Long(+Serializer).
Cart 도메인 FK primitive 전환
src/main/kotlin/com/dh/baro/cart/domain/CartItem.kt, .../CartItemRepository.kt, .../CartService.kt
CartItem에서 User/Product 연관 제거, userId/productId 필드 도입 및 팩토리 시그니처 변경; Service에서 존재 검증 제거하고 무결성 예외로 병합 처리; Repository의 EntityGraph 제거.
Cart 애플리케이션/프레젠테이션
src/main/kotlin/com/dh/baro/cart/application/CartFacade.kt, .../CartItemBundle.kt, .../presentation/CartController.kt, .../presentation/dto/* (AddItemRequest.kt, CartItemResponse.kt, CartResponse.kt), .../presentation/swagger/CartSwagger.kt
CartFacade에 UserService/ProductQueryService 의존 추가 및 트랜잭션 어노테이션; 장바구니 조회가 CartItemBundle 리스트 반환(상품 로딩/매핑 포함); API에서 itemId/productId를 Long으로 변경, JSON 직렬화 어노테이션 추가; 요청 DTO의 productId String→Long.
Look 프레젠테이션
src/main/kotlin/com/dh/baro/look/presentation/LookController.kt, .../dto/* (LookCreateRequest.kt, LookCreateResponse.kt, LookDetailResponse.kt, LookDto.kt), .../swagger/LookSwagger.kt
Path/Query 파라미터와 DTO의 ID를 String→Long으로 변경, 필요한 필드에 Long→String 직렬화 적용; 신규 필드(likesCount, lookImageUrls, products) 추가.
Order 애플리케이션/프레젠테이션
src/main/kotlin/com/dh/baro/order/application/OrderCreateCommand.kt, .../OrderFacade.kt, .../presentation/OrderController.kt, .../presentation/dto/* (OrderCreateRequest.kt, OrderDetailResponse.kt, OrderSummary.kt), .../presentation/swagger/OrderSwagger.kt
Product ID 조회/전달에서 toLong 제거 후 Long 직접 사용; 컨트롤러 파라미터 및 DTO의 ID를 Long으로 변경하고 직렬화 적용; OrderDetail에 orderedAt 추가.
Product 프레젠테이션/애플리케이션
src/main/kotlin/com/dh/baro/product/application/CategoryFacade.kt, .../presentation/ProductController.kt, .../presentation/dto/* (CategoryCreateRequest.kt, CategoryResponse.kt, PopularCursor.kt, ProductCreateRequest.kt, ProductCreateResponse.kt, ProductDetail.kt, ProductListItem.kt), .../presentation/swagger/ProductSwagger.kt
카테고리/상품 관련 요청·응답 DTO 및 컨트롤러의 ID·커서 타입을 Long으로 변경하고 직렬화 적용; 생성 요청에서 storeId/categoryIds Long 사용 및 변환 로직 제거; Popular/Newest 목록 커서 Long 사용.
Identity 프레젠테이션
src/main/kotlin/com/dh/baro/identity/presentation/dto/UserProfileResponse.kt
id를 Long으로 변경하고 JSON 직렬화 어노테이션 적용.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Controller as CartController
  participant Facade as CartFacade
  participant UserSvc as UserService
  participant CartSvc as CartService
  participant ProdQuery as ProductQueryService

  rect rgb(245,248,255)
  note over Client,Facade: 조회: GET /cart
  Client->>Controller: getCart(userId)
  Controller->>Facade: getCartItems(userId)
  Facade->>UserSvc: getUserById(userId)
  UserSvc-->>Facade: User
  Facade->>CartSvc: findByUserId(userId)
  CartSvc-->>Facade: List<CartItem>
  Facade->>ProdQuery: getProductsByIds(productIds)
  ProdQuery-->>Facade: List<Product>
  Facade-->>Controller: List<CartItemBundle>
  Controller-->>Client: CartResponse
  end

  rect rgb(245,255,245)
  note over Client,CartSvc: 추가: POST /cart/items
  Client->>Controller: addItem(userId, productId, qty)
  Controller->>Facade: addItem(userId, request)
  Facade->>UserSvc: getUserById(userId)
  UserSvc-->>Facade: User
  Facade->>ProdQuery: getProduct(productId)
  ProdQuery-->>Facade: Product
  Facade->>CartSvc: addItem(userId, productId, qty)
  alt 신규 행 삽입
    CartSvc-->>Facade: CartItem
  else 중복 무결성 충돌
    CartSvc-->>Facade: CartItem(수량 증가)
  end
  Facade-->>Controller: CartItem
  Controller-->>Client: 200 OK
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60–90 minutes

Possibly related PRs

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 438e728 and 4d0ce43.

📒 Files selected for processing (8)
  • src/main/kotlin/com/dh/baro/cart/domain/CartService.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/cart/presentation/dto/AddItemRequest.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/look/presentation/dto/LookCreateRequest.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/order/presentation/dto/OrderCreateRequest.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/ProductController.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/dto/CategoryCreateRequest.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/dto/ProductCreateRequest.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/swagger/ProductSwagger.kt (2 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/#35

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.

@codrin2 codrin2 changed the title Refactor/#35 [REFACTOR] 장바구니 조회 기능 개선 Sep 10, 2025
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: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/main/kotlin/com/dh/baro/cart/presentation/swagger/CartSwagger.kt (1)

33-49: Swagger 예시 JSON의 ID 값을 문자열로 변경
CartItemResponse DTO에 @JsonSerialize(using = LongToStringSerializer::class)가 적용되어 ID가 문자열로 직렬화되므로, CartSwagger.kt의 examples 섹션(itemId/productId)에 쌍따옴표를 추가하세요.
– 응답 예시: "itemId": "101", "productId": "11"
– 요청 예시(상품 추가): "productId": "11"

src/main/kotlin/com/dh/baro/look/presentation/swagger/LookSwagger.kt (2)

27-31: 문서 오타 수정 필요(사용자 노출).

  • "productIds는 최초애 등록된…" → "최초에"
  • "reationType" → "reactionType"

다음 패치를 제안합니다:

-            productIds는 최초애 등록된 순서를 유지합니다.
+            productIds는 최초에 등록된 순서를 유지합니다.
...
-            reationType = [LIKE, DISLIKE]
+            reactionType = [LIKE, DISLIKE]

Also applies to: 69-70


180-199: 예시 응답이 실제 DTO와 불일치.

LookDetailResponse는 images 대신 lookImageUrls(List), 제품 항목은 name→productName, storeName 포함, displayOrder 미노출입니다. 문서-구현 불일치를 반드시 정정하세요.

예시 교체 패치:

-                        {
-                          "lookId": 1001,
-                          "title": "여름 바캉스룩",
-                          "description": "린넨 셔츠 + 쇼츠 + 로퍼",
-                          "thumbnailUrl": "https://example.com/look-thumb.jpg",
-                          "likesCount": 42,
-                          "images": [
-                            { "imageUrl": "https://example.com/look-1.jpg", "displayOrder": 1 },
-                            { "imageUrl": "https://example.com/look-2.jpg", "displayOrder": 2 }
-                          ],
-                          "products": [
-                            { "productId": 101, "name": "린넨 셔츠", "price": 39000, "thumbnailUrl": "https://...", "displayOrder": 1 },
-                            { "productId": 102, "name": "베이지 쇼츠", "price": 29000, "thumbnailUrl": "https://...", "displayOrder": 2 }
-                          ]
-                        }
+                        {
+                          "lookId": 1001,
+                          "title": "여름 바캉스룩",
+                          "description": "린넨 셔츠 + 쇼츠 + 로퍼",
+                          "thumbnailUrl": "https://example.com/look-thumb.jpg",
+                          "likesCount": 42,
+                          "lookImageUrls": [
+                            "https://example.com/look-1.jpg",
+                            "https://example.com/look-2.jpg"
+                          ],
+                          "products": [
+                            { "productId": 101, "storeName": "A스토어", "productName": "린넨 셔츠", "price": 39000, "thumbnailUrl": "https://..." },
+                            { "productId": 102, "storeName": "B스토어", "productName": "베이지 쇼츠", "price": 29000, "thumbnailUrl": "https://..." }
+                          ]
+                        }
src/main/kotlin/com/dh/baro/product/presentation/swagger/ProductSwagger.kt (1)

112-119: Swagger 예시 JSON의 ID 필드를 문자열로 변경
ProductListItem.idPopularCursor.id@JsonSerialize(using = LongToStringSerializer::class)이 적용돼 실제 응답 시 문자열로 직렬화되므로, Swagger 예시 블록에서 아래와 같이 "id": 21"id": "21""nextCursor": { "id": 21, ... }"nextCursor": { "id": "21", ... }로 수정하세요.

-    { "id": 21, "storeName": "무신사", "productName": "Sneakers", "price": 99000, "thumbnailUrl": "..." }
+    { "id": "21", "storeName": "무신사", "productName": "Sneakers", "price": 99000, "thumbnailUrl": "..." }
...
-    "nextCursor": { "id": 21, "likes": 500 }
+    "nextCursor": { "id": "21", "likes": 500 }
🧹 Nitpick comments (33)
src/main/kotlin/com/dh/baro/core/StringListToLongListDeserializer.kt (2)

3-8: 불필요한 TypeFactory 의존 제거(또는 ctxt.typeFactory 사용)

위 개선안을 적용하면 CollectionType, TypeFactory import가 불필요합니다. 만약 현재 방식을 유지한다면 TypeFactory.defaultInstance() 대신 ctxt.typeFactory를 사용해 ObjectMapper 레벨 설정과 일관되게 가는 것을 권장합니다.

불필요 import 제거(diff):

 import com.fasterxml.jackson.core.JsonParser
 import com.fasterxml.jackson.databind.DeserializationContext
 import com.fasterxml.jackson.databind.JsonDeserializer
-import com.fasterxml.jackson.databind.type.CollectionType
-import com.fasterxml.jackson.databind.type.TypeFactory
+import com.fasterxml.jackson.core.JsonToken

9-15: 역직렬화 단위 테스트 커버리지 제안

다음 케이스에 대한 단위 테스트 추가를 권장합니다: ["1","2"], [1,2,3], [1,"2",3], ["", "x"], null 포함 요소. 실패 케이스는 메시지에 필드 경로가 포함되는지 검증해 주세요.

원하시면 JUnit5+Jackson(ObjectMapper) 기반 테스트 스켈레톤을 생성해 드립니다.

src/main/kotlin/com/dh/baro/look/presentation/dto/LookCreateRequest.kt (1)

22-24: 컬렉션 전체 deserializer 대신 contentUsing으로 단순화(선택)

프로젝트에 단건용 StringToLongDeserializer가 이미 있다면, 리스트 전체용 커스텀 클래스 없이 요소 단위 변환으로 일관성을 높일 수 있습니다.

대안(diff):

-import com.dh.baro.core.StringListToLongListDeserializer
+import com.dh.baro.core.StringToLongDeserializer
@@
-    @JsonDeserialize(using = StringListToLongListDeserializer::class)
+    @JsonDeserialize(contentUsing = StringToLongDeserializer::class)

참고: 이 방식은 [ "1", 2, "3" ]처럼 숫자/문자열 혼합 입력도 단일 로직으로 수용 가능합니다(해당 deserializer가 숫자 토큰도 허용하도록 구현되어 있다는 가정).

src/main/kotlin/com/dh/baro/core/Cursor.kt (2)

6-7: 출력만이 아니라 입력 대칭성도 고려: @JsonDeserialize 추가 제안(선택)

Cursor가 요청 본문/캐시 역직렬화에 사용될 여지가 있다면, 문자열 ID 입력을 Long으로 강제하는 역직렬화도 대칭으로 제공하는 것이 안전합니다.

예시(diff):

 import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
@@
 class Cursor (
-    @JsonSerialize(using = LongToStringSerializer::class)
+    @JsonSerialize(using = LongToStringSerializer::class)
+    @JsonDeserialize(using = StringToLongDeserializer::class)
     val id: Long,
 )

5-8: 값 객체 특성 강화: data class 전환(선택)

동등성/해시코드/디스트럭처링 지원을 위해 data class로의 전환을 고려해볼 만합니다. 외부 API 시그니처에 영향이 거의 없습니다.

-class Cursor (
+data class Cursor (
src/main/kotlin/com/dh/baro/cart/application/CartItemBundle.kt (1)

6-9: 데이터 무결성 보강 + 패키지 일관성 제안

  • cartItem.productId와 product.id의 일치 여부를 런타임에 보증하면 디버깅 비용을 줄일 수 있습니다.
  • 다른 Bundle들이 application.dto 하위에 위치하는 패턴과 경로를 맞추는 것도 고려해 주세요.

적용 예시:

 data class CartItemBundle(
     val cartItem: CartItem,
     val product: Product,
 )
+{
+    init {
+        require(cartItem.productId == product.id) {
+            "CartItem.productId(${cartItem.productId}) != Product.id(${product.id})"
+        }
+    }
+}
src/main/kotlin/com/dh/baro/order/application/OrderFacade.kt (2)

29-34: 중복 productId 제거로 조회 비용 절감

동일 상품이 여러 번 주문되는 경우가 있어도, 존재 검증과 조회는 distinct()로 중복을 제거해도 기능적으로 동일합니다(수량 병합은 도메인 서비스에서 수행).

 fun placeOrder(userId: Long, request: OrderCreateRequest): Order {
     userService.checkUserExists(userId)
-    val productList = productQueryService.getProductsExists(request.orderItems.map { orderItem -> orderItem.productId })
+    val productIds = request.orderItems.map { it.productId }.distinct()
+    val productList = productQueryService.getProductsExists(productIds)
     val cmd = OrderCreateCommand.toCommand(userId, productList, request)
     val order = orderService.createOrder(cmd)
     return order
 }
 @Transactional
 fun placeOrderV2(userId: Long, request: OrderCreateRequest): Order {
     userService.checkUserExists(userId)
-    val productList = productQueryService.getProductsExists(
-        request.orderItems.map { orderItem -> orderItem.productId },
-    )
+    val productIds = request.orderItems.map { it.productId }.distinct()
+    val productList = productQueryService.getProductsExists(productIds)

Also applies to: 41-45


73-88: 재고 차감 이벤트는 커밋 이후 발행 권장

이벤트 리스너가 DB 상태를 읽는다면, @TransactionalEventListener(phase = AFTER_COMMIT) 사용을 전제로 커밋 이후에 처리되도록 구성하는 것을 권장합니다. 현재 메서드는 @transactional 내에서 publish하고 있으니, 리스너 측 설정만으로 정합성 개선이 가능합니다.

src/main/kotlin/com/dh/baro/look/presentation/dto/LookDto.kt (1)

7-12: 필드 명명 일관성(lookId vs id) 재검토

다른 DTO는 id 또는 orderId 등을 사용합니다. API 전반의 명명 규칙을 정해 일관성 확보를 고려해 주세요(대규모 변경이므로 추후 배치 권장).

src/main/kotlin/com/dh/baro/product/presentation/dto/CategoryCreateRequest.kt (1)

9-15: ID/이름 검증 강화 제안

  • id에는 양수 제약을 추가하면 잘못된 키를 사전에 차단할 수 있습니다.
  • name은 @SiZe 대신 @notblank를 사용하면 공백만 있는 값을 방지할 수 있습니다.
 data class CategoryCreateRequest(
     @JsonDeserialize(using = StringToLongDeserializer::class)
-    @field:NotNull
-    val id: Long,
+    @field:NotNull
+    @field:jakarta.validation.constraints.Positive
+    val id: Long,
 
-    @field:Size(min = 1, max = 50)
-    val name: String,
+    @field:jakarta.validation.constraints.NotBlank
+    @field:Size(min = 1, max = 50)
+    val name: String,
 )

참고: Kotlin non-null 타입이라 @NotNull은 런타임 검증/스키마 용도로만 남습니다(유지 여부는 팀 규칙에 따르세요).

src/main/kotlin/com/dh/baro/product/presentation/dto/ProductListItem.kt (1)

18-27: 네이밍 사소 제안: ofOrNull → fromOrNull

다른 DTO의 from(...) 컨벤션과 맞추면 검색/일관성 측면에서 이점이 있습니다. (선택)

src/main/kotlin/com/dh/baro/core/LongToStringSerializer.kt (1)

7-15: 대안 제안: 표준 ToStringSerializer 사용 고려(선택)

Jackson의 com.fasterxml.jackson.databind.ser.std.ToStringSerializer를 활용하면 커스텀 클래스를 줄일 수 있습니다. 대규모 교체는 아니므로 추후 리팩터 단계에서 검토해 주세요.

src/main/kotlin/com/dh/baro/cart/presentation/dto/CartResponse.kt (1)

19-21: 반올림 위치 검토(선택)

아이템 단계에서 소계 스케일을 확정하고 최종 합계에선 스케일 변경이 없도록 하는 것도 한 방법입니다(누적 반올림 오차를 더 명확히 관리).

src/main/kotlin/com/dh/baro/product/presentation/dto/ProductCreateRequest.kt (1)

14-17: storeId 유효성 강화(선택)

ID가 양수만 허용된다면 @field:Positive를 추가해 주세요.

     @JsonDeserialize(using = StringToLongDeserializer::class)
     @field:NotNull
+    @field:Positive
     @Schema(type = "string", example = "11")
     val storeId: Long,
src/main/kotlin/com/dh/baro/cart/presentation/CartController.kt (1)

40-41: 경로 변수 ID 유효성 검증(@positive) 추가 제안

음수/0 등의 비정상 ID를 사전에 차단하려면 @Positive를 붙이는 편이 안전합니다. 메서드 파라미터 제약을 활성화하려면 컨트롤러에 @Validated도 추가해 주세요.

적용 예(diff):

@@
-import org.springframework.web.bind.annotation.*
+import org.springframework.web.bind.annotation.*
+import jakarta.validation.constraints.Positive
+import org.springframework.validation.annotation.Validated
@@
-@CheckAuth(UserRole.BUYER)
+@CheckAuth(UserRole.BUYER)
+@Validated
 class CartController(
@@
-    override fun updateQuantity(
+    override fun updateQuantity(
         @CurrentUser userId: Long,
-        @PathVariable itemId: Long,
+        @PathVariable @Positive itemId: Long,
         @Valid @RequestBody request: UpdateQuantityRequest,
     ) = cartFacade.updateQuantity(userId, itemId, request.quantity)
@@
-    override fun removeItem(
+    override fun removeItem(
         @CurrentUser userId: Long,
-        @PathVariable itemId: Long,
+        @PathVariable @Positive itemId: Long,
     ) = cartFacade.removeItem(userId, itemId)

Also applies to: 48-49

src/main/kotlin/com/dh/baro/product/presentation/dto/ProductCreateResponse.kt (1)

8-9: Long → String 직렬화 적용 좋습니다. Kotlin use-site/스키마 힌트 소소 제안

Kotlin에서는 명시적 use-site를 써 주면(직렬화의 경우 보통 @get:) 도구 호환성이 좋아집니다. 또한 Swagger 문서에서 타입을 문자열로 노출하려면 @Schema(type = "string")를 추가해 두면 혼선을 줄일 수 있습니다.

예시(diff):

-import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import io.swagger.v3.oas.annotations.media.Schema
@@
-    @JsonSerialize(using = LongToStringSerializer::class)
+    @get:JsonSerialize(using = LongToStringSerializer::class)
+    @Schema(type = "string")
     val id: Long,
src/main/kotlin/com/dh/baro/order/presentation/dto/OrderCreateRequest.kt (1)

22-24: Kotlin use-site(target) 명시 및 검증 강화 제안

생성자 파라미터 기반 역직렬화를 확실히 적용하려면 @param:(+호환을 위해 @field:도) use-site를 권장합니다. 또한 ID에 @Positive를 추가하면 음수 ID를 차단할 수 있습니다. Swagger 문서엔 문자열 ID로 보이도록 @Schema(type = "string")를 권장합니다.

예시(diff):

-import com.fasterxml.jackson.databind.annotation.JsonDeserialize
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize
+import io.swagger.v3.oas.annotations.media.Schema
@@
-        @JsonDeserialize(using = StringToLongDeserializer::class)
-        @field:NotNull(message = "상품 ID를 입력해주세요.")
-        val productId: Long,
+        @param:JsonDeserialize(using = StringToLongDeserializer::class)
+        @field:JsonDeserialize(using = StringToLongDeserializer::class)
+        @field:NotNull(message = "상품 ID를 입력해주세요.")
+        @field:Positive(message = "상품 ID는 양수여야 합니다.")
+        @Schema(type = "string")
+        val productId: Long,
src/main/kotlin/com/dh/baro/cart/presentation/dto/CartItemResponse.kt (3)

28-38: 게터 대신 프로퍼티 직접 접근 및 중복 호출 제거.

Product가 불변 val이라면 프로퍼티 직접 접근이 선호됩니다(학습된 선호도 반영). 또한 price를 변수로 담아 중복 호출을 제거하세요.

         fun from(bundle: CartItemBundle): CartItemResponse {
             val cartItem = bundle.cartItem
             val product = bundle.product
+            val price = product.price
 
             return CartItemResponse(
                 itemId = cartItem.id,
                 productId = product.id,
-                productName = product.getName(),
-                productThumbnailUrl = product.getThumbnailUrl(),
-                price = product.getPrice(),
+                productName = product.name,
+                productThumbnailUrl = product.thumbnailUrl,
+                price = price,
                 quantity = cartItem.quantity,
-                subtotal = product.getPrice()
-                    .multiply(BigDecimal(cartItem.quantity))
+                subtotal = price.multiply(BigDecimal(cartItem.quantity))
                     .setScale(SCALE_NONE, RoundingMode.HALF_UP)
             )
         }

10-13: 응답 스키마 일관성(OpenAPI): ID는 문자열 직렬화이므로 스키마도 string으로 명시.

@JsonSerialize만으로는 스키마 타입이 integer로 남습니다. @Schema(type="string")를 추가해 문서/실제 응답을 일치시켜 주세요.

-import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import com.fasterxml.jackson.databind.annotation.JsonSerialize
+import io.swagger.v3.oas.annotations.media.Schema
@@
-    @JsonSerialize(using = LongToStringSerializer::class)
+    @JsonSerialize(using = LongToStringSerializer::class)
+    @Schema(type = "string", example = "10001")
     val itemId: Long,
-    @JsonSerialize(using = LongToStringSerializer::class)
+    @JsonSerialize(using = LongToStringSerializer::class)
+    @Schema(type = "string", example = "11")
     val productId: Long,

35-37: 반올림 책임 위치 재검토(도메인 vs. 프리젠터).

원화(0 스케일) 보장을 도메인/DB(precision/scale)에서 이미 하고 있다면 여기의 setScale(0, HALF_UP)은 중복일 수 있습니다. 제거하거나 도메인 계층으로 이동 검토를 권장합니다.

src/main/kotlin/com/dh/baro/order/presentation/swagger/OrderSwagger.kt (2)

59-83: 예시 JSON의 ID 타입을 문자열로 수정 필요.

응답 DTO는 @JsonSerialize(Long→String)이므로 예시도 문자열이어야 합니다.

-                        {
-                          "orderId": 1001,
+                        {
+                          "orderId": "1001",
@@
-                            {
-                              "productId": 11,
+                            {
+                              "productId": "11",
@@
-                            {
-                              "productId": 12,
+                            {
+                              "productId": "12",

176-195: 목록/커서 예시의 ID도 문자열로 통일.

-                            {
-                              "orderId": 1003,
+                            {
+                              "orderId": "1003",
@@
-                            {
-                              "orderId": 1002,
+                            {
+                              "orderId": "1002",
@@
-                          "nextCursor": { "id": 1002 }
+                          "nextCursor": { "id": "1002" }
src/main/kotlin/com/dh/baro/order/presentation/dto/OrderDetailResponse.kt (2)

11-13: OpenAPI 스키마 타입을 string으로 명시.

문서/응답 일치성을 위해 @Schema(type="string") 추가를 권장합니다.

-    @JsonSerialize(using = LongToStringSerializer::class)
+    @JsonSerialize(using = LongToStringSerializer::class)
+    @io.swagger.v3.oas.annotations.media.Schema(type = "string", example = "1001")
     val orderId: Long,

추가로 파일 상단에 다음 import를 권장합니다(프로젝트 스타일에 맞게 정리):

import io.swagger.v3.oas.annotations.media.Schema

21-23: 아이템의 productId도 스키마 string으로.

-        @JsonSerialize(using = LongToStringSerializer::class)
+        @JsonSerialize(using = LongToStringSerializer::class)
+        @io.swagger.v3.oas.annotations.media.Schema(type = "string", example = "11")
         val productId: Long,
src/main/kotlin/com/dh/baro/cart/domain/CartItem.kt (3)

17-21: userId/productId는 변경 불가 컬럼으로 고정하세요.

생성 후 변경될 이유가 없으므로 DB 차원에서 업데이트를 금지하면 무결성이 강화됩니다.

다음 패치를 제안합니다:

-    @Column(name = "user_id", nullable = false)
+    @Column(name = "user_id", nullable = false, updatable = false)
     val userId: Long,

-    @Column(name = "product_id", nullable = false)
+    @Column(name = "product_id", nullable = false, updatable = false)
     val productId: Long,

29-35: 수량 변경 메서드에 방어 로직 추가 권장.

다른 호출 경로에서 음수/0이 들어오면 도메인 불변식이 깨질 수 있습니다(요청 DTO에서 1 이상 검증하더라도).

-    fun addQuantity(quantity: Int) {
-        this.quantity += quantity
-    }
+    fun addQuantity(quantity: Int) {
+        require(quantity > 0) { "quantity must be > 0" }
+        this.quantity += quantity
+    }

-    fun changeQuantity(quantity: Int) {
-        this.quantity = quantity
-    }
+    fun changeQuantity(quantity: Int) {
+        require(quantity >= 1) { "quantity must be >= 1" }
+        this.quantity = quantity
+    }

38-45: 팩토리에서 최소 수량 보장.

도메인 객체 생성 시점에 하한(>=1) 보장을 추가하면 일관성이 높아집니다.

-        fun newCartItem(userId: Long, productId: Long, quantity: Int): CartItem {
-            return CartItem(
+        fun newCartItem(userId: Long, productId: Long, quantity: Int): CartItem {
+            require(quantity >= 1) { "quantity must be >= 1" }
+            return CartItem(
                 id = IdGenerator.generate(),
                 userId = userId,
                 productId = productId,
                 quantity = quantity,
             )
         }
src/main/kotlin/com/dh/baro/order/presentation/OrderController.kt (1)

55-63: size 파라미터에 범위 검증 추가 제안.

과도한 페이지 크기 요청 방지를 위해 최소/최대 제약을 권장합니다(예: 1~100).

-        @RequestParam(required = false) cursorId: Long?,
-        @RequestParam(defaultValue = "10") size: Int,
+        @RequestParam(required = false) cursorId: Long?,
+        @RequestParam(defaultValue = "10") @Min(1) @Max(100) size: Int,

추가 import:

import jakarta.validation.constraints.Max
import jakarta.validation.constraints.Min
src/main/kotlin/com/dh/baro/cart/presentation/dto/AddItemRequest.kt (1)

9-13: @NotNull 메시지가 실제로 노출되지 않을 수 있음.

Kotlin 비널러블(Long) 필드가 누락되면 바인딩 단계에서 예외가 나 Validation 메시지까지 도달하지 않을 수 있습니다. 커스텀 메시지를 보장하려면 val productId: Long? + @NotNull로 바꾸는 방식을 고려하세요(연쇄 수정 영향 검토 필요).

또한 StringToLongDeserializer가 숫자/문자열 양쪽 입력을 모두 수용하는지(공백/널 처리 포함) 확인 부탁드립니다.

커스텀 역직렬화 도입으로 컨트롤러 단 toLong 제거는 잘 되었습니다.

src/main/kotlin/com/dh/baro/cart/application/CartFacade.kt (1)

22-24: 상품 조회 시 중복 ID 제거로 쿼리 최적화.

같은 상품이 여러 카트아이템에 존재하면 불필요한 조회가 반복될 수 있습니다.

-        val productIds = cartItems.map { it.productId }
-        val products = productQueryService.getAllByIds(productIds).associateBy { it.id }
+        val productIds = cartItems.map { it.productId }.distinct()
+        val products = productQueryService.getAllByIds(productIds).associateBy { it.id }
src/main/kotlin/com/dh/baro/look/presentation/LookController.kt (1)

52-60: cursorId/size에 Bean Validation 추가 제안

잘 동작하나, 유효성 한계를 명시하면 방어적입니다.

  • cursorId: 양수만 허용 (@positive, null은 통과)
  • size: 최소/최대 한계 설정(@min, @max)

적용 예시(선택):

-        @RequestParam(required = false) cursorId: Long?,
-        @RequestParam(defaultValue = "10") size: Int,
+        @RequestParam(required = false) @jakarta.validation.constraints.Positive cursorId: Long?,
+        @RequestParam(defaultValue = "10")
+        @jakarta.validation.constraints.Min(1)
+        @jakarta.validation.constraints.Max(50)
+        size: Int,

또는 상단 import 추가 후 간단히 표기:

import jakarta.validation.constraints.Max
import jakarta.validation.constraints.Min
import jakarta.validation.constraints.Positive
src/main/kotlin/com/dh/baro/product/presentation/swagger/ProductSwagger.kt (1)

155-160: 최신 상품 API: Long 전환 OK + nextCursor 예시 타입 확인

파라미터 Long 전환은 적절합니다. nextCursor.id의 직렬화 형태(숫자 vs 문자열)가 실제 런타임과 동일한지 함께 점검해 주세요.
설정이 문자열 직렬화라면 아래처럼 조정:

-                          "nextCursor": { "id": 11 }
+                          "nextCursor": { "id": "11" }

검증 스크립트는 위 코멘트와 동일합니다.

src/main/kotlin/com/dh/baro/product/presentation/ProductController.kt (1)

38-41: size/cursor 유효성 검증 애너테이션 제안

런타임 방어 강화를 위해 컨트롤러 수준에서 경계값을 제한하는 것이 안전합니다.

적용 예시:

-        @RequestParam(required = false) cursorId: Long?,
-        @RequestParam(required = false) cursorLikes: Int?,
-        @RequestParam(defaultValue = DEFAULT_PAGE_SIZE) size: Int,
+        @RequestParam(required = false) @jakarta.validation.constraints.Positive cursorId: Long?,
+        @RequestParam(required = false) cursorLikes: Int?,
+        @RequestParam(defaultValue = DEFAULT_PAGE_SIZE)
+        @jakarta.validation.constraints.Min(1)
+        @jakarta.validation.constraints.Max(50)
+        size: Int,

필요 시 상단 import:

import jakarta.validation.constraints.Max
import jakarta.validation.constraints.Min
import jakarta.validation.constraints.Positive
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da934f6 and 438e728.

📒 Files selected for processing (38)
  • src/main/kotlin/com/dh/baro/cart/application/CartFacade.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/cart/application/CartItemBundle.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/cart/domain/CartItem.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/cart/domain/CartItemRepository.kt (0 hunks)
  • src/main/kotlin/com/dh/baro/cart/domain/CartService.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/cart/presentation/CartController.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/cart/presentation/dto/AddItemRequest.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/cart/presentation/dto/CartItemResponse.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/cart/presentation/dto/CartResponse.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/cart/presentation/swagger/CartSwagger.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/core/Cursor.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/core/LongToStringSerializer.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/core/StringListToLongListDeserializer.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/core/StringToLongDeserializer.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/identity/presentation/dto/UserProfileResponse.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/look/presentation/LookController.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/look/presentation/dto/LookCreateRequest.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/look/presentation/dto/LookCreateResponse.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/look/presentation/dto/LookDetailResponse.kt (4 hunks)
  • src/main/kotlin/com/dh/baro/look/presentation/dto/LookDto.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/look/presentation/swagger/LookSwagger.kt (4 hunks)
  • src/main/kotlin/com/dh/baro/order/application/OrderCreateCommand.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/order/application/OrderFacade.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/order/presentation/OrderController.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/order/presentation/dto/OrderCreateRequest.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/order/presentation/dto/OrderDetailResponse.kt (3 hunks)
  • src/main/kotlin/com/dh/baro/order/presentation/dto/OrderSummary.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/order/presentation/swagger/OrderSwagger.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/product/application/CategoryFacade.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/ProductController.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/dto/CategoryCreateRequest.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/dto/CategoryResponse.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/dto/PopularCursor.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/dto/ProductCreateRequest.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/dto/ProductCreateResponse.kt (1 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/dto/ProductDetail.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/dto/ProductListItem.kt (2 hunks)
  • src/main/kotlin/com/dh/baro/product/presentation/swagger/ProductSwagger.kt (3 hunks)
💤 Files with no reviewable changes (1)
  • src/main/kotlin/com/dh/baro/cart/domain/CartItemRepository.kt
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: codrin2
PR: S-BARO/server#17
File: src/main/kotlin/com/dh/baro/look/presentation/dto/LookDetailResponse.kt:38-59
Timestamp: 2025-08-12T13:52:29.449Z
Learning: codrin2 prefers direct field access for immutable id properties (val) rather than using getters, reasoning that immutable fields pose less encapsulation risk.
📚 Learning: 2025-08-01T08:00:47.771Z
Learnt from: codrin2
PR: S-BARO/server#8
File: src/main/kotlin/com/dh/baro/cart/domain/CartItem.kt:31-37
Timestamp: 2025-08-01T08:00:47.771Z
Learning: CartItem 엔티티에서 수량 관련 validation은 request DTO 레벨에서 처리하는 것을 선호함. AddItemRequest와 UpdateQuantityRequest에서 Min(1) 어노테이션을 사용하여 수량 검증을 수행.

Applied to files:

  • src/main/kotlin/com/dh/baro/cart/presentation/dto/AddItemRequest.kt
🧬 Code graph analysis (33)
src/main/kotlin/com/dh/baro/cart/application/CartItemBundle.kt (3)
src/main/kotlin/com/dh/baro/product/application/dto/ProductDetailBundle.kt (1)
  • product (6-9)
src/main/kotlin/com/dh/baro/product/application/dto/ProductSliceBundle.kt (1)
  • productSlice (7-10)
src/main/kotlin/com/dh/baro/look/application/dto/LookDetailBundle.kt (1)
  • look (7-12)
src/main/kotlin/com/dh/baro/cart/presentation/dto/AddItemRequest.kt (2)
src/main/kotlin/com/dh/baro/product/domain/InventoryItem.kt (1)
  • productId (3-6)
src/main/kotlin/com/dh/baro/cart/presentation/dto/UpdateQuantityRequest.kt (1)
  • value (5-8)
src/main/kotlin/com/dh/baro/core/LongToStringSerializer.kt (2)
src/main/kotlin/com/dh/baro/core/event/JacksonEventSerializer.kt (2)
  • objectMapper (7-19)
  • serialize (12-18)
src/main/kotlin/com/dh/baro/core/event/EventSerializer.kt (2)
  • serialize (3-5)
  • serialize (4-4)
src/main/kotlin/com/dh/baro/order/application/OrderFacade.kt (4)
src/main/kotlin/com/dh/baro/order/domain/OrderService.kt (1)
  • productRepository (10-57)
src/test/kotlin/com/dh/baro/order/domain/OrderServiceTest.kt (1)
  • cmd (131-147)
src/main/kotlin/com/dh/baro/order/domain/Order.kt (1)
  • name (10-65)
src/main/kotlin/com/dh/baro/order/domain/service/OrderServiceV2.kt (3)
  • orderRepository (10-34)
  • orderItem (19-29)
  • createOrderV2 (15-33)
src/main/kotlin/com/dh/baro/order/presentation/OrderController.kt (3)
src/test/kotlin/com/dh/baro/order/domain/OrderQueryServiceTest.kt (1)
  • classes (24-180)
src/main/kotlin/com/dh/baro/order/domain/OrderQueryService.kt (1)
  • readOnly (9-32)
src/main/kotlin/com/dh/baro/order/domain/OrderRepository.kt (1)
  • findByUserIdAndCursorId (10-27)
src/main/kotlin/com/dh/baro/order/application/OrderCreateCommand.kt (1)
src/main/kotlin/com/dh/baro/order/domain/OrderService.kt (3)
  • it (31-31)
  • productRepository (10-57)
  • _ (32-36)
src/main/kotlin/com/dh/baro/core/StringListToLongListDeserializer.kt (1)
src/main/kotlin/com/dh/baro/core/event/JacksonEventSerializer.kt (1)
  • objectMapper (7-19)
src/main/kotlin/com/dh/baro/order/presentation/dto/OrderSummary.kt (2)
src/main/kotlin/com/dh/baro/order/domain/Order.kt (2)
  • name (10-65)
  • getId (40-40)
src/main/kotlin/com/dh/baro/product/infra/event/InventoryDeductionRequestedEvent.kt (1)
  • orderId (5-9)
src/main/kotlin/com/dh/baro/product/application/CategoryFacade.kt (4)
src/main/kotlin/com/dh/baro/product/presentation/CategoryController.kt (2)
  • categoryFacade (13-26)
  • HttpStatus (19-25)
src/main/kotlin/com/dh/baro/product/presentation/swagger/CategorySwagger.kt (1)
  • summary (24-67)
src/test/kotlin/com/dh/baro/product/domain/CategoryServiceTest.kt (3)
  • categoryService (44-46)
  • classes (19-78)
  • categoryService (35-35)
src/main/kotlin/com/dh/baro/product/domain/service/CategoryService.kt (1)
  • readOnly (9-29)
src/main/kotlin/com/dh/baro/product/presentation/dto/CategoryResponse.kt (3)
src/main/kotlin/com/dh/baro/product/domain/repository/CategoryRepository.kt (1)
  • interface CategoryRepository : JpaRepository<Category, Long> (6-6)
src/main/kotlin/com/dh/baro/product/domain/Category.kt (1)
  • name (5-14)
src/main/kotlin/com/dh/baro/product/presentation/CategoryController.kt (1)
  • categoryFacade (13-26)
src/main/kotlin/com/dh/baro/core/StringToLongDeserializer.kt (1)
src/main/kotlin/com/dh/baro/core/event/JacksonEventSerializer.kt (2)
  • objectMapper (7-19)
  • serialize (12-18)
src/main/kotlin/com/dh/baro/product/presentation/dto/CategoryCreateRequest.kt (5)
src/main/kotlin/com/dh/baro/product/presentation/CategoryController.kt (1)
  • categoryFacade (13-26)
src/main/kotlin/com/dh/baro/product/domain/Category.kt (1)
  • name (5-14)
src/main/kotlin/com/dh/baro/product/presentation/swagger/CategorySwagger.kt (1)
  • summary (24-67)
src/test/kotlin/com/dh/baro/product/domain/CategoryServiceTest.kt (1)
  • classes (19-78)
src/main/kotlin/com/dh/baro/product/domain/repository/CategoryRepository.kt (1)
  • interface CategoryRepository : JpaRepository<Category, Long> (6-6)
src/main/kotlin/com/dh/baro/product/presentation/dto/ProductListItem.kt (2)
src/main/kotlin/com/dh/baro/product/domain/InventoryItem.kt (1)
  • productId (3-6)
src/main/kotlin/com/dh/baro/product/domain/Product.kt (2)
  • name (11-135)
  • getId (58-58)
src/main/kotlin/com/dh/baro/look/presentation/dto/LookCreateResponse.kt (1)
src/main/kotlin/com/dh/baro/look/domain/Look.kt (1)
  • name (8-125)
src/main/kotlin/com/dh/baro/cart/application/CartFacade.kt (1)
src/main/kotlin/com/dh/baro/cart/domain/CartItemRepository.kt (1)
  • attributePaths (6-16)
src/main/kotlin/com/dh/baro/product/presentation/dto/PopularCursor.kt (1)
src/test/kotlin/com/dh/baro/product/domain/ProductQueryServiceTest.kt (1)
  • cursorLikes (70-84)
src/main/kotlin/com/dh/baro/look/presentation/dto/LookDto.kt (1)
src/main/kotlin/com/dh/baro/look/domain/Look.kt (1)
  • name (8-125)
src/main/kotlin/com/dh/baro/product/presentation/dto/ProductCreateResponse.kt (1)
src/main/kotlin/com/dh/baro/product/domain/Product.kt (1)
  • name (11-135)
src/main/kotlin/com/dh/baro/product/presentation/dto/ProductCreateRequest.kt (3)
src/main/kotlin/com/dh/baro/product/application/ProductCreateCommand.kt (1)
  • name (5-15)
src/main/kotlin/com/dh/baro/product/domain/Product.kt (1)
  • name (11-135)
src/main/kotlin/com/dh/baro/product/application/ProductFacade.kt (1)
  • storeService (12-51)
src/main/kotlin/com/dh/baro/look/presentation/dto/LookCreateRequest.kt (5)
src/main/kotlin/com/dh/baro/look/domain/Look.kt (2)
  • addProducts (90-100)
  • getOrderedProductIds (59-63)
src/main/kotlin/com/dh/baro/look/application/dto/LookDetailBundle.kt (1)
  • look (7-12)
src/main/kotlin/com/dh/baro/look/application/LookCreateCommand.kt (1)
  • creatorId (3-10)
src/test/kotlin/com/dh/baro/look/domain/LookServiceTest.kt (1)
  • cmd (103-119)
src/main/kotlin/com/dh/baro/look/domain/LookProduct.kt (1)
  • name (6-42)
src/main/kotlin/com/dh/baro/order/presentation/dto/OrderCreateRequest.kt (3)
src/main/kotlin/com/dh/baro/product/domain/InventoryItem.kt (1)
  • productId (3-6)
src/main/kotlin/com/dh/baro/order/domain/OrderItem.kt (1)
  • name (8-59)
src/main/kotlin/com/dh/baro/product/infra/event/InventoryDeductionRequestedEvent.kt (1)
  • orderId (5-9)
src/main/kotlin/com/dh/baro/order/presentation/swagger/OrderSwagger.kt (3)
src/test/kotlin/com/dh/baro/order/domain/OrderQueryServiceTest.kt (1)
  • classes (24-180)
src/main/kotlin/com/dh/baro/order/domain/OrderQueryService.kt (1)
  • readOnly (9-32)
src/main/kotlin/com/dh/baro/order/domain/OrderRepository.kt (1)
  • findByUserIdAndCursorId (10-27)
src/main/kotlin/com/dh/baro/look/presentation/LookController.kt (2)
src/main/kotlin/com/dh/baro/look/domain/service/LookReactionService.kt (1)
  • lookRepository (10-48)
src/main/kotlin/com/dh/baro/look/application/LookReactionFacade.kt (1)
  • recordReaction (14-21)
src/main/kotlin/com/dh/baro/cart/domain/CartService.kt (1)
src/main/kotlin/com/dh/baro/cart/domain/CartItemRepository.kt (1)
  • attributePaths (6-16)
src/main/kotlin/com/dh/baro/core/Cursor.kt (2)
src/main/kotlin/com/dh/baro/core/BaseTimeEntity.kt (1)
  • name (10-34)
src/test/kotlin/com/dh/baro/product/domain/ProductQueryServiceTest.kt (1)
  • cursorId (116-129)
src/main/kotlin/com/dh/baro/product/presentation/dto/ProductDetail.kt (2)
src/main/kotlin/com/dh/baro/product/application/dto/ProductDetailBundle.kt (1)
  • product (6-9)
src/main/kotlin/com/dh/baro/product/domain/Product.kt (2)
  • name (11-135)
  • getId (58-58)
src/main/kotlin/com/dh/baro/look/presentation/swagger/LookSwagger.kt (3)
src/main/kotlin/com/dh/baro/look/application/LookReactionFacade.kt (1)
  • recordReaction (14-21)
src/main/kotlin/com/dh/baro/look/domain/service/LookReactionService.kt (1)
  • lookRepository (10-48)
src/main/kotlin/com/dh/baro/look/domain/LookReaction.kt (1)
  • name (7-43)
src/main/kotlin/com/dh/baro/cart/domain/CartItem.kt (1)
src/main/kotlin/com/dh/baro/cart/domain/CartItemRepository.kt (2)
  • attributePaths (6-16)
  • findByIdAndUserId (13-13)
src/main/kotlin/com/dh/baro/product/presentation/swagger/ProductSwagger.kt (1)
src/test/kotlin/com/dh/baro/product/domain/ProductQueryServiceTest.kt (1)
  • classes (24-197)
src/main/kotlin/com/dh/baro/product/presentation/ProductController.kt (1)
src/main/kotlin/com/dh/baro/product/application/ProductFacade.kt (1)
  • storeService (12-51)
src/main/kotlin/com/dh/baro/look/presentation/dto/LookDetailResponse.kt (4)
src/main/kotlin/com/dh/baro/look/application/dto/LookDetailBundle.kt (1)
  • look (7-12)
src/main/kotlin/com/dh/baro/look/application/LookFacade.kt (1)
  • getLookDetail (29-44)
src/main/kotlin/com/dh/baro/look/domain/Look.kt (1)
  • name (8-125)
src/main/kotlin/com/dh/baro/look/domain/LookProduct.kt (1)
  • name (6-42)
src/main/kotlin/com/dh/baro/identity/presentation/dto/UserProfileResponse.kt (4)
src/main/kotlin/com/dh/baro/identity/presentation/swagger/UserSwagger.kt (2)
  • summary (24-80)
  • name (16-81)
src/main/kotlin/com/dh/baro/identity/infra/KakaoUserInfoResponse.kt (2)
  • ignoreUnknown (18-21)
  • ignoreUnknown (6-22)
src/main/kotlin/com/dh/baro/identity/application/dto/AuthResult.kt (1)
  • userId (5-9)
src/main/kotlin/com/dh/baro/identity/domain/repository/UserRepository.kt (1)
  • interface UserRepository : JpaRepository<User, Long> (6-6)
src/main/kotlin/com/dh/baro/order/presentation/dto/OrderDetailResponse.kt (2)
src/main/kotlin/com/dh/baro/order/domain/Order.kt (1)
  • name (10-65)
src/main/kotlin/com/dh/baro/order/domain/OrderItem.kt (1)
  • name (8-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI-CD
🔇 Additional comments (39)
src/main/kotlin/com/dh/baro/look/presentation/dto/LookCreateRequest.kt (1)

26-33: toCommand 간소화는 적절합니다

필드 타입을 Long으로 고정하고 변환을 제거한 점 좋습니다. 호출부/서비스 단 로직 단순화에 도움이 됩니다.

src/main/kotlin/com/dh/baro/order/application/OrderFacade.kt (1)

31-31: toLong 제거 적절합니다

요청 DTO에서 Long을 직접 사용하도록 정리한 방향성 좋습니다. 호출부 일관성도 유지됐습니다.

Also applies to: 42-43

src/main/kotlin/com/dh/baro/product/presentation/dto/PopularCursor.kt (1)

3-9: ID Long→String 직렬화 적용 일관성 OK

프론트 호환성을 유지하면서 내부 Long 타입을 보존하는 전략이 명확합니다. 동일 패턴을 다른 DTO에도 지속 적용해 주세요.

src/main/kotlin/com/dh/baro/order/presentation/dto/OrderSummary.kt (1)

11-12: Order ID 직렬화 전환 LGTM

LongToStringSerializer 적용과 from 매핑이 일치합니다.

Also applies to: 20-20

src/main/kotlin/com/dh/baro/look/presentation/dto/LookDto.kt (1)

8-9: Look ID 직렬화 전환 LGTM (직접 필드 접근도 선호와 일치)

look.id 직접 접근은 과거 선호도와도 맞습니다.

Also applies to: 15-15

src/main/kotlin/com/dh/baro/product/application/CategoryFacade.kt (1)

14-14: toLong 제거로 단순화 LGTM

서비스 시그니처(Long)와 정확히 일치합니다.

src/main/kotlin/com/dh/baro/order/application/OrderCreateCommand.kt (1)

21-29: Long 키 매핑으로 정합성 회복 LGTM

productMap의 키(id: Long)와 요청 키 타입이 일치해 NPE/변환 비용이 제거되었습니다.

src/main/kotlin/com/dh/baro/product/presentation/dto/CategoryResponse.kt (1)

3-9: ID 직렬화 전략 전환 일관성 확보됨

Long → String 직렬화를 필드 단위로 적용했고, 매핑에서도 entity.id를 그대로 전달해 불필요한 변환이 사라졌습니다.

Also applies to: 15-15

src/main/kotlin/com/dh/baro/product/presentation/dto/ProductListItem.kt (1)

10-12: 불필요 변환 제거로 간결해졌습니다

product.id를 그대로 사용하고 직렬화만 문자열로 처리한 방향이 적절합니다.

Also applies to: 21-21

src/main/kotlin/com/dh/baro/identity/presentation/dto/UserProfileResponse.kt (1)

9-10: ID Long 직렬화 적용 적절

도메인-DTO 간 불필요 변환 제거, 직렬화 전략과 일관성 유지 모두 좋습니다.

Also applies to: 20-20

src/main/kotlin/com/dh/baro/product/presentation/dto/ProductDetail.kt (1)

10-11: 세부 응답의 ID 직렬화 전환 OK

직렬화 정책과 도메인 타입이 일치합니다.

Also applies to: 22-22

src/main/kotlin/com/dh/baro/core/LongToStringSerializer.kt (1)

7-15: 간단하고 안전한 구현입니다

널 처리 포함, 목적에 충분합니다.

src/main/kotlin/com/dh/baro/cart/presentation/dto/CartResponse.kt (1)

14-24: 합계 계산 로직 명확

fold로 합산 후 setScale(0, HALF_UP) 적용이 KRW(정수 가격) 모델과 부합합니다.

src/main/kotlin/com/dh/baro/cart/presentation/swagger/CartSwagger.kt (2)

121-126: PathVariable 타입 변경 LGTM

컨트롤러와의 시그니처 일치가 확인됩니다.


145-149: 삭제 API PathVariable 타입 변경 LGTM

일관성 확보되었습니다.

src/main/kotlin/com/dh/baro/product/presentation/dto/ProductCreateRequest.kt (3)

14-17: 입력 역직렬화 방향성 적절

문자열 기반 ID를 Long으로 안전 변환하는 설정과 커맨드 매핑 단순화가 좋습니다.

Also applies to: 32-35


1-51: toLong()/toString() 변환 호출 없음 확인
DTO/컨트롤러에서 수동 변환 호출이 모두 제거되었습니다.


14-17: NUMBER 토큰도 정상 지원됨 – 추가 구현 불필요
StringToLongDeserializerp.text.toLong() 호출 시 VALUE_NUMBER_INT 토큰일 때도 p.text가 숫자 문자열을 반환하므로 Long 변환이 정상 작동합니다.
StringListToLongListDeserializer는 내부적으로 StringDeserializer가 숫자 토큰을 문자열로 처리하여 it.toLong() 매핑 시에도 문제없습니다.

src/main/kotlin/com/dh/baro/cart/presentation/CartController.kt (2)

24-27: Cart 조회 응답 조립 단순화(LGTM)

Facade에서 받은 번들을 그대로 DTO로 변환해 반환하는 흐름이 명확합니다.


40-42: updateQuantity 호출부-정의부 일치 확인됨
CartFacade의 updateQuantity(userId: Long, itemId: Long, qty: Int) 시그니처와 컨트롤러에서 request.quantity(Int)를 전달하는 호출부가 일치하므로 추가 변경 불필요합니다.

src/main/kotlin/com/dh/baro/product/presentation/dto/ProductCreateResponse.kt (1)

15-16: toString() 제거 후 Long 그대로 매핑(LGTM)

도메인 val id: Long을 그대로 사용해 직렬화 계층에 위임한 점이 일관되고 안전합니다.

src/main/kotlin/com/dh/baro/order/presentation/dto/OrderCreateRequest.kt (2)

21-25: 요청 DTO에서 productId를 Long으로 전환(LGTM)

중복 toLong() 제거와 검증 어노테이션 전환(@notblank@NotNull)이 의도에 맞습니다.


22-24: StringToLongDeserializer가 숫자 토큰도 지원합니다
JsonParser.getText()는 숫자(JSON number) 토큰일 때도 해당 숫자의 문자열 표현(예: "123")을 반환하므로 p.text.toLong()으로 변환이 가능합니다. 별도 추가 조치 불필요합니다.

src/main/kotlin/com/dh/baro/order/presentation/swagger/OrderSwagger.kt (2)

152-153: 타입 변경(Long) OK.

경로 변수의 Long 전환이 서비스/리포지토리 시그니처와 일치합니다.


203-205: 타입 변경(Long?) OK.

커서 파라미터가 Long?로 변경되어 리포지토리 쿼리(o.id < :cursorId)와 정합적입니다.

src/main/kotlin/com/dh/baro/order/presentation/dto/OrderDetailResponse.kt (1)

37-48: 정렬 기준 변경의 도메인 영향 확인.

items.sortedBy { productId }는 원래 순서(예: 추가 순서)와 다를 수 있습니다. 클라이언트 기대치가 “주문 생성 시 항목 순서”라면 영향이 있습니다. 요구사항 확인 부탁드립니다.

src/main/kotlin/com/dh/baro/look/presentation/dto/LookCreateResponse.kt (1)

8-9: Long → String 직렬화 전환 LGTM.

불필요한 toString() 제거와 불변 id 직접 접근(look.id)이 일관적입니다. 팀 선호도(immutable id direct access)와도 부합합니다.

Also applies to: 15-15

src/main/kotlin/com/dh/baro/cart/domain/CartService.kt (1)

13-16: @entitygraph(attributePaths = ["product"]) 어노테이션이 더 이상 존재하지 않습니다.
CartItemRepository에서 해당 어노테이션이 발견되지 않아, product 연관 제거에 따른 부팅 오류 우려가 해소되었습니다.

src/main/kotlin/com/dh/baro/order/presentation/OrderController.kt (1)

45-49: Long 바인딩 전환 LGTM.

toLong() 제거로 컨트롤러 단 단순화 및 오버헤드 제거가 잘 되었습니다.

src/main/kotlin/com/dh/baro/cart/application/CartFacade.kt (1)

33-41: 존재성 검증 추가 LGTM.

user/product 존재 체크를 파사드에서 선제 수행해 도메인 단 예외를 줄인 점 좋습니다.

src/main/kotlin/com/dh/baro/look/presentation/dto/LookDetailResponse.kt (1)

9-11: ID Long→String 직렬화, 필드 매핑 일치 LGTM.

orderedProductIds 기반 정렬 보존, 누락 엔티티 mapNotNull 처리도 합리적입니다.

Also applies to: 20-21, 41-41, 50-50

src/main/kotlin/com/dh/baro/look/presentation/swagger/LookSwagger.kt (1)

94-96: 경로/쿼리 파라미터 Long 전환 LGTM.

스펙과 컨트롤러/DTO 타입이 일치합니다.

Also applies to: 116-117, 163-165, 206-206

src/main/kotlin/com/dh/baro/look/presentation/LookController.kt (2)

33-39: Long 기반 PathVariable 및 파사드 전달 일관성 확보 OK

String → Long 전환이 컨트롤러·파사드 호출까지 잘 반영되었습니다.


45-46: 취소(reaction) API도 Long 전환 반영 OK

시그니처와 위임 호출 모두 일관적입니다.

src/main/kotlin/com/dh/baro/product/presentation/swagger/ProductSwagger.kt (1)

74-74: 상품 상세 PathVariable Long 전환 OK

스키마가 int64로 문서화될 것이며 런타임 타입과 일치합니다.

src/main/kotlin/com/dh/baro/product/presentation/ProductController.kt (4)

30-33: 상품 상세: Long 기반 전달로 단순화 OK

toLong() 제거되어 흐름이 간결하고 파사드 시그니처와 일치합니다.


38-53: 인기 상품 목록: 커서 XOR 검증 유지·일관성 OK

  • (cursorId == null) xor (cursorLikes == null) 검증으로 API 계약을 명확히 보장합니다.
  • ID는 val 접근(it.id), likes는 getter 사용으로 도메인 의도를 잘 반영합니다.

59-70: 최신 상품 목록: Long 전환 및 커서 구성 OK

  • categoryId/cursorId Long? 파라미터 전환 적절
  • Cursor(it.id)로 nextCursor 추출 일관성 확보

43-44: IllegalArgumentException이 400 Bad Request로 매핑됨 확인
GlobalExceptionHandler.kt에서 IllegalArgumentException이 @ResponseStatus(HttpStatus.BAD_REQUEST)로 처리되고 있음을 확인했습니다.

Comment on lines +27 to 31
val currentItemCnt = cartItemRepository.countByUserId(userId)
if (!cartPolicy.canAddMoreItems(currentItemCnt)) {
throw IllegalStateException(ErrorMessage.CART_ITEM_LIMIT_EXCEEDED.message)
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

장바구니 최대 개수 정책, 동시성에 취약.

count→insert 사이 창(window)로 초과 삽입이 가능합니다. 사용자별 아이템 행을 비관적 락으로 선점하거나(예: findByUserId with PESSIMISTIC_WRITE) DB 측에서 보장(별도 집계/트리거, 제한 컬럼)하는 방식을 검토하세요.

원치 않으면 최소한 limit 체크를 "신규 생성 시도 직후" 재검증하는 방어 로직을 추가하세요.

🤖 Prompt for AI Agents
In src/main/kotlin/com/dh/baro/cart/domain/CartService.kt around lines 27-31,
the current count→insert flow is racy and allows exceeding the cart item limit
under concurrency; update the logic to either (a) obtain a pessimistic write
lock on the user's cart row(s) (e.g., use repository.findByUserId(...,
PESSIMISTIC_WRITE)) and re-check the count under that lock before inserting, or
(b) enforce the limit in the DB (unique/aggregate constraint, trigger or a
dedicated counter column updated transactionally) so the DB rejects excess
inserts; if you choose not to change locking/DB, add a defensive re-validation
immediately after attempting to persist the new item and rollback/throw if the
limit is exceeded so no over-limit state is committed.

@S-BARO S-BARO deleted a comment from coderabbitai bot Sep 10, 2025
@S-BARO S-BARO deleted a comment from coderabbitai bot Sep 10, 2025
@S-BARO S-BARO deleted a comment from coderabbitai bot Sep 10, 2025
@S-BARO S-BARO deleted a comment from coderabbitai bot Sep 10, 2025
@S-BARO S-BARO deleted a comment from coderabbitai bot Sep 10, 2025
@S-BARO S-BARO deleted a comment from coderabbitai bot Sep 10, 2025
@S-BARO S-BARO deleted a comment from coderabbitai bot Sep 10, 2025
@S-BARO S-BARO deleted a comment from coderabbitai bot Sep 10, 2025
@S-BARO S-BARO deleted a comment from coderabbitai bot Sep 10, 2025
@S-BARO S-BARO deleted a comment from coderabbitai bot Sep 10, 2025
@codrin2 codrin2 merged commit 04766ad into develop Sep 10, 2025
1 of 2 checks passed
@codrin2 codrin2 deleted the refactor/#35 branch September 10, 2025 07:37
@coderabbitai coderabbitai bot mentioned this pull request Sep 12, 2025
5 tasks
@coderabbitai coderabbitai bot mentioned this pull request Sep 25, 2025
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] 장바구니 조회 기능 개선

2 participants