장바구니 기능 - Part 1 장바구니 추가
ㆍProject Diary/React + Firebase (Snack ShoppingMall)
상품을 장바구니에 추가하는 기능 구현 방법 기록
Point
- Firebase 데이터베이스 연동 : 사용자의 장바구니 데이터를 Firebase에 저장 및 업데이트
- 리덕스 연동 : 장바구니 상태를 리덕스로 관리하여, 헤더와 장바구니 페이지의 동기화를 유지
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;
장바구니에 상품을 추가하고, 헤더와 동기화하는 데 필요한 기능을 수행합니다.