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 #53

Open
wants to merge 119 commits into
base: osdoonhyun
Choose a base branch
from

Conversation

osdoonhyun
Copy link

가이드 - 로컬 환경

# install
npm install

# run
npm run dev

🙂

2. 리뷰 요청 ✍️

안녕하세요, 태은님!
만나 뵙게 되어 반갑습니다.

마지막 미션인 만큼 더욱 최선을 다하겠습니다.
잘 부탁드리며, 많이 배우겠습니다.
너무 늦게 리뷰 요청드린 점 죄송합니다..!😅

2-1. 어렵거나 아쉬웠던 점

새로운 라이브러리(msw, Tanstack Router, Zustand)들을 이번 기회에 시도해 볼 수 있어서 좋았습니다.
어떤 문제를 해결하기 위함과 어떻게 하면 더욱 효율적으로 사용할 수 있을지에 대해 앞으로도 더욱 고민해 봐야겠습니다!
아직 남은 숙제(test관련 라이브러리)는 다음 step에서 꼭 도입해 보겠습니다!!

2-2. 도식화된 자료 혹은 작성된 이미지 형태의 설계 구조

📦src
  📂apis
   📂@common
   📂Cart
   📂Order
   📂Product
 
  📂assets
 
  📂components
   📂@common
   📂Cart
   📂Order
    📂@common
    📂Detail
    📂List
    📂Result
   📂Product
   
  📂constants
  📂css
  📂hooks
   📂common
   📂mutations
   📂queries
 
  📂layout
  📂mocks
  📂pages
   📂Cart
   📂Order
   📂Product
 
  📂routes
  📂store
   📜 - AlertDialogStore, CartStore
  📂stories
  📂types
  📂utils
  📜App.tsx
  📜main.tsx
  📜routeTree.gen.ts
  📜vite-env.d.ts

프로젝트 특성과 규모에 관계없이 Boilerplate 처럼 사용하고 있는 폴더 구조인데 저는 눈에 익고 손에 익숙해서 개발할 때 크게 불편함이 없었으나, 한 발 떨어져서 보니 굉장히 복잡해 보이고 중복도 많아 문제가 있어 보입니다..
태은님이 보시기엔 어떠한지, 프로젝트 규모에 따라 어떤 기준을 가지고 폴더 구조를 유연하게 가져갈 수 있을까요?

2-3. 중점적으로 리뷰받고 싶은 부분

이전 미션에서는 Atom Design System을 통해 설계 이후에 계층별로 조립하다 보니 의식적으로 비즈니스 로직과 UI를 분리할 수 있었는데 이번 미션에서는 top-down 방식으로 하니 컴포넌트를 나누는 부분에서 어려움이 있었습니다.
아래 개발하고 컴포넌트를 설계하는 과정에서의 생각을 적어보았습니다.

CartPage
장바구니 페이지를 크게 3개의 section으로 나눴습니다.

  • CartHeader Section
  • CartTable Section
  • CartPayments Section
// CartPage.tsx
return (
    <section className='cart-section'>
      <CartHeader />

      <div className='flex'>
        <section className='cart-left-section'>
          <CartTable/>
        </section>

        <section className='cart-right-section'>
          <CartPayments />
        </section>
      </div>
    </section>
  );
}
  1. CartTable과 CartPayments의 경우 'cart-right-section'와 같이 className을 통해 컴포넌트 내 위치를 알 수 있기 때문에 두 컴포넌트의 경우 section 태그는 포함하지 않았습니다.
  2. CartPayments 컴포넌트는 결제 금액에 대해 공통 로직이 다른 페이지(주문/상세, 주문 내역 상세)에서도 사용되었지만, 각 페이지의 뷰 요구 사항이 조금씩 다르기 때문에 성급한 추상화라 생각되어 일단은 중복 처리하였습니다.

CartTable 컴포넌트
CartTable 컴포넌트의 복잡도를 낮추기 위해 같은 관심사를 갖는 컴포넌트로 아래와 같이 나눴습니다

  • CartToolBar : 장바구니 상품 전체 선택/해제, 상품 삭제 기능
  • CartProductHeader : 장바구니 상단에 상품 수를 표시
  • CartProducts : 장바구니에 담긴 상품을 렌더링하고 상품 갯수 증가, 감소, 삭제 기능
// CartTable.tsx
return (
 <>
   <CartToolBar />
   <CartProductsHeader />
   <CartProducts cartProducts={cart} />
 </>
)

위와 같이 컴포넌트를 나누니 각 컴포넌트의 복잡도는 낮아지고 컴포넌트명을 통해 어떤 컴포넌트인지는 알겠으나, 내부에서 어떤 동작이 이뤄지는지 전혀 알 수 없다는 단점이 있었습니다.

CartTable과 하위 컴포넌트가 모두 서로 의존적이기에 위와 같이 컴포넌트들만 호출하는 것은 옳지 않다고 생각을 하였고, 부모 컴포넌트(CartTable)에서 action들을 정의하고 하위 컴포넌트(CartToolBar, CartProductsHeader, CartProducts)로 주입시키기로 하였습니다.

 return (
    <>
      <CartToolBar
        hasProducts={hasProducts}
        isSelectedAll={isSelectedAll}
        onSelectAllChange={handleSelectAllChange}
        onRemoveSelectedProducts={handleRemoveSelectedProducts}
      />
      <CartProductsHeader productCount={productCount} />
      <CartProducts
        hasProducts={hasProducts}
        cartProducts={cart}
        selectionProducts={selection}
        onToggleSelection={handleSelectChange}
        onIncreaseQuantity={handleIncreaseQuantity}
        onDecreaseQuantity={handleDecreaseQuantity}
        onRemoveProduct={handleRemoveProduct}
      />
    </>
  );
}

