결제 섹션 구현

Project Diary/React + Firebase (Snack ShoppingMall)

Point

  1. 배송비와 총 주문 금액 계산
  2. 사용자 정보 괸리 및 변경
  3. 주문지 선택 및 초기화
  4. 다음 우편번호 api 연동
  5. 상품 재고 업데이트

 

1. 배송비와 총 주문 금액 계산

// 배송비 기본값 설정 및 총 주문 금액 초기화
const [deliveryFee, setDeliveryFee] = useState(700); // 기본 배송비를 700으로 설정
const [total, setTotal] = useState(0); // 총 주문 금액을 0으로 초기화
const [totalDeliveryPrice, setTotalDeliveryPrice] = useState(0); // 총 주문 금액 + 배송비를 0으로 초기화
  • useState 훅을 사용하여 배송비, 총 주문 금액, 총 주문 금액 + 배송비를 초기화합니다.
  • useEffect 훅을 사용하여 product가 변경될 때마다 총 주문 금액과 배송비를 계산합니다.
  • 총 가격이 50,000원 이상일 경우 배송비를 무료로 설정합니다.
useEffect(() => {
  const totalPrice = product.reduce(
    (acc, item) => acc + parseInt(item.product.price) * parseInt(item.qty),
    0
  );
  setTotal(totalPrice); // 총 주문 금액 설정

  if (totalPrice >= 50000) {
    setDeliveryFee(0); // 총 가격이 50000원 이상일 경우 배송비 무료
  } else {
    setDeliveryFee(700); // 그렇지 않으면 배송비 700원 설정
  }

  setTotalDeliveryPrice(totalPrice + deliveryFee); // 총 주문 금액 + 배송비 설정
}, [product, deliveryFee]); // product와 deliveryFee가 변경될 때마다 실행

 

 

2. 사용자 정보 관리 및 변경

const [userInfo, setUserInfo] = useState({
  userId: user.userId, // 사용자 ID
  userIrum: user.userIrum, // 사용자 이름
  handphone: user.handphone, // 사용자 휴대전화
  zipCode: user.zipCode, // 사용자 우편번호
  addr1: user.addr1, // 사용자 주소1
  addr2: user.addr2, // 사용자 주소2
});
  • useState 훅을 사용하여 사용자 정보를 초기화합니다.
  • handleChange 함수는 사용자 입력 변경을 감지하고 userInfo 상태를 업데이트합니다.
const handleChange = (e) => {
  const { value, name } = e.target; // 이벤트 대상의 이름과 값을 가져옴
  setUserInfo((userInfo) => ({ ...userInfo, [name]: value })); // 해당 이름에 맞는 값을 업데이트
};

 

 

3. 주문지 선택 및 초기화

const [placeType, setPlaceType] = useState("default"); // 주문지 선택 타입을 default로 설정

const placeTypeChange = (type) => {
  setPlaceType(type); // 주문지 선택 타입 변경
};
  • placeType 상태를 사용하여 기본 주소와 새 주소 입력을 관리합니다.
  • placeTypeChange 함수는 주문지 타입을 변경합니다.
const onReset = (type) => {
  if (type === "default") {
    setUserInfo({
      userId: user.userId, // 사용자 ID
      userIrum: user.userIrum, // 사용자 이름
      handphone: user.handphone, // 사용자 휴대전화
      zipCode: user.zipCode, // 사용자 우편번호
      addr1: user.addr1, // 사용자 주소1
      addr2: user.addr2, // 사용자 주소2
    });
  } else {
    setUserInfo({
      userId: "", // 사용자 ID 초기화
      userIrum: "", // 사용자 이름 초기화
      handphone: "", // 사용자 휴대전화 초기화
      zipCode: "", // 사용자 우편번호 초기화
      addr1: "", // 사용자 주소1 초기화
      addr2: "", // 사용자 주소2 초기화
    });
  }
};
  • onReset 함수는 기본 주소 또는 새 주소로 사용자 정보를 초기화합니다.

 

4. 다음 우편번호 API 연동

