장바구니 기능 - Part 1 장바구니 추가

Project Diary/React + Firebase (Snack ShoppingMall)

상품을 장바구니에 추가하는 기능 구현 방법 기록


 

Point

  1. Firebase 데이터베이스 연동 : 사용자의 장바구니 데이터를 Firebase에 저장 및 업데이트
  2. 리덕스 연동 : 장바구니 상태를 리덕스로 관리하여, 헤더와 장바구니 페이지의 동기화를 유지

 

1. Firebase 초기화 및 설정

// firebase.js
import firebase from "firebase/compat/app";
import "firebase/compat/database";
import "firebase/compat/storage";

// Firebase 설정
const firebaseConfig = firebase.initializeApp({
  apiKey: "API_KEY",
  authDomain: "AUTH_DOMAIN",
  databaseURL: "DATABASE_URL",
  projectId: "PROJECT_ID",
  storageBucket: "STORAGE_BUCKET",
  messagingSenderId: "MESSAGING_SENDER_ID",
  appId: "APP_ID",
  measurementId: "MEASUREMENT_ID",
});

// 데이터베이스 참조 설정
const kuwazawaDB = firebaseConfig.database();
export const kuwazawa_productDB = kuwazawaDB.ref("kuwazawa_products");
export const kuwazawa_cartDB = kuwazawaDB.ref("kuwazawa_carts");
export const kuwazawa_memberDB = kuwazawaDB.ref("kuwazawa_members");
export const kuwazawa_noticeDB = kuwazawaDB.ref("kuwazawa_notice");
export const kuwazawa_reviewDB = kuwazawaDB.ref("kuwazawa_review");

// 스토리지 참조 설정
export const oStorage = firebaseConfig.storage();

 

 

2. 상품 섹션 컴포넌트 (OnlineShopsection)

 

2-1 장바구니에 상품 추가 함수 (addToCart 함수)

// 장바구니에 상품 추가 함수
const addToCart = async (id) => {
  if (user) { // 사용자가 로그인 되어 있는지 확인
    try {
      const cartItemRef = kuwazawa_cartDB.child(user.key).child(id); // 해당 유저의 레퍼런스 생성
      const cartItemSnapshot = await cartItemRef.once("value"); // 해당 유저의 스냅샷 가져오기
      let quantity = 1;
      if (cartItemSnapshot.exists()) {
        // 해당 유저가 이미 장바구니에 있는 경우 수량을 증가시킴
        quantity = cartItemSnapshot.val().qty + 1;
      }
      // 장바구니에 상품 추가 또는 업데이트
      await cartItemRef.set({ qty: quantity });
      dispatch(fetchCarts()); // 장바구니 데이터 업데이트
    } catch (error) {
      console.log("오류메시지:", error); // 오류 처리
    }
  } else {
    alert("로그인을 해주세요."); // 사용자가 로그인하지 않은 경우 알림
    sessionStorage.setItem("previousUrl", "/product");
    navigate("/login");
  }
};

 

2-2 장바구니에 추가된 수량 가져오기 (cartIdCount 함수)

이 함수는 현재 장바구니에 특정 상품이 얼마나 들어있는지 확인합니다.

// 장바구니에 추가된 수량 가져오기
const cartIdCount = (id) => {
  const userItem = carts.find((value) => value.key == id); // 장바구니에서 해당 상품을 찾음
  if (userItem) {
    return userItem.qty; // 상품이 장바구니에 있으면 수량 반환
  } else {
    return 0; // 상품이 장바구니에 없으면 0 반환
  }
};

 

2-3 유저 정보가 변경되었을 때 장바구니 데이터 가져오기 (useEffect 훅)

이 훅은 유저 정보가 변경될 때마다 장바구니 데이터를 업데이트합니다.

useEffect(() => {
  if (user) {
    dispatch(fetchCarts()); // 장바구니 데이터 가져오기
  }
}, [dispatch, user]);

 

 

 

3. 헤더와 장바구니 동기화

장바구니의 헤더는 장바구니에 담긴 상품의 개수를 실시간으로 표시해야 합니다.

 

3-1 Redux 액션을 통해 장바구니 데이터 가져오기

// 장바구니 데이터 가져오기 액션
export const fetchCarts = () => async (dispatch, getState) => {
  const user = getState().members.user;
  if (user) {
    try {
      const snapshot = await kuwazawa_cartDB.child(user.key).once('value'); // Firebase에서 장바구니 데이터 가져오기
      const cartsObj = snapshot.val();
      const cartsArr = cartsObj ? Object.entries(cartsObj).map(([key, value]) => ({ key, ...value })) : [];
      dispatch(initCarts(cartsArr)); // 장바구니 데이터를 리덕스 스토어에 업데이트
    } catch (error) {
      console.error('Error fetching carts:', error); // 오류 처리
    }
  }
};

 

3-2 헤더 컴포넌트에서 장바구니 항목 수 표시

헤더 컴포넌트는 장바구니에 담긴 상품의 개수를 표시합니다.

import React, { useEffect } from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";
import { BsCart4 } from "react-icons/bs";
import { useSelector, useDispatch } from "react-redux";
import { fetchCarts, initCarts } from "@/store/product";

// 스타일 생략

const LeftHeader = ({ isOpen, toggleMenu, handleCloseMenu }) => {
  const dispatch = useDispatch();
  const cartsCount = useSelector((state) => state.products.cartsCount); // 장바구니 항목 수 가져오기

  useEffect(() => {
    dispatch(fetchCarts()); // 장바구니 데이터 가져오기
  }, [dispatch]);

  return (
    <LeftHeaderBlock isOpen={isOpen}>
      <div className="nav">
        <div className="menuButton">
          <button onClick={toggleMenu}>
            {isOpen ? <IoCloseOutline /> : <CiMenuBurger />}
          </button>
          <div className="cart">
            <Link to="/cart" onClick={handleCloseMenu}>
              <BsCart4 />
              <span>{cartsCount}</span> {/* 장바구니 항목 수 표시 */}
            </Link>
          </div>
        </div>
      </div>
      {/* 메뉴가 열렸을 때 표시되는 메뉴 */}
      {isOpen && (
        <div className="menu">
          {/* 메뉴 항목들 */}
        </div>
      )}
    </LeftHeaderBlock>
  );
};

export default LeftHeader;

 

 

 

4. 리덕스 스토어 설정

리덕스 스토어 설정은 장바구니 데이터를 관리하는 데 필요합니다.

 

productSlice.js

import { createSlice } from "@reduxjs/toolkit";
import { kuwazawa_cartDB } from "@/assets/firebase";

const productSlice = createSlice({
  name: "products",
  initialState: {
    products: [],
    carts: [],
    cartsCount: 0,
  },
  reducers: {
    initProducts(state, action) {
      state.products = action.payload;
    },
    initCarts(state, action) {
      state.carts = action.payload;
      state.cartsCount = action.payload.length;
    },
  },
});

export const { initProducts, initCarts } = productSlice.actions;

export default productSlice.reducer;

장바구니에 상품을 추가하고, 헤더와 동기화하는 데 필요한 기능을 수행합니다.

 

 

상세페이지에서 장바구니 추가
상품리스트에서 장바구니 추가