[React] 상태 관리 | 전역상태 관리, Props Drilling, cmarket-hooks

Eunji Lee·2022년 12월 27일
0

[TIL] Front-end

목록 보기
24/36
post-thumbnail

전역상태 관리

상태(state)

  • 상태(state): 동적으로 표현되는 데이터

로컬 상태

  • 특정 컴포넌트 안에서만 관리되는 상태
  • 보통 컴포넌트 내에서만 영향을 끼치는 상태
    • 예1. 다른 컴포넌트와 데이터를 공유하지 않는 폼(form) 데이터의 대부분
    • 예2. input box, select box 등과 같이 입력값을 받는 경우

전역 상태

  • 프로덕트 전체 혹은 여러 가지 컴포넌트가 동시에 관리하는 상태
  • 다른 컴포넌트와 상태를 공유하고 영향을 끼치는 상태
  • 서로 다른 컴포넌트가 동일한 상태를 필요로 할 때 전역 상태로 관리할 수 있음
    • 예. 다크모드, 국제화 설정(언어 바꾸기)

신뢰할 수 있는 단일 출처 원칙

  • 데이터 무결성을 위해, 동일한 데이터는 항상 같은 곳에서 데이터를 가지고 온다.
  • 서로 다른 컴포넌트가 동일한 상태를 다룬다면, 이 출처는 오직 한 곳이어야 함
    • 만일 사본이 있을 경우, 두 데이터는 서로 동기화(sync)하는 과정이 필요한데, 이는 코드 유지 및 보수를 어렵게 만듦

상태관리 라이브러리

  • 많이 쓰이는 라이브러리로는 React Context, Redux, MobX가 있음
  • 애플리케이션 개발 시 반드시 필요하진 않지만 사용하면 편리함
  • 장점
    • 전역 상태를 위한 저장소 제공
    • props drilling 이슈 해결
    • 상태의 출처 파악하기 쉬움 → 코드 유지 보수에 용이



Props Drilling

의미

  • 상위 컴포넌트의 state를 props를 통해 하위 컴포넌트로 전달할 때 props를 전달하는 용도로만 쓰이는 컴포넌트들을 거치면서 데이터를 전달하는 현상

문제점

규모가 커지고 구조가 복잡해지면 문제 발생

  • 코드의 가독성이 매우 나빠짐
  • 코드의 유지 및 보수가 어려움
  • state 변경시 Props 전달 과정에서 불필요하게 관여된 컴포넌트들 또한 리렌더링이 발생 → 웹성능에 악영향을 미침

해결방법

  1. 컴포넌트와 관련있는 state는 될 수 있으면 가까이 유지
  2. 상태관리 라이브러리 사용하기



cmarket-hooks

컴포넌트 분석

App.js

  • 최상위 컴포넌트
  • 상태
    • items(상태) & setItems(상태 관리 함수): 상품에 대한 정보가 담긴 배열과 그 배열의 상태를 관리하는 함수
    • cartItems & setCartItems(상태 관리 함수)를 가짐: 장바구니에 담긴 상품에 대한 정보가 담긴 배열과 그 배열의 상태를 관리하는 함수

ItemListContainer

  • 상품을 담은 페이지
  • 주요 메서드
    • handleClick: 장바구니에 상품을 추가하는 역할 → 구현 필요
  • 하위 컴포넌트
    • Item 컴포넌트: 각각의 상품 정보를 렌더링하는 컴포넌트

ShoppingCart

  • 장바구니에 담긴 상품을 보여주는 페이지
  • 주요 메서드
    • handleCheckChange: 각각 상품의 체크 상태를 변경하는 역할
      → 이미 구현됨
    • handleAllCheck : 전체 상품의 체크 상태를 변경하는 역할
      → 이미 구현됨
    • handleQuantityChange: 장바구니에 담긴 상품의 수량을 변경하는 역할
      구현 필요
    • handleDelete: 각각의 상품을 삭제하는 역할
      구현 필요
  • 하위 컴포넌트
    • CartItem: 장바구니에 담긴 각각의 상품 정보가 렌더링하는 컴포넌트
    • OrderSummer: 장바구니에 있는 상품의 총 개수와 총 가격을 렌더링하는 컴포넌트

