Skip to content
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

[클린코드 리액트 3기 유소정] 장바구니 미션 Step1 #52

Open
wants to merge 39 commits into
base: kaori-killer
Choose a base branch
from

Conversation

kaori-killer
Copy link

안녕하세요, 민경님!

상태 관리를 설계하는데 어려움을 겪어서 리뷰 요청이 늦었습니다.
할 수 있는 최대한 작성하였습니다.
장바구니 STEP1 잘 부탁드립니다!

🎯 기능 요구사항

필수 요구사항

  • TanStack Query를 기반으로 상태 분리
    • 낙관적 업데이트를 활용하여 UX/UI 증진
  • MSW를 활용한 API mocking
  • Endpoint만 변경하면 언제든 Real API를 바라볼 수 있다고 가정하고 상상한다.
    • Real API 없이 로컬에서만 동작하는 상태로 리뷰 받는 것이 기본 원칙
  • 상태 관리 라이브러리가 필요하다면 추가적으로 선택하고 적용한다.
    • 전략을 세우고 PR 본문에 내용을 작성한다.

GNB

  • 로고를 누르면 상품목록 페이지로 이동한다.
  • 장바구니 버튼을 누르면 장바구니 페이지로 이동한다.
  • 주문목록 버튼을 누르면 주문목록 페이지로 이동한다.

상품목록

  • 상품들은 n x 4 레이아웃으로 보여진다.
  • 상품들에는 사진, 이름, 금액이 보여진다.
  • 장바구니 버튼을 클릭하면 (**) / 자유롭게 구현 후 내용 작성

선택 요구사항 (심화)

상품상세

  • 페이지에는 상품 사진, 이름, 금액 정보가 보여진다.
  • 장바구니 버튼을 클릭하면 장바구니 페이지로 이동한다.
  • 장바구니 버튼을 클릭하면 해당 상품이 장바구니에 담긴다.

2. 리뷰 요청 ✍️

어렵거나 아쉬웠던 점

1. 장바구니 페이지에서 상태를 관리하는데 문제가 있습니다.

장바구니에 상품을 담고 -> 수량을 증감시키고 -> 상품을 선택하면 -> 결제예상금액이 정상적으로 보입니다.
하지만
장바구니에 상품을 담고 -> 상품을 선택하고 -> 수량을 증감시키면 -> 결제예상금액이 정상적으로 보이지 않습니다.

선택을 한 다음에, 수량을 증감시킬 때 결제예상금액을 변경하는 부분이 어려워서 구현하지 못했습니다.

장바구니 페이지에서 전체 상품을 관리는 context/OrderPayListProvider.tsx에서 하고 있습니다.

CartContent.tsx 장바구니에서 상품을 선택했을 때 결제예상금액을 변경하는 코드는 다음과 같습니다.
이 부분이 문제가 되는 것 같은데, 어떻게 개선해야 할지 감을 못 잡고 있습니다.

상태관리를 아예 잘못하고 있는 것 같기도 합니다.

  const handleToggleProductSelection: React.MouseEventHandler<
    HTMLInputElement
  > = (event) => {
    const { checked } = event.target;
    setIsChecked(!isChecked);

    if (checked) {
      addProductToOrderPayList(product, quantity);
      updateTotalPrice(product.price, quantity);
    }

    if (!checked) {
      deleteProductFromOrderPayList(product.id);
      updateTotalPrice(-product.price, quantity);
    }
  };

2. 장바구니에서 상품을 삭제했을 때, 삭제를 바로 반영하지 못합니다.

장바구니 리스트에서 상품을 삭제했을 때, 정상적으로 상품은 삭제됩니다.
하지만 이를 어떻게 바로 반영할 수 있는지 모르겠습니다.
상품을 삭제했을 때, 상품 리스트를 다시 불러오려고 해도 useCartListQuery를 조건문 안에 넣게 되어서 쉽지 않았습니다.

새로운 방법이 있으면 조언 부탁드립니다!

3. handler 함수 네이밍에 대한 어려움

