Skip to content

Conversation

@TaeyeonRoyce
Copy link
Member

@TaeyeonRoyce TaeyeonRoyce commented May 17, 2024

📌 관련 이슈

📁 작업 설명

하단 화면에 필요한 데이터를 응답하는 API를 개발했습니다.
주문테이블이 따로 존재하지 않는 데이터 구조에서, 주문을 기준으로 페이징 조회를 위해 2번의 쿼리가 작성 되었습니다.
관련하여 코멘트 첨부하겠습니다! #133 (comment)


페이징을 위해 id 기반으로 처리하는데 문제가 있었습니다.
현재 주문 테이블은 아래와 같은 구조인데요.

주문id 주문번호 상품이름 ...
1 OrderA ProductA1 ...
2 OrderA ProductA2 ...
3 OrderB ProductB1 ...
4 OrderC ProductC1 ...
5 OrderC ProductC2 ...
6 OrderD ProductD1 ...

주문번호를 기준으로 페이징이 되다 보니 lastViewedId 이외로 lastViewOrderNumber도 추가적으로 요청을 받게했습니다.


위 데이터를 기준으로 [2개의 주문 조회] 를 하는 경우 아래와 같은 데이터가 응답됩니다.

주문id 주문번호 상품이름 ...
6 OrderD ProductD1 ...
5 OrderC ProductC2 ...
4 OrderC ProductC1 ...

이때, 마지막 주문번호(OrderC)에 해당하는 주문Id는 4, 5가 모두 존재합니다.
다음 페이지를 분명하게 조회하기 위해 lastViewedIdlastViewOrderNumber를 모두 전달받도록 구성하였습니다.
최초 조회시 lastViewedId = -1(기존 방식), lastViewOrderNumber = "EMPTY" 의 값을 활용하였습니다.

📸 작업화면(선택)

Screenshot 2024-05-17 at 11 58 28

Json 응답 예시

{
    "orders": [
        {
            "orderNumber": "202402211607020ORDERNUMBER",
            "orderedAt": "2024-05-17T11:56:19.44041",
            "orderProducts": [
                {
                    "orderId": 6,
                    "orderStatus": "ORDER_CREATED",
                    "productId": 0,
                    "storeId": 0,
                    "storeName": "storeName",
                    "thumbnail": "image.url",
                    "productName": "C1",
                    "quantity": 1,
                    "sex": "FEMALE",
                    "orderPrice": 1,
                    "price": 1,
                    "deliveryFee": 3000,
                    "deliveryMethod": "SAFETY"
                }
            ],
            "totalAmount": 3001
        },
        {
            "orderNumber": "202302211607020ORDERNUMBER",
            "orderedAt": "2024-05-17T11:56:19.440209",
            "orderProducts": [
                {
                    "orderId": 5,
                    "orderStatus": "ORDER_CREATED",
                    "productId": 0,
                    "storeId": 0,
                    "storeName": "storeName",
                    "thumbnail": "image.url",
                    "productName": "B2",
                    "quantity": 1,
                    "sex": "FEMALE",
                    "orderPrice": 1,
                    "price": 1,
                    "deliveryFee": 3000,
                    "deliveryMethod": "SAFETY"
                },
                {
                    "orderId": 4,
                    "orderStatus": "ORDER_CREATED",
                    "productId": 0,
                    "storeId": 0,
                    "storeName": "storeName",
                    "thumbnail": "image.url",
                    "productName": "B1",
                    "quantity": 1,
                    "sex": "FEMALE",
                    "orderPrice": 1,
                    "price": 1,
                    "deliveryFee": 3000,
                    "deliveryMethod": "SAFETY"
                }
            ],
            "totalAmount": 3001
        }
    ],
    "hasNextPage": true
}

기타

@github-actions
Copy link

github-actions bot commented May 17, 2024

Test Results

 65 files  + 1   65 suites  +1   27s ⏱️ +2s
312 tests + 4  312 ✅ + 4  0 💤 ±0  0 ❌ ±0 
445 runs  +13  445 ✅ +13  0 💤 ±0  0 ❌ ±0 

Results for commit d672574. ± Comparison against base commit 92681b6.

