Wecode 1차 프로젝트(React 장바구니 구현)

juno·2022년 8월 22일
0

React

목록 보기
2/5

wecode 1차 클론 프로젝트 React를 사용하여 구현한 장바구니 기능 풀이

핵심 기능 한 줄 설명

  • useEffect,fetch로 api로 부터 데이터를 받아와 useState로 저장

  • 상품 리스트별로 컴포넌트화하기

  • 상품데이터의 수량이 변화거나 삭제될때, 저장돤 데이터 업데이트

  • 결제하기 누를 시 업데이트 된 데이터 서버에 전송

코드

Cart

import { useEffect, useState } from 'react';
import Product from './Product/Product';
import './Cart.scss';

const Cart = () => {
  const [productData, setProductData] = useState([]);

  let totalSumPrice = 0;

  const deletedList = e =>
    setProductData(
      productData.filter(product => product.id !== Number(e.target.id))
    );

  const goToPay = () =>
    fetch('api주소', {
      method: 'POST',
      headers: {
        'content-Type': 'application/json',
      },
      body: JSON.stringify({
        productData: productData,
      }),
    });

  const productList = productData.map((product, idx) => {
    totalSumPrice += product.price * product.count;
    return (
      <Product
        key={idx}
        id={product.id}
        idx={idx}
        name={product.name}
        size={product.size}
        count={product.count}
        price={product.price}
        deletedList={deletedList}
        productData={productData}
        setProductData={setProductData}
      />
    );
  });

  useEffect(() => {
    fetch('data/data.json')
      .then(response => response.json())
      .then(setProductData);
  }, []);

  return (
    <div className="cart">
      {productList.length === 0 ? (
        <div className="empty">
          <div />
          <p>카트가 비어있습니다.</p>
        </div>
      ) : (
        <>
          <div className="naviLocation">
            <div className="titleArea">카트</div>
            <div className="sizeArea">사이즈</div>
            <div className="countArea">수량</div>
            <i className="fi fi-br-cross" />
          </div>
          {productList}
          <div className="cartUnder">
            <div className="underArea">
              <div className="notice">
                <span>전 제품 무료 배송 혜택을 즐겨보세요.</span>
              </div>
              <div className="price">
                <p>소계 (세금 포함)</p>
                <span>₩ {totalSumPrice.toLocaleString('ko-KR')}</span>
              </div>
              <div className="payment">
                <button onClick={goToPay}>결제하기</button>
              </div>
            </div>
          </div>
        </>
      )}
    </div>
  );
};

export default Cart;

product

import { useState } from 'react';
import './Product.scss';

const Product = ({
  key,
  id,
  idx,
  name,
  size,
  count,
  price,
  deletedList,
  setProductData,
  productData,
}) => {
  const sumPrice = price * count;

  const [showBtn, setShowBtn] = useState(false);
  const [showCount, setShowCount] = useState(true);

  const showDelete = () => setShowBtn(true);

  const hideDelte = () => setShowBtn(false);

  const showCountList = e => {
    setShowCount(!showCount);
  };

  const changeCount = e => {
    const newList = productData;
    newList[idx].count = Number(e.target.innerText);
    setProductData([...newList]);
    setShowCount(true);
  };

  return (
    <div className="productLocation" key={key}>
      <p className="titleArea">{name}</p>
      <p className="sizeArea productArea">{size}</p>
      <div
        className="countArea btnLocation"
        onMouseOver={showDelete}
        onMouseLeave={hideDelte}
      >
        {showCount ? (
          <>
            <button className="quantity" type="number" onClick={showCountList}>
              <p className="btnCount">{count}</p>
              <i className="fi fi-rr-angle-small-down" />
            </button>
            {showBtn && (
              <p className="deleteBtn" id={id} onClick={deletedList}>
                삭제
              </p>
            )}
          </>
        ) : (
          <ul className="countList" onClick={changeCount}>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
          </ul>
        )}
      </div>
      <p className="price">₩ {sumPrice.toLocaleString('ko-KR')}</p>
    </div>
  );
};

export default Product;

API로 가져온 데이터 저장

 const [productData, setProductData] = useState([]);
 
 
   useEffect(() => {
    fetch('API주소')
      .then(response => response.json())
      .then(setProductData);
  }, []);

 
 
 