위와 같이 리팩토링을 하니 컴포넌트의 복잡도도 낮추고 props들을 통해 각 컴포넌트들에서 어떤 동작들이 이뤄지고 렌더링 될지 유추할 수 있게 되었습니다.

그러나 모든 action 함수들을 부모 컴포넌트에서 정의하다 보니 CartTable은 조금은 복잡하다는 아쉬움이 있는 거 같습니다.

아직도 컴포넌트를 어디까지 나누고, 어느 컴포넌트에서 이벤트를 정의하고, 어디서 값을 불러와야 하고, 어디에서는 값을 props로 전달 받아야 할지 명확한 기준이 모호한 것 같습니다.

리팩토링 과정 중 코드나 저의 생각에 대해 리뷰 받아보고 싶습니다!
위 작업을 위해 의식적으로 생각할 요소나 과정, 잘하기 위한 팁 등 태은님의 어떤 사고 흐름이 궁금합니다..!!

쿼리 캐싱 전략

평소 쿼리 캐싱 전략에 대해 생각해 보지 않았는데, 이번 미션을 통해 데이터 특성에 따라 캐싱 전략에 대해 생각해 보았습니다.

  1. 상품 정보

    데이터: products, productDetail

    • 판매자에 의해 변경 시 업데이트되지만, 상품 목록이 자주 변경되지 않으므로, staleTime을 설정할 수 있음.
      • staleTime: 5분
    • 사용자가 자주 접근하는 데이터이므로 캐시에 오래 유지되어야 함
      • gTcime: 30분
  2. 장바구니 정보

    데이터: cartProducts

    • 사용자에 의해 자주 변경되고, 실시간 업데이트해줘야 함
      • 항상 fresh한 상태 유지 → staleTime: 0
    • 장바구니 세션에만 유의미하다.
      • gcTime: 기본값
  3. 주문 정보

    데이터: orders, orderDetails

    • 사용자 주문 시 업데이트 됨, 주문 정보의 경우 확정된 이후에 변경되지 않음.
      • staleTime을 하루 정도로 둠.
    • 주문 정보의 경우 사용자가 주문 내역 확인 시 조회함으로 오래 유지되어야 함.
      • gcTime 또한 하루 정도

위 쿼리 캐싱 전략에 대해서도 피드백 받아 보고 싶습니다. 또한 refetch 옵션은 기본 값으로 두었는데, 이번 미션에서 유의미하게 사용될 곳이 있었는지도 궁금합니다.

3. 질문있어요 🙋

장바구니 관리 방식

전역상태 장바구니를 구현 시 zustand를 사용해 보고 싶어 성급하게(?) 도입하였습니다. 이후 mock API를 통해 데이터 처리를 하다 보니 장바구니에 상품 담기(post), 삭제하기(delete), 조회하기(get) 등 장바구니 상태를 서버에서도 관리하고 zustand의 store로도 장바구니의 상태(조회)와 액션(추가, 삭제, 상품 갯수 증감)을 처리하고 있습니다. 구현하고 보니 장바구니에 상품 담기, 삭제, 조회하기가 중복되는 문제를 발견하였습니다.

문제
설계 당시 상태 관리에 대해 명확히 하지 않고 구현하다 보니 위와 같이 하나의 상태(장바구니)를 서버와 클라이언트에서 관리하다 보니 예를 들어 장바구니 상태를 불러올 때 클라이언트에서 가져와야 할지 서버에서 가져와야 할지 혼동됩니다.

질문
장바구니의 경우 주로 클라이언트 단에서 처리하는 것으로 알고 있는데, 어떻게 처리해야 할지, 어떤 식으로 개선해 보면 좋을지 고민이 됩니다.

Tanstack Query

Tanstack Query에 대한 깊은 지식이 없어 어떻게 하면 더욱 효율적으로 사용할 수 있을지에 궁금합니다.

컨벤션

네이밍 컨벤션(폴더, 컴포넌트, state, props, event Handler, 상수, 함수 등)에 대해서도 고민해 보고 하나씩 정립해 가고 있는데, 태은님이 보시기엔 어떠한지 궁금합니다.
안 좋은 코드 습관 등에 대해서도 가감 없이 피드백 주시면 대단히 감사드립니다!

4. 남은 요구 사항 ✅

  • 반응형 레이아웃을 구현한다.
  • 낙관적 업데이트를 활용하여 UX/UI 증진
  • React Testing Library & Jest를 활용해 자유로운 단위의 테스트라도 진행
  • Storybook 상호 작용 테스트
  • 무한스크롤
  • 뒤로가기로 페이지 이동 시 마지막 스크롤 위치로 이동
  • 쿼리키 팩토리 리팩토링
  • 리액트 최적화 작업

쓰다보니 질문의 양이 많아졌네요.. 태은님 편하실 때에 보시고 천천히 피드백 주시면 감사하겠습니다!!

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