useEffect(() => {
  window.openDaumPostcode = () => {
    new window.daum.Postcode({
      oncomplete: (data) => {
        let fullAddr = ""; // 최종 주소 변수
        let extraAddr = ""; // 조합형 주소 변수
        if (data.userSelectedType === "R") {
          fullAddr = data.roadAddress; // 도로명 주소
        } else {
          fullAddr = data.jibunAddress; // 지번 주소
        }
        if (data.userSelectedType === "R") {
          if (data.bname !== "") {
            extraAddr += data.bname; // 법정동명이 있을 경우 추가
          }
          if (data.buildingName !== "") {
            extraAddr += extraAddr !== "" ? ", " + data.buildingName : data.buildingName; // 건물명이 있을 경우 추가
          }
          fullAddr += extraAddr !== "" ? " (" + extraAddr + ")" : ""; // 조합형 주소를 최종 주소에 추가
        }
        setUserInfo((prevState) => ({
          ...prevState,
          zipCode: data.zonecode, // 우편번호 설정
          addr1: fullAddr, // 주소 설정
        }));
        mAddressSubRef.current.focus(); // 상세 주소 입력으로 포커스 이동
      },
    }).open();
  };
}, []);
  • useEffect 훅을 사용하여 다음 우편번호 API를 연동합니다.
  • 사용자가 주소를 선택하면 userInfo 상태를 업데이트합니다.

 

5. 모달 상태 및 네비게이션

const Modal = ({ modalOpen, onReset, product, qty }) => {
  const navigate = useNavigate(); // 네비게이션 훅 사용
  const user = useSelector((state) => state.members.user); // 리덕스에서 사용자 정보 가져오기

  const onBuy = () => {
    if (!user) {
      alert("로그인을 하십시오."); // 로그인하지 않은 경우 알림
      sessionStorage.setItem("previousUrl", "/payment"); // 이전 URL 저장
      sessionStorage.setItem(
        "choiceProduct",
        JSON.stringify({ product: [{ product, qty }] })
      );
      navigate("/login"); // 로그인 페이지로 이동
    } else {
      navigate("/payment", { state: { product: [{ product, qty }] } }); // 결제 페이지로 이동
    }
  };

  const onCart = () => {
    navigate("/cart"); // 장바구니 페이지로 이동
  };

  return (
    <ModalBlock className={modalOpen.open && "on"}>
      {modalOpen.what === "buy" ? (
        <div className="content">
          <h2>확인</h2>
          <p>바로구매를 진행하시겠습니까?</p>
          <div className="btn">
            <button onClick={onBuy}>확인</button>
            <button onClick={onReset}>취소</button>
          </div>
        </div>
      ) : (
        <div className="content">
          <h2>확인</h2>
          <p>
            상품이 장바구니에 담겼습니다.
            <br />
            바로 확인 하시겠습니까?
          </p>
          <div className="btn">
            <button onClick={onCart}>확인</button>
            <button onClick={onReset}>계속쇼핑</button>
          </div>
        </div>
      )}
    </ModalBlock>
  );
};
  • onBuy 함수는 사용자가 로그인하지 않았을 경우 로그인 페이지로 리다이렉트하고, 로그인했을 경우 결제 페이지로 이동합니다.

 

6. 상품 재고 업데이트

6-1 상품 재고를 업데이트하는 리덕스

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;
    },
    updateProductStock(state, action) {
      const { productId, quantity } = action.payload;
      const product = state.products.find(p => p.key === productId);
      if (product) {
        product.stock = product.stock - quantity;
      }
    },
  },
});

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


6-2 상품 재고 업데이트 함수

export const updateStock = (productId, quantity) => async (dispatch) => {
  try {
    const productRef = kuwazawa_productDB.child(productId);
    productRef.once('value', (snapshot) => {
      const product = snapshot.val();
      if (product && product.stock >= quantity) {
        productRef.update({ stock: product.stock - quantity });
        dispatch(updateProductStock({ productId, quantity }));
      } else {
        console.error("재고 부족");
      }
    });
  } catch (error) {
    console.error("Error updating stock:", error);
  }
};

 

6-3 결제 및 재고 업데이트 통합

const handlePurchase = (productId, quantity) => {
  // 결제 로직 수행
  dispatch(updateStock(productId, quantity));
};

// 구매 버튼 클릭 시 실행되는 함수
const onBuyClick = (productId, quantity) => {
  handlePurchase(productId, quantity);
};

 

이렇게 하면  결제 과정에서 상품 재고를 실시간으로 업데이트할 수 됩니다.

 


 

5만원 이상 무료배송

 

상품 상세페이지에서 바로 구매