관리 메뉴

거니의 velog

(48) 리액트 장바구니 구성 3 본문

SpringBoot_React 풀스택 프로젝트

(48) 리액트 장바구니 구성 3

Unlimited00 2024. 3. 11. 15:59

4. 상품 조회에서 장바구니 추가

* 장바구니에 존재하는 장바구니 아이템을 변경하거나 삭제하는 작업이 완료되었다면 이제 상품 조회 화면에서 장바구니에 상품을 추가하는 기능을 구현한다. 상품을 추가하면 장바구니 아이템을 생성하게 되는데 이때 서버에 전달하는 데이터는 '이메일, 상품번호, 수량'이다. 아래와 같은 상품 조회 화면에는 장바구니에 담기 버튼을 추가해 주어야 한다.


(1) 상품 조회 기능 수정

* 장바구니에 상품 추가 기능은 components/products/ReadComponent.js를 수정해야만 한다.

import React, { useEffect, useState } from "react";
import { getOne } from "../../api/productsApi";
import { API_SERVER_HOST } from "../../api/todoApi";
import useCustomMove from "../../hooks/useCustomMove";
import FetchingModal from "../common/FetchingModal";
import useCustomCart from "../../hooks/useCustomCart";
import useCustomLogin from "../../hooks/useCustomLogin";

const initState = {
  pno: 0,
  pname: "",
  pdesc: "",
  price: 0,
  uploadFileNames: [],
};

const host = API_SERVER_HOST;

const ReadComponent = ({ pno }) => {
  const [product, setProduct] = useState(initState);
  //화면 이동용 함수
  const { moveToList, moveToModify } = useCustomMove();
  //fetching
  const [fetching, setFetching] = useState(false);
  //장바구니 기능
  const { changeCart, cartItems } = useCustomCart();
  //로그인 정보
  const { loginState } = useCustomLogin();

  const handleClickAddCart = () => {
    let qty = 1;
    const addedItem = cartItems.filter((item) => item.pno === parseInt(pno))[0];

    if (addedItem) {
      if (
        window.confirm("이미 추가된 상품입니다. 추가하시겠습니까? ") === false
      ) {
        return;
      }
      qty = addedItem.qty + 1;
    }

    changeCart({ email: loginState.email, pno: pno, qty: qty });
  };

  useEffect(() => {
    setFetching(true);

    getOne(pno).then((data) => {
      setProduct(data);
      setFetching(false);
    });
  }, [pno]);

  return (
    <div className="border-2 border-sky-200 mt-10 m-2 p-4">
      (...)
      
      <div className="flex justify-end p-4">
        <button
          type="button"
          className="inline-block rounded p-4 m-2 text-xl w-32  text-white bg-green-500"
          onClick={handleClickAddCart}
        >
          Add Cart
        </button>
        <button
          type="button"
          className="inline-block rounded p-4 m-2 text-xl w-32  text-white bg-red-500"
          onClick={() => moveToModify(pno)}
        >
          Modify
        </button>
        <button
          type="button"
          className="rounded p-4 m-2 text-xl w-32 text-white bg-blue-500"
          onClick={moveToList}
        >
          List
        </button>
      </div>
    </div>
  );
};

export default ReadComponent;

* Add Cart 버튼의 이벤트 처리는 handelClickAddCart() 를 통해서 처리하고 내부에서는 추가된 적이 있는 상품인지 검사해서 경고창을 보여준다. 장바구니에 추가된 적이 없는 상품은 경고창 없이 장바구니에 바로 추가된다.

수량이 자동으로 잘 증가한다.


[고려해야 하는 점들]

* 장바구니의 모든 정보는 데이터베이스를 이용해서 보관되기 때문에 애플리케이션 내의 상태 데이터라고 인식하기보다는 컴포넌트 간의 공유 데이터라고 보는 것이 더 적합한 설명이 될 수 있다. 실제 장바구니 구현의 고민은 조금 다른 곳에 있는데, 상품 정보가 수정되거나 삭제되는 경우이다. 삭제의 경우 FK로 작성되기 때문에 서버에서 문제가 발생할 수 있지만, 상품의 이름이나 가격을 수정하는 경우는 조금 사정이 다르다.

* 예를 들어, '상품의 가격이 수정되었다면 현재 상품을 장바구니에 담은 모든 사용자는 장바구니 내에 존재하는 상품 가격이 실시간으로 변동되어야 할까?' 는 상당히 중요한 문제이다. 이에 대한 가장 완벽한 해결책은 상품 자체가 불변 데이터인 경우가 되겠지만, 이는 변경되지 않는 것을 상정하기 때문에 논외의 상황이 된다.

* 이에 대한 절충안은 약간의 시간(5분/10분)을 두고 자동으로 갱신되도록 하는 것이다. 최근 개발에서는 다양한 라이브러리를 이용해서 일정 시간 동안만 리액트에서 데이터를 캐싱하는 기능들을 구현할 수 있을 것이다.

* 만일 최대한 실시간에 가깝게 데이터를 처리해야 한다면 Firebase를 이용하는 것이 가장 현실적인 해결책이 될 것이다.