풀이

  1. useEffect에 의존성 배열을 빈 배열로 하여 데이터는 처음 마운트시 한번만 데이터를 받아온다.
  2. fetch 메서드로 데이터를 받아온다.
  3. useState로 데이터를 저장한다.

상품 리스트별로 컴포넌트화 하기

데이터는 이러한 형태로 받아옵니다.

[
  {
    "id": 1,
    "name": "비 트리플 씨 페이셜 벨런싱 젤",
    "size": "60mL",
    "count": 2,
    "price": 580000
  },
  {
    "id": 2,
    "name": "휠 오드 퍼품",
    "size": "50mL",
    "count": 1,
    "price": 145000
  },
  {
    "id": 3,
    "name": "레퍼런스 아로마틱 핸드 밤",
    "size": "75mL",
    "count": 1,
    "price": 31000
  },
  {
    "id": 4,
    "name": "제라늄 리프 바디 스크럽",
    "size": "180mL",
    "count": 1,
    "price": 43000
  }
]

상품리스트를 컴포넌트화 하는 과정에서 'totalSumPrice'라는 총 가격을 구합니다.

 
 
 let totalSumPrice = 0;

  const productList = productData.map((product, idx) => {
    totalSumPrice += product.price * product.count;
    return (
      <Product
        key={idx}
        id={product.id}
        idx={idx}
        name={product.name}
        size={product.size}
        count={product.count}
        price={product.price}
        deletedList={deletedList}
        productData={productData}
        setProductData={setProductData}
      />
    );
  });

풀이

  1. product는 컴포넌트 입니다.
    • key 값은 반복되는 컴포넌트를 만들때, 컴포넌트를 구분하기 위한 필수 요소입니다.
    • id는 리스틑 삭제시 사용됩니다. (deletedList는 삭제작업을 구현한 함수입니다.)
    • idx는 useState로 저장한 데이터의 index값입니다.
      (productData,setProductData와 같이 수량 변동시 사용됩니다.)

상품 리스트 삭제 및, 수량 변동

1. 리스트 삭제

-- 최상위 컴포넌트

  const deletedList = e =>
    setProductData(
      productData.filter(product => product.id !== Number(e.target.id))
    );
    
    
-- Product컴포넌트 내부


        <p className="deleteBtn" id={id} onClick={deletedList}>
           삭제
        </p>

    
    

풀이

  1. props로 전달한 id는 삭제 버튼에 id속성으로 주고 이벤트 발생시,
    해당 id가 아닌 목록들로만 productData를 재 구성합니다.

2. 수량 변동


Product 컴포넌트 내부

  const changeCount = e => {
    const newList = productData;
    newList[idx].count = Number(e.target.innerText);
    setProductData([...newList]);
    setShowCount(true);
    
    
    
    
    
       <ul className="countList" onClick={changeCount}>
            <li>1</li>
            <li>2</li>
            <li>3</li>
            <li>4</li>
            <li>5</li>
       </ul>
  };
  
  

풀이

  1. 수량 변동 함수가 실행되면 일단 newList를 만들어 productData을 담은 새로운 배열을 선언하고,
    newList[idx].count = Number(e.target.innerText)
    이와 같이 특정 부분의 counte 값을 바꾸고 'setProductData([...newList])'로 훅을 일으켜 업데이트 해줍니다.

결제하기

  const goToPay = () =>
    fetch('api주소', {
      method: 'POST',
      headers: {
        'content-Type': 'application/json',
      },
      body: JSON.stringify({
        productData: productData,
      }),
    });
    
    
     <button onClick={goToPay}>결제하기</button>

풀이

  1. 업데이트된 productData는 결제하기 버튼을 누르면 서버에 전송됩니다.

마무리

해당 내용은 정답이 아닙니다.
그저 wecode 수강생분들이 '어떻게해야하지' 라는 생각이 들때,
참고 할 수 있도록 불친절하게 풀어 쓴 글이니 오류가 있을 수도 있습니다.
댓글로 남겨주시면 적극 반영 수정 하겠습니다.

profile
안녕하세요 인터랙션한 웹 개발을 지향하는 프론트엔드 개발자 입니다. https://kimjunho97.tistory.com => 블로그 이전 중

0개의 댓글