특정한 페이지를 이동시키는 handler 함수는 handleClickOrder, handleClickCart 이런식으로 이름을 많이 지었습니다.

그런데 어떤 버튼을 눌렀을 때 동작하는 handler 함수의 네이밍은 짓기가 쉽지 않았습니다.
예를 들어, 장바구니에 상품을 담는 handler 함수 이름을 다음과 같이 지었는데요, 봤을 때 직관적으로 이해가 되는 이름으로 지으면 괜찮은지 궁금합니다.

  const handleAddProductToCart = () => {
    confirm("장바구니에 추가되었습니다.");
    mutate(product);
    navigate("/cart");
  };

4. react-query에 대한 조언

react-query를 처음 사용해봤습니다! React-Query 카카오페이 문서를 보고 참고해서 작성했는데, 수정할 부분이 있으면 조언 부탁드립니다!

리뷰해주셔서 감사합니다! ☺️

This reverts commit 813b6e0.
Copy link

@ddongule ddongule left a comment

Choose a reason for hiding this comment

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

안녕하세요 소정님!
먼저 장바구니 구현하시느라 고생하셨어요! 👍
장바구니가 생각보다 상태 관리할 부분도 많고, 계산해야 되는 부분도 많아서 복잡한 프로젝트에요.
아마 소정님도 요 부분에서 몇가지 어려움을 겪으셨던 것 같은데 같이 차근차근 수정해보아요! 🚀

전반적인 리뷰를 드리자면,
먼저 하나의 값은 하나의 상태로 관리되면 좋을 것 같아요.
지역 상태와 전역 상태가 혼재되면서 생기는 이슈가 몇가지 있어서,
이 상태는 어떤 값을 가리키는가? 에 대해 생각하시면서 코드를 만들어보시면 조금 감이 오실거에요.

그리고 API 호출시 가장 중요한 부분 중 하나는 에러를 처리하는 부분이에요.
API가 제대로 오지 않는 상황은 굉장히 다양하기 때문에 꼭 고려해주시는게 좋아요.
프로젝트 전반적으로 에러처리하는 부분이 빠져있어서, 상황을 가정해보고 어떻게 처리할지 한번 생각해보시면 좋을 것 같아요.

질문에 대해 답변드릴게요!

  1. 장바구니 페이지에서 상태를 관리하는데 문제가 있습니다.

현재 quantity가 두군데에서 관리되고 있어요. orderPayList 내에 quantity가 있는데, useProductForm에서 또 quantity 를 관리하고있어요. 그러면서 totalPrice도 useProductForm에서 계산하고있구요! useProductForm은 필요하지 않아 보입니다.
해당 hook을 지워보시고, OrderPayListContext 내에서 totalPrice 계산을 해보세요.

  1. 장바구니에서 상품을 삭제했을 때, 삭제를 바로 반영하지 못합니다.

흠 제가 프로젝트를 돌려봤을 때에는 장바구니 리스트에서 상품을 삭제했을 때, 정상적으로 상품이 삭제되지 않고 있어요! 🥲🥲
음 일단 생각해봤을 때에는 상품이 제대로 삭제되었을 때에 api response data로 업데이트 된 값을 반환받으면 해당 상태값을 업데이트하는 방법도 있을 것 같구요, useQuery의 refetch 함수를 사용해볼수도 있을 것 같아요.

  1. handler 함수 네이밍에 대한 어려움

저는 핸들러 함수는 동작에 관련된 이름으로 짓는 편이에요. 어떤 이벤트가 발생했을 때에는 보통 하나의 동작만 수행하는 것이 아니라 다양한 동작이 수행될 수 있기 때문에 handle~동작 이런식으로 짓는 편이에요.

예시로 주신 함수는 사실 AddProductToCart를 하는게아니라, 내용을 살펴보면

  1. 사용자에게 alert
  2. update api 호출
  3. cart 페이지로 이동
    이라는 세가지 동작을 하고있어요.

이런 경우 저는 handleShoppingCartClick 과 같은 네이밍으로 지을 것 같아요.

  1. react-query에 대한 조언