♻️ This comment has been updated with latest results.

Comment on lines +22 to +37
val latestOrderNumbers = findLatestOrderNumbers(memberId, orderPaging)
if (latestOrderNumbers.isEmpty()) {
return emptyList()
}

val query = jpql {
select(
entity(Order::class),
).from(
entity(Order::class),
).where(
path(Order::orderNumber)(OrderNumber::value).`in`(latestOrderNumbers),
).orderBy(
path(Order::id).desc()
)
}
Copy link
Member Author

Choose a reason for hiding this comment

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

주문을 기준으로 페이징을 하기 위해 서브쿼리가 필요했습니다.

  1. 조회 요청에 맞는 OrderNumber 조회
  2. OrderNumber에 해당하는 Order 조회

Kotlin-jdsl을 통해 1번에 해당하는 subquery를 작성하였는데요.
.asSubquery()를 통해 메인 쿼리에 추가하는 식으로 작성할 수 있습니다.

하지만 Kotlin-jdsl은 쿼리 자체에서 limit를 제공하지 않고, JPQL로 렌더될 때 setMaxResult()를 통해 조회 개수를 설정합니다.
결국 subquery에서 개수 제한을 사용하지 못해서 쿼리를 분리하여 사용했습니다. (방법이 있다면 알려주세요!)

JPQL을 직접 작성하려 했지만, 조회 조건이 동적으로 변하는 쿼리를 담아내기엔 어려움이 있어 2번의 쿼리로 구성했어요..!
리뷰하실때 참고하시면 좋을 것 같습니다

Copy link
Contributor

Choose a reason for hiding this comment

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

오... 생각을 못했는데 주문 조회에서 고려할 게 많았네요
저는 지금 방식 좋아요!!👍
더 공부하고 좋은 방식이 있다면 다시 리뷰 남기겠습니다!!🙇‍♀️

Copy link
Contributor

@hgo641 hgo641 left a comment

Choose a reason for hiding this comment

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

주문 번호를 기준으로 조회해와야해서 생각한 것보다 더 복잡한 로직이었네요...!
그런데도 코드가 깔끔하게 잘 구현되어 있어서 이해하기 쉬웠습니다! 고생하셨습니다!!

Comment on lines +318 to +324
private fun adjustInitialReadOrderNumber(lastViewedOrderNumber: String) =
if (lastViewedOrderNumber == INITIAL_READ_ORDER_NUMBER) {
null
} else {
lastViewedOrderNumber
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

그냥 궁금한건데 request에서 바로 null로 안받고 "EMPTY"로 받는 이유는 혹시 모르는 예외를 막기 위해서일까요?.?

Copy link
Member Author

Choose a reason for hiding this comment

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

상품이나 봉달목록에서 사용하는 커서기반 페이징 요청과 일관성 있게 작성했습니다!
lastViewedOrderNumber 자체가 요청에 포함되지 않는 경우보단, EMPTY같이 명시적으로 표현되는게 낫다고 생각했어요.
다른 요청들은 Id를 기반으로 하다보니 -1값을 사용하더라고요!

@GetMapping
fun readAll(
@Auth loginMember: LoginMember,
@ModelAttribute request: OrderReadRequest,
Copy link
Contributor

Choose a reason for hiding this comment

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

@ModelAttribute 로 받는 이유가 궁금합니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

필요하진 않네요!
제거해두겠습니다!

Comment on lines +22 to +37
val latestOrderNumbers = findLatestOrderNumbers(memberId, orderPaging)
if (latestOrderNumbers.isEmpty()) {
return emptyList()
}

val query = jpql {
select(
entity(Order::class),
).from(
entity(Order::class),
).where(
path(Order::orderNumber)(OrderNumber::value).`in`(latestOrderNumbers),
).orderBy(
path(Order::id).desc()
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

오... 생각을 못했는데 주문 조회에서 고려할 게 많았네요
저는 지금 방식 좋아요!!👍
더 공부하고 좋은 방식이 있다면 다시 리뷰 남기겠습니다!!🙇‍♀️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feature: 주문 내역(입양 내역)을 조회 API

3 participants