[Wikea] 장바구니 기능

sue·2021년 3월 24일
0

Wikea Project

목록 보기
4/6

장바구니 기능은 생각보다 디테일하게 신경 쓸 부분들이 많았다. 그중에서 수량을 선택하는 컴포넌트를 공통 컴포넌트로 만드는데 시간을 오래 쏟기도 했다. 완성하고 보면 간단해 보이는 코드도 백지 상태에서 만들 때는 눈앞이 하얘지는 순간들을 종종 만났다. 그래도 문제를 파악하고 팀원들과 의견을 나누면서 해결 방법을 찾았고, 기획했던 구현 사항들을 빼놓지 않고 만들 수 있었다.

구현 기능

  • 장바구니 버튼을 클릭해, 장바구니에 상품 추가 기능
  • 유저 별 장바구니 상품 리스트 데이터 출력 구현
  • 수량 선택 탭을 활용해 상품 수량 변경 및 변경된 수량, 총액 출력 구현
  • 장바구니에서 위시리스트로 상품 이동, 삭제 구현
  • 주문 페이지로 상품 정보 전송 기능

장바구니에 상품 추가

  • 목록에서 상품 위로 마우스를 호버하면 장바구니 버튼이 나타나고, 버튼을 클릭해 상품을 장바구니로 이동시킬 수 있다.

장바구니 리스트 출력

  • 유저의 장바구니에 담긴 상품 리스트를 출력한다.

상품 수량 변경

  • 수량 선택 탭을 이용해 상품의 수량을 변경하고, 변경된 수량과 총액을 확인할 수 있다.

장바구니에서 상품 위시리스트로 이동, 삭제

  • 장바구니의 상품을 위시리스트로 이동시키거나, 장바구니에서 삭제시킬 수 있다.

주문 페이지로 상품 정보 전송

  • 결제하기 버튼을 눌러 장바구니에 담긴 상품 정보를 가지고 주문 페이지로 넘어갈 수 있다.

구현 코드

CartPage.jsx

const getTotal = data =>
  data.reduce(
    (acc, v) => parseInt(v.quantity, 10) * parseInt(v.Product.slCost, 10) + acc,
    0
  ); 

export default function CartPage({ history }) {
  const userInfo = useCheckLogin(); // 유저 로그인 정보 확인 
  const {
    loadCartLoading,
    loadCartData: data,
    loadCartError,
  } = useSelector(state => state.user);

  const dispatch = useDispatch();
  const totalPrice = useMemo(() => data && getTotal(data), [data]); // 총액 계산
  
  useEffect(() => {
    if (userInfo) {
      dispatch(getCart(userInfo.email));
    }
  }, [dispatch]); // 

  if (loadCartLoading) return <Loading />;
  if (!data) return null;
  if (data.length === 0) {
    return <Error text="장바구니에 담긴 상품이 없습니다." />;
  }
  if (loadCartError) return <div>에러페이지</div>;
  return (
    <CartInner>
      <CartTopContainer>
        <h1>장바구니</h1>
        <ButtonBig onClick={() => history.push('/user/payment')}>
          결제하기
        </ButtonBig>
        <ul>
          {data &&
            data.map(item => (
              <CartProduct data={item} key={item.id} userInfo={userInfo} />
            ))}
        </ul>
      </CartTopContainer>
      <CartLoginPromoContainer>
        <div>
          <h2>상품 더 둘러보기</h2>
          <div>
            <Link to="/user/wish">위시리스트</Link>
            <span>에 저장한 상품을 한번 더 둘러보세요</span>
          </div>
        </div>
        <i>
          <RiHeartLine />
        </i>
      </CartLoginPromoContainer>

      <CartBottomContainer>
        <h3>주문 내역</h3>
        <div>
          <span>전체 서비스 비용</span>
          <span>
            이 금액에는 배송비가 포함되어 있지 않으며, 배송지에 따라 구매가
            불가할 수 있습니다
          </span>
        </div>
        <hr />
        <div>
          <b>총 주문금액</b>
          <span>{`${totalPrice.toLocaleString()}`}</span>
        </div>
        <ButtonBig onClick={() => history.push('/user/payment')}>
          결제하기
        </ButtonBig>
      </CartBottomContainer>
    </CartInner>
  );
}

CartPage에서는 먼저 useCheckLogin() 함수로 유저의 현재 로그인 상태를 확인한다. 유저가 로그인 상태인 경우, 유저 이메일 정보를 담아 getCart 함수를 디스패치해 해당 유저의 장바구니 상품 리스트를 불러온다.

  
export const getCart = email => async dispatch => {
  try {
    dispatch({ type: LOAD_CART_REQUEST });
    const response = await axios.get(`/api/userproduct/cart/${email}`);
    dispatch({
      type: LOAD_CART_SUCCESS,
      payload: response.data,
    });
  } catch (e) {
    console.error(e);
    dispatch({
      type: LOAD_CART_ERROR,
      payload: e.response.data,
    });
  }
};

CartProduct.jsx


export default function CartProduct({ data, userInfo }) {
  const imgIndex = data.Product ? data.Product.ProdImages[0] : null;
  const dispatch = useDispatch();
  const [quantity, setQuantity] = useState(data.quantity);

  const onChangeQuantity = e => { // 상품 수량 변경 
    setQuantity(e.target.value);
    const data1 = {
      cartId: data.id,
      quantity: parseInt(e.target.value, 10),
    };
    dispatch(changeCart(data1));
  };

  const onRemoveCart = () => { // 상품 삭제 
    dispatch(removeCart({ email: userInfo.email, productId: data.Product.id }));
  };

  const onAddWishRemoveCart = () => { // 상품 위시리스트에 추가 및 장바구니에서 삭제
    dispatch(
      addWish({ userEmail: userInfo.email, productId: data.Product.id })
    );
    dispatch(removeCart({ email: userInfo.email, productId: data.Product.id }));
  };

  return (
    <CartItem>
      <figure>
        {imgIndex && (
          <img
            srcSet={imgIndex.srcSet}
            size={imgIndex.sizes}
            src={imgIndex.src}
            alt={imgIndex.info}
          />
        )}
      </figure>
      <div>
        <article>
          <div>
            <h2>{data.Product.title}</h2>
            <span>{data.Product.summary}</span>
            <div>{data.Product.size}</div>
            <div>{`수량: ${data.quantity}`}</div>

            {data.Product.prCost !== data.Product.slCost && (
              <span>
                {`할인 전 가격: ${(
                  data.Product.prCost * data.quantity
                ).toLocaleString()}`}
              </span>
            )}
            <div>
              {`총액: ${(
                data.Product.slCost * data.quantity
              ).toLocaleString()}`}
            </div>
          </div>
          <div>
            <strong>{`${data.Product.slCost.toLocaleString()}`}</strong>
            <div>/&nbsp;</div>
          </div>
        </article>
        <div>
          <SelectNumber onChange={onChangeQuantity} value={quantity} />
          <button onClick={onRemoveCart}>삭제</button>
          <button onClick={onAddWishRemoveCart}>위시리스트 저장</button>
        </div>
      </div>
    </CartItem>
  );
}

onChagneQuantity 함수는 장바구니에 담긴 상품 수량을 변경해 주는 함수다.
setQuantity로 컴포넌트의 값이 변경될 때 수량의 값을 변경해주고, 변경된 수량을 다시 해당 장바구니 아이디와 함께 넘겨 실제 장바구니 수량을 변경하는 thunk 함수를 디스패치한다.

0개의 댓글