상품관리 - part4 상품 수정하기

Project Diary/React + Firebase (Snack ShoppingMall)

관리자가 기존에 등록된 상품 정보를 변경하는 방법 기록


 

1. 라우터 설정

먼저, 상품 수정 페이지로 이동할 수 있도록 라우터를 설정해야 합니다. react-router-dom을 사용하여 경로를 설정합니다.

import React from "react";
import { Route, Routes } from "react-router-dom";
import Layout from "@/Layout";
import ProductModifyView from "@/views/product/ProductModifyView";

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<Layout />}>
        {/* 다른 경로 생략 */}
        <Route path="/productModify" element={<ProductModifyView />} />
      </Route>
    </Routes>
  );
};

export default App;

 

왜 라우터가 중요한가?

라우터는 사용자가 특정 경로로 이동할 때 해당 경로에 맞는 컴포넌트를 렌더링합니다. 

상품 수정 페이지로 이동하기 위해서는 /productModify 경로를 설정하여 ProductModifyView 컴포넌트를 렌더링하도록 해야 합니다. 이를 통해 사용자가 상품 수정 페이지에 접근할 수 있습니다.

 

 

2. ProductModifyView 컴포넌트

ProductModifyView 컴포넌트는 상품 수정 페이지의 메인 컴포넌트입니다. 여기에서는 useLocation 훅을 사용하여 상품 정보를 전달받아 ProductModifySection 컴포넌트에 전달합니다.

import React from "react";
import ProductModifySection from "@/components/product/ProductModifySection";
import { useLocation } from "react-router-dom";

const ProductModifyView = () => {
  const location = useLocation();
  const { product } = location.state;
  return (
    <div>
      <ProductModifySection item={product} />
    </div>
  );
};

export default ProductModifyView;

 

 

3. ProductModifySection 컴포넌트

ProductModifySection 컴포넌트는 실제 상품 정보를 수정하는 폼을 제공합니다.

import React, { useState } from "react";
import styled from "styled-components";
import { kuwazawa_productDB, oStorage } from "@/assets/firebase";
import { useNavigate } from "react-router-dom";

const ProductModifyBlock = styled.div`
//스타일 생략
`;

const OnlineShopModify = ({ item }) => {
  const navigate = useNavigate();
  const { category, name, price, description, inventory, photo, detailPhotos } = item;
  const [product, setProduct] = useState({
    category,
    name,
    price,
    description,
    inventory,
    photo,
    detailPhotos: [],
  });

  const [photoValue, setPhotoValue] = useState("");

  const handleChange = (e) => {
    const { value, name } = e.target;
    setProduct((product) => ({ ...product, [name]: value }));
  };

  const handleFileChange = (e) => {
    const file = e.target.files[0];
    setProduct((prevProduct) => ({ ...prevProduct, photo: file }));
    setPhotoValue(e.target.value);
  };

  const handleDetailFileChange = (e) => {
    const files = e.target.files;
    setProduct((prevProduct) => ({
      ...prevProduct,
      detailPhotos: Array.from(files),
    }));
  };

  const onSubmit = async (e) => {
    e.preventDefault();
    try {
      const storageRef = oStorage.ref();
      if (product.photo) {
        const fileRef = storageRef.child(product.photo.name);
        await fileRef.put(product.photo);
        product.photo = await fileRef.getDownloadURL();
      }
      if (product.detailPhotos.length > 0) {
        const detailPhotoURLs = [];
        await Promise.all(
          product.detailPhotos.map(async (file, index) => {
            const fileName = `detailPhoto${index + 1}_${Date.now()}_${file.name}`;
            const detailFileRef = storageRef.child(fileName);
            await detailFileRef.put(file);
            detailPhotoURLs.push(await detailFileRef.getDownloadURL());
          })
        );
        product.detailPhotos = detailPhotoURLs;
      }
      await kuwazawa_productDB.child(item.key).update(product);
      navigate("/product");
    } catch (error) {
      console.log("오류 : ", error);
    }
  };

  return (
    <ProductModifyBlock>
      <h2>상품 수정</h2>
      <form onSubmit={onSubmit}>
        <div>
          <label htmlFor="category">카테고리:</label>
          //생략
        </div>
        <div>
          <label htmlFor="name">상품명:</label>
          //생략
        </div>
        <div>
          <label htmlFor="price">가격:</label>
          //생략
        </div>
        <div>
          <label htmlFor="description">요약설명:</label>
         //생략
        </div>
        <div>
          <label htmlFor="inventory">재고:</label>
          //생략
        </div>
        <div>
          <label htmlFor="photo">상품사진:</label>
         //생략
        </div>
        <div>
          <label htmlFor="detailPhoto">상세 상품사진:</label>
          //생략
        </div>
        <div className="btn">
          <button type="submit">등록</button>
        </div>
      </form>
    </ProductModifyBlock>
  );
};

export default OnlineShopModify;

 

 

주요 코드를 설명하겠습니다

 

상태관리
const [product, setProduct] = useState({
  category,
  name,
  price,
  description,
  inventory,
  photo,
  detailPhotos: [],
});
  • 상태 관리는 useState 훅을 사용하여 상품 정보를 관리합니다.
  • 초기 상태는 전달받은 item의 정보를 기반으로 설정됩니다.
입력 값 변경 핸들러
const handleChange = (e) => {
  const { value, name } = e.target;
  setProduct((product) => ({ ...product, [name]: value }));
};

 

입력 값이 변경될 때 상태를 업데이트하는 함수입니다.

setProduct((product) => ({ ...product, [name]: value }));
  • 기존 product 객체를 스프레드 연산자(...product)를 사용하여 복사합니다
  • [name]: value 부분은 객체의 속성 이름이 동적으로 설정되는 것을 의미합니다. name 변수의 값이 속성 이름이 되고, 해당 속성의 값은 value로 설정됩니다.
  • 즉, product 객체의 특정 속성을 업데이트하거나 새로 추가하는 것입니다.
파일 입력 핸들러
const handleFileChange = (e) => {
  const file = e.target.files[0];
  setProduct((prevProduct) => ({ ...prevProduct, photo: file }));
  setPhotoValue(e.target.value);
};

 

사진 파일이 변경될 때 상태를 업데이트하는 함수입니다.

setProduct((prevProduct) => ({ ...prevProduct, photo: file }));
  • 기존 prevProduct 객체를 스프레드 연산자(...prevProduct)를 사용하여 복사합니다.
  • photo: file 부분은 prevProduct 객체의 photo 속성을 file 값으로 설정합니다.
  • 즉, prevProduct 객체의 photo 속성을 새로운 파일 값으로 업데이트합니다.
상세 사진 파일 입력 핸들러
const handleDetailFileChange = (e) => {
  const files = e.target.files;
  setProduct((prevProduct) => ({
    ...prevProduct,
    detailPhotos: Array.from(files),
  }));
};

 

여러 개의 상세 사진 파일을 처리하는 함수입니다.

setProduct((prevProduct) => ({ ...prevProduct, detailPhotos: Array.from(files) }));
  • 기존 prevProduct 객체를 스프레드 연산자(...prevProduct)를 사용하여 복사합니다.
  • detailPhotos: Array.from(files) 부분은 prevProduct 객체의 detailPhotos 속성을 files 배열의 복사본으로 설정합니다. Array.from(files)는 files를 새로운 배열로 변환합니다.
  • prevProduct 객체의 detailPhotos 속성을 files 배열의 복사본으로 업데이트합니다.
폼 제출 핸들러
const onSubmit = async (e) => {
  e.preventDefault();
  try {
    const storageRef = oStorage.ref();
    if (product.photo) {
      const fileRef = storageRef.child(product.photo.name);
      await fileRef.put(product.photo);
      product.photo = await fileRef.getDownloadURL();
    }
    if (product.detailPhotos.length > 0) {
      const detailPhotoURLs = [];
      await Promise.all(
        product.detailPhotos.map(async (file, index) => {
          const fileName = `detailPhoto${index + 1}_${Date.now()}_${file.name}`;
          const detailFileRef = storageRef.child(fileName);
          await detailFileRef.put(file);
          detailPhotoURLs.push(await detailFileRef.getDownloadURL());
        })
      );
      product.detailPhotos = detailPhotoURLs;
    }
    await kuwazawa_productDB.child(item.key).update(product);
    navigate("/product");
  } catch (error) {
    console.log("오류 : ", error);
  }
};
  • 폼이 제출될 때 호출되는 함수입니다.
  • Firebase Storage에 파일을 업로드하고 Realtime Database를 업데이트합니다.