Props로 넘길 state 파악하기

코드 작성

1. App.js에 Props로 state 넘겨주기

  • Nav 컴포넌트
    • cartItems 넘기기
      → 장바구니의 상품 개수의 변동이 생길 때마다, 상단 내비게이션 바에 상품 개수가 업데이트되도록 구현
  • ItemListContainer 컴포넌트
    • cartItems 넘기기
      → handleClick 메서드 구현 시 장바구니에 담을 상품이 이미 장바구니에 있는지 없는지 확인하기 위해
    • setCartItems 넘기기
      → handleClick 메서드 구현 시 장바구니에 담을 상품을 업데이트하기 위해
  • ShoppingCart 컴포넌트
    • setCartItems 넘기기
      → 장바구니에 있는 상품 변경 및 수량 변경 위해
return (
    <Router>
      <Nav cartItems= {cartItems}/>
      <Routes>
        <Route path="/" element={<ItemListContainer items={items} cartItems={cartItems} setCartItems={setCartItems} />} />
        <Route
          path="/shoppingcart"
          element={<ShoppingCart cartItems={cartItems} items={items} setCartItems={setCartItems}/>}
        />
      </Routes>
      <img
        id="logo_foot"
        src={`${process.env.PUBLIC_URL}/codestates-logo.png`}
        alt="logo_foot"
      />
    </Router>
  );

2. Nav 컴포넌트 수정

  • 전달한 Props인 cartItems 추가하기
  • 장바구니의 상품 개수는 cartItems 배열의 길이와 같음
function Nav({ cartItems }) {

  return (
    <div id="nav-body">
      <span id="title">
        <img id="logo" src="../logo.png" alt="logo" />
        <span id="name">CMarket</span>
      </span>
      <div id="menu">
        <Link to="/">상품리스트</Link>
        <Link to="/shoppingcart">
          장바구니<span id="nav-item-counter">{cartItems.length}</span>
        </Link>
      </div>
    </div>
  );
}

3. ItemListContainer 컴포넌트 수정

  • 전달받은 Props인 cartItems, setCartItems 추가하기
  • handleClick 메서드 수정
    • 이미 장바구니에 있는 상품을 선택한 경우 수량만 하나 추가
    • 장바구니에 없는 물건을 선택한 경우 아이템과 수량을 각각 하나씩 추가
function ItemListContainer({ items, cartItems, setCartItems }) {
  const handleClick = (e, id) => {
    let existedItem = cartItems.slice();
    let doesHaveItem = cartItems.findIndex(el => el.itemId === id);
    if(doesHaveItem === -1) {
      setCartItems([...existedItem, {"itemId": id, "quantity": 1}]);
    }else {
      existedItem[doesHaveItem].quantity++;
      setCartItems(existedItem);
    }
  }
  
  return (
    <div id="item-list-container">
      <div id="item-list-body">
        <div id="item-list-title">쓸모없는 선물 모음</div>
        {items.map((item, idx) => <Item item={item} key={idx} handleClick={handleClick} />)}
      </div>
    </div>
  );
}

4. ShoppingCart 컴포넌트 수정

  • 전달 받은 Props인 setCartItems 추가하기
export default function ShoppingCart({ items, cartItems, setCartItems }) {
  // 생략
}
  • handleQuantityChange 메서드 수정하기
  const handleQuantityChange = (quantity, itemId) => {
    setCartItems(cartItems.map(el => {
      if(el.itemId === itemId) {
        return {
          "itemId" : itemId,
          "quantity" : quantity
        }
      }else {
        return el
      }
    }));
  }
  • handleDelete 메서드 수정하기
  const handleDelete = (itemId) => {
    setCheckedItems(checkedItems.filter((el) => el !== itemId));
    setCartItems(cartItems.filter(el => el.itemId !== itemId));
  }

0개의 댓글