react query를 처음 사용해보셨군용! 👏👏
왜 react query를 사용하고자 하셨나요? 선택하신 이유가 궁금해용! 🦝
수정할 부분이 있다기보다는 react query를 어떻게 하면 더 효율적으로 사용할 수 있는지에 대해 고민해보시면 좋을 것 같아요. 어떤 점들을 제공하고, 그걸 활용하려면 어떻게 사용해야하는구나 의 flow로 한번 고민해보세요!

고생 많으셨어요! 👍👍
조금만 더 힘내보아요오이!!!! 화이팅!!!!

@@ -0,0 +1,30 @@
# React + TypeScript + Vite

Choose a reason for hiding this comment

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

readme 파일에는 프로젝트 내용을 간단하게 적어주시는게 좋아요! 🚗
관련 아티클들을 읽어보셔도 좋고,
오픈소스 readme등을 보면서 어떤 내용들이 들어가면 좋을지 보시는것도 많은 도움이 될거에요!

return (
<nav className="nav flex justify-around">
<div className="flex-center">
<h1 className="nav-title" onClick={handleClickHome}>

Choose a reason for hiding this comment

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

로고가 clickable하다면 cursor: pointer 속성을 주는것도 좋겠네요!


export default function Layout() {
return (
<div className="root">

Choose a reason for hiding this comment

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

현재 여기에는 아무 스타일 속성이 적용되어있지 않네요! 🎨
root layout에 전반적인 글로벌 레이아웃 스타일을 적용하면 더 좋을 것 같아요.
지금은 body에 width: 1920px 이 정해져있어서 화면에 스크롤이 생기고있어요!

Comment on lines 4 to 6
body {
width: 1920px;
}

Choose a reason for hiding this comment

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

앞서 말씀드렸듯 width를 정해두면 유연한 레이아웃을 만들기 어려우니 변경해보시는건 어떨까요? 🥹

Comment on lines 42 to 50
if (checked) {
addProductToOrderPayList(product, quantity);
updateTotalPrice(product.price, quantity);
}

if (!checked) {
deleteProductFromOrderPayList(product.id);
updateTotalPrice(-product.price, quantity);
}

Choose a reason for hiding this comment

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

early return 을 사용해도 되겠네요!

Suggested change
if (checked) {
addProductToOrderPayList(product, quantity);
updateTotalPrice(product.price, quantity);
}
if (!checked) {
deleteProductFromOrderPayList(product.id);
updateTotalPrice(-product.price, quantity);
}
if (checked) {
addProductToOrderPayList(product, quantity);
updateTotalPrice(product.price, quantity);
return
}
deleteProductFromOrderPayList(product.id);
updateTotalPrice(-product.price, quantity);


export const QUERY_KEY = "/products";

const BASE_URL = "http://localhost:3003";

Choose a reason for hiding this comment

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

여러 query에서 같은 BASE_URL을 사용하는 것 같은데 constant로 분리해보시는건 어떨까요?

Comment on lines +10 to +13
const fetcher = (id: number) =>
axios.get<ProductDetail>(`${BASE_URL}/products/${id}`).then(({ data }) => {
return data;
});

Choose a reason for hiding this comment

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

async/await을 활용해보셔도 좋겠어요!


import OrderPayListProvider from "./context/OrderPayListProvider";

const queryClient = new QueryClient();

Choose a reason for hiding this comment

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

queryClient options를 한번 살펴보셔도 좋을 것 같아요.
어떤 옵션들을 활용해볼 수 있을까요?
https://tanstack.com/query/latest/docs/reference/QueryClient

@@ -0,0 +1,3 @@
export default function priceFormat(value: number) {
return value.toLocaleString();

Choose a reason for hiding this comment

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

Suggested change
return value.toLocaleString();
return value.toLocaleString("ko-KR");

Comment on lines +4 to +8
const [quantity, setQuantity] = useState(1);

const getProductTotalPrice = (price: number) => {
return price * quantity;
};

Choose a reason for hiding this comment

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

OrderPayDetail type을 보면 detail 내부에 quantity가 있는데 여기에서 quantity가 또 관리되고 있는 것 같아요.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants