2 분 소요

장바구니에 담고 빼기

브라우저 저장소(웹 스토리지)를 활용해서 비회원 장바구니와 오늘 본 상품 리스트를 만들 수 있다.

비회원은 아직 로그인 하기 전이므로, 백엔드에서 어떤 유저가 장바구니에 무엇을 담고 있는지 기록하기가 조금 애매해다. 따라서 쿠키보다는 로컬 스토리지에 임시로 저장해 놓고, 나중에 로그인에 성공했을 때 미리 담아 놓은 장바구니를 쉽게 가져올 수 있도록 하는 것이 좋다.

또한, 브라우저를 껐다가 다시 들어올 수도 있으므로 세션 스토리지보다는 로컬 스토리지가 더 알맞다.

import { useQuery, gql } from "@apollo/client";
import styled from "@emotion/styled";
import { IBoard } from "../../src/commons/types/generated/types";

const FETCH_BOARDS = gql`
  query fetchBoards {
    fetchBoards {
      _id
      writer
      title
      contents
    }
  }
`;

const Row = styled.div`
  display: flex;
  flex-direction: row;
`;

const Column = styled.div`
  width: 25%;
`;

export default function BasketPage() {
  const { data } = useQuery(FETCH_BOARDS);

  const onClickBasket = (el: IBoard) => () => {
    console.log(el);

    // 지난번까지 담았던 장바구니 (1. 기존 장바구니 가져오기)
    const baskets = JSON.parse(localStorage.getItem("baskets") || "[]");

    // 중복으로 담기지 않게 (2. 이미 담겼는지 확인하기)
    // baskets를 순회하면서 꺼낸 요소의 id랑 방금 클릭한 요소의 id랑 비교
    const temp = baskets.filter((basketEl: IBoard) => basketEl._id === el._id);
    // 같은 게 있으면 temp의 길이는 1이 됨
    if (temp.length === 1) {
      alert("이미 담으신 상품입니다!");
      // 아래로 내려가지 않도록 함수 종료해주기
      return;
    }
    // ----> 혹은, 장바구니에서 빼주는 기능으로 구현할 수도 있음
    // alert 대신 실행될 내용:
    // const newBaskets = baskets.filter((basketEl: IBoard) => basketEl._id !== el._id);
    // localStorage.setItem("baskets", JSON.stringify(newBaskets));

    // (3. 장바구니에 담기)
    // delete el.__typename // 원본을 건드리는 건 비추
    // 이름이 꼭 rest일 필요 없다. newEl로 사용해도 됨.
    const { __typename, ...newEl } = el;
    baskets.push(newEl);
    localStorage.setItem("baskets", JSON.stringify(baskets));
  };

  return (
    <>
      {data?.fetchBoards.map((el: IBoard) => (
        <Row key={el._id}>
          <Column>{el.writer}</Column>
          <Column>{el.title}</Column>
          <button onClick={onClickBasket(el)}>장바구니 담기</button>
        </Row>
      ))}
    </>
  );
}
  • object로 받게 되는 value를 JSON.stringify로 감싸주어야 문자열로 담기는 것을 주의하자!

  • 담아주는 정보에서 불필요한 부분들은 REST 파라미터를 사용해 제외해주면 된다.

  • 클릭할 때마다 우리가 담는 상품들은 계속 장바구니에 쌓여야하는데 새로 갱신되어 장바구니에는 항상 1개만 담기게 된다. 이 문제는 JSON.parse()를 사용해 LocalStorage의 basket을 객체로 만들어 변수에 담아주고 이 변수에 push를 통해 새로운 상품들을 담아주는 형태로 만듦으로써 해결한다.

  • 장바구니에 중복 상품이 들어가면 1) alert로 중복되었음을 알려주거나, 2) 좋아요/취소처럼 장바구니에서 빼주는 방법으로 구현해주는 두 가지 방법이 있다. 장바구니를 돌면서 filter를 통해 방금 클릭된 물품과 장바구니에 있는 물품의 id가 같다면 temp 배열에 담아준다. temp의 길이가 1이라면 중복이라는 의미이므로 그때 중복 클릭에 대한 처리를 해주면 된다.


장바구니 내역

import styled from "@emotion/styled";
import { useEffect, useState } from "react";
import { IBoard } from "../../src/commons/types/generated/types";

const Row = styled.div`
  display: flex;
  flex-direction: row;
`;

const Column = styled.div`
  width: 25%;
`;

export default function BasketPage() {
  const [basketItems, setBasketItems] = useState([]);

  useEffect(() => {
    const baskets = JSON.parse(localStorage.getItem("baskets") || "[]");
    setBasketItems(baskets);
  }, []);

  return (
    <>
      {basketItems.map((el: IBoard) => (
        <Row key={el._id}>
          <Column>{el.writer}</Column>
          <Column>{el.title}</Column>
        </Row>
      ))}
    </>
  );
}

댓글남기기