쇼핑몰 구현 프로젝트

[쇼핑몰 구현 프로젝트] 06. 상품 관련 기능

binning 2024. 6. 23. 15:34

① Frontend

- 상품 생성 시 JSON 대신 FromData 형식으로 전달 (image 때문에)

   const submitHandler = async (evt) => {
    evt.preventDefault();
    try {
      const sendData = new FormData();
      sendData.append('name', formData.name);
      sendData.append('description', formData.description);
      sendData.append('image', evt.target.image.files[0]);
      sendData.append('category', formData.category);
      sendData.append('price', formData.price);
      sendData.append('stock', formData.stock);
      await sendRequest(
        process.env.REACT_APP_BACKEND_URL + '/products',
        'POST',
        sendData
        /*JSON.stringify({
          name: formData.name,
          description: formData.description,
          image: formData.image,
          category: formData.category,
          price: formData.price,
          stock: formData.stock
        }),
        {
          'Content-Type': 'application/json'
        }*/
      );
      window.location.href = "/";
    } catch (err) {
      setIsError(true);
      setErrorMessage(err.message);
    }
  };

 

- 상품 수정 시 기존 데이터를 useEffect로 넣기

  useEffect(() => {
    setFormData({
      name: product?.name || "",
      description: product?.description || "",
      category: product?.category || "",
      price: product?.price || 0,
      stock: product?.stock || 0
    });
  }, [product]);

 

- Data를 불러올 때 useEffect안에 함수를 작성하여 불러오기

  useEffect(() => {
    const fetchProducts = async () => {
      try {
        const responseData = await sendRequest(
          process.env.REACT_APP_BACKEND_URL + '/products'
        );
        setProducts(responseData.products);
      } catch (err) {
        setIsError(true);
        setErrorMessage(err.message);
      }
    };
    fetchProducts();
  }, [sendRequest]);

 

② Backend

상품의 Image를 올리면 내부 파일에 저장되고 상품 삭제 시 같이 삭제되도록 함

- Image upload

const MIME_TYPE_MAP = {
  'image/png': 'png',
  'image/jpeg': 'jpeg',
  'image/jpg': 'jpg'
};

const fileUpload = multer({
  limits: 500000,
  storage: multer.diskStorage({
    destination: (req, file, cb) => {
      cb(null, 'uploads/images');
    },
    filename: (req, file, cb) => {
      const ext = MIME_TYPE_MAP[file.mimetype];
      cb(null, uuid() + '.' + ext);
    }
  }),
  fileFilter: (req, file, cb) => {
    const isValid = !!MIME_TYPE_MAP[file.mimetype];
    let error = isValid ? null : new Error('Invalid mime type!');
    cb(error, isValid);
  }
});

 

- Controller

const getHomeProduct = async (req, res, next) => {
  let products;
  try {
    products = await Product.find({});
  } catch (err) {
    const error = new Error('something went wrong');
    error.code = 500;
    return next(error);
  }
  res.status(200).json({products: products.map(product => product.toObject({ getters: true }))});
};

const getProduct = async (req, res, next) => {
  const productId = req.params.pid;

  let product;
  try {
    product = await Product.findById(productId);
  } catch (err) {
    const error = new Error('something went wrong');
    error.code = 500;
    return next(error);
  }

  if (!product) {
    const error = new Error('can not find product');
    error.code = 404;
    return next(error);
  }

  res.status(200).json({ product: product.toObject({ getters: true }) });
};

const getCategory = async (req, res, next) => {
  const categoryId = req.params.cid;

  let products;
  try {
    products = await Product.find({category: categoryId});
  } catch (err) {
    const error = new Error('something went wrong');
    error.code = 500;
    return next(error);
  }

  if (!products || products.length === 0) {
    const error = new Error('can not find product');
    error.code = 404;
    return next(error);
  }

  res.status(200).json({
    products: products.map(product =>
      product.toObject({ getters: true })
    )
  });
};

const createProduct = async (req, res, next) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    const error = new Error("Invalid inputs");
    error.code = 422;
    return next(error);
  }

  const { name, description, category, price, stock } = req.body;

  
  const createdProduct = new Product({
    name,
    description,
    category,
    price,
    stock,
    image: process.env.BACKEND_URL + req.file.path,
    review: []
  });

  try {
    await createdProduct.save();
  } catch (err) {
    const error = new Error('something went wrong');
    error.code = 500;
    return next(error);
  }

  res.status(201).json({ product: createdProduct });
};

const editProduct = async (req, res, next) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    const error = new Error("Invalid inputs");
    error.code = 422;
    return next(error);
  }

  const { name, description, category, price, stock } = req.body;
  const productId = req.params.pid;

  let product;
  try {
    product = await Product.findById(productId);
  } catch (err) {
    const error = new Error('something went wrong');
    error.code = 500;
    return next(error);
  }

  product.name = name;
  product.description = description;
  product.category = category;
  product.price = price;
  product.stock = stock;

  try {
    await product.save();
  } catch (err) {
    const error = new Error('something went wrong');
    error.code = 500;
    return next(error);
  }

  res.status(200).json({ product: product.toObject({ getters: true }) });
};

const deleteProduct = async (req, res, next) => {
  const productId = req.params.pid;
  
  let product;
  try {
    product = await Product.findById(productId);
  } catch (err) {
    const error = new Error('something went wrong');
    error.code = 500;
    return next(error);
  }

  if (!product) {
    const error = new Error('can not find product');
    error.code = 404;
    return next(error);
  }
  
  const imagePath = product.image.replace(process.env.BACKEND_URL, '');
  
  try {
    await product.deleteOne();
  } catch (err) {
    const error = new Error('something went wrong');
    error.code = 500;
    return next(error);
  }
  
  fs.unlink(imagePath, err => {
    console.log(err);  
  });
  
  res.status(200).json({ message: 'Delete product.' });
};