๐Ÿ˜Ž ๊ณต๋ถ€ํ•˜๋Š” ์ง•์ง•์•ŒํŒŒ์นด๋Š” ์ฒ˜์Œ์ด์ง€?

[E-Commerce App with REST API] (18) ์œ„์‹œ๋ฆฌ์ŠคํŠธ์— ์ƒํ’ˆ ๋„ฃ๊ธฐ (PUT) & ์ƒํ’ˆ ๋ณ„์  ๋งค๊ธฐ๊ธฐ (PUT) ๋ณธ๋ฌธ

๐Ÿ‘ฉ‍๐Ÿ’ป ๋ฐฑ์—”๋“œ(Back-End)/Node js

[E-Commerce App with REST API] (18) ์œ„์‹œ๋ฆฌ์ŠคํŠธ์— ์ƒํ’ˆ ๋„ฃ๊ธฐ (PUT) & ์ƒํ’ˆ ๋ณ„์  ๋งค๊ธฐ๊ธฐ (PUT)

์ง•์ง•์•ŒํŒŒ์นด 2023. 4. 1. 19:26
728x90
๋ฐ˜์‘ํ˜•

<๋ณธ ๋ธ”๋กœ๊ทธ๋Š” Developers Corner ์˜ ์œ ํŠœ๋ธŒ๋ฅผ ์ฐธ๊ณ ํ•ด์„œ ๊ณต๋ถ€ํ•˜๋ฉฐ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค :-)>

=> Node.js E-Commerce App with REST API: Let's Build a Real-Life Example!

 

๐ŸŒท ์œ„์‹œ๋ฆฌ์ŠคํŠธ์— ์ƒํ’ˆ ๋„ฃ๊ธฐ 

 

๐ŸŒท ์ƒํ’ˆ ๋ณ„์  ๋งค๊ธฐ๊ธฐ

ํŠน์ • field์˜ ๊ฐ’์„ ์ˆ˜์ •ํ•  ๋• $set ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉ

$elemMatch ๋ฐฐ์—ด์•ˆ์— ducument ์—ฌ๋Ÿฌ ํ•„๋“œ๋ฅผ ์กฐ๊ฑด ๊ฑธ์–ด์„œ ์ฐพ์„ ๋•Œ ์‚ฌ์šฉ

 

โœ… Map

: ๋ฐฐ์—ด ๋‚ด์˜ ๋ชจ๋“  ์š”์†Œ ๊ฐ๊ฐ์— ๋Œ€ํ•˜์—ฌ ์ฃผ์–ด์ง„ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์•„ ์ƒˆ๋กœ์šด ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜

๋ฐฐ์—ด.map((์š”์†Œ, ์ธ๋ฑ์Šค, ๋ฐฐ์—ด) => { return ์š”์†Œ });

 

โœ… Reduce

: ๋ฐฐ์—ด์„ ์ˆœํšŒํ•˜๋ฉด์„œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ๋ฐ˜๋ณต ์‹คํ–‰ํ•˜๊ณ , ๋ชจ๋“  ๋ฐ˜๋ณต์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ์ค„์ด๋Š”(reduce) ํ•จ์ˆ˜

๋ฐฐ์—ด.reduce((๋ˆ„์ ๊ฐ’, ํ˜„์žฟ๊ฐ’, ์ธ๋ฑ์Šค, ์š”์†Œ) => { return ๊ฒฐ๊ณผ }, ์ดˆ๊นƒ๊ฐ’);

 

๐ŸŒท ์ƒํ’ˆ ๋ณ„์  ๋งค๊ธฐ๊ธฐ (+ ์ „์ฒด ํ‰๊ท  ๋ณ„์ )

ํ‰๊ท ๋„ ๋ƒˆ๊ณ  ์ถ”๊ฐ€๋กœ comment ๋„ ๋‹ฌ์Œ ^^

 

๐ŸŒท ์ฝ”๋“œ

โœ… controllers/productCtrl.js

const Product = require("../models/Product");
const User = require("../models/User");
const asyncHandler = require("express-async-handler");
const slugify = require("slugify");
const { validateMongodbID } = require("../utils/validateMongodbID");

// ์ƒํ’ˆ ๋“ฑ๋ก
const createProduct = asyncHandler(async (req, res) => {
  try {
    if (req.body.title) {
      // slugify : ํ…์ŠคํŠธ๋ฅผ url ์ฃผ์†Œ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
      // slug : ์ด๋ฏธ ์–ป์€ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์œ ํšจํ•œ URL์„ ์ƒ์„ฑ (URL๊ณผ ์˜๋ฏธ์žˆ๋Š” ์ด๋ฆ„์„ ์‚ฌ์šฉ)
      req.body.slug = slugify(req.body.title);
    }
    const newProduct = await Product.create(req.body);
    res.json(newProduct);
  } catch (error) {
    throw new Error(error);
  }
});

// ์ƒํ’ˆ ์ˆ˜์ •
const updateProduct = asyncHandler(async (req, res) => {
  const { id } = req.params;
  validateMongodbID(id);
  try {
    if (req.body.title) {
      // slugify : ํ…์ŠคํŠธ๋ฅผ url ์ฃผ์†Œ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
      // slug : ์ด๋ฏธ ์–ป์€ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์œ ํšจํ•œ URL์„ ์ƒ์„ฑ (URL๊ณผ ์˜๋ฏธ์žˆ๋Š” ์ด๋ฆ„์„ ์‚ฌ์šฉ)
      req.body.slug = slugify(req.body.title);
    }
    const updateProduct = await Product.findOneAndUpdate(id,
      req.body, {
      new: true,
    });
    res.json(updateProduct);
  } catch (error) {
    throw new Error(error);
  }
});

// ์ƒํ’ˆ ์‚ญ์ œ
const deleteProduct = asyncHandler(async (req, res) => {
  const { id } = req.params;
  validateMongodbID(id);
  try {
    if (req.body.title) {
      // slugify : ํ…์ŠคํŠธ๋ฅผ url ์ฃผ์†Œ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
      // slug : ์ด๋ฏธ ์–ป์€ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์œ ํšจํ•œ URL์„ ์ƒ์„ฑ (URL๊ณผ ์˜๋ฏธ์žˆ๋Š” ์ด๋ฆ„์„ ์‚ฌ์šฉ)
      req.body.slug = slugify(req.body.title);
    }
    const deleteProduct = await Product.findOneAndDelete(id);
    res.json(deleteProduct);
  } catch (error) {
    throw new Error(error);
  }
});

// ์ƒํ’ˆ id ์กฐํšŒ
const getAProduct = asyncHandler(async (req, res) => {
  const { id } = req.params;
  validateMongodbID(id);

  try {
    const findProduct = await Product.findById(id);
    res.json(findProduct);
  } catch (error) {
    throw new Error(error);
  }
});

// ๋ชจ๋“  ์ƒํ’ˆ ์กฐํšŒ
const getAllProduct = asyncHandler(async (req, res) => {
  try {
    // 1) Filtering => t?price[lt]=50000
    const queryObj = { ...req.query };
    const excludeFields = ["page", "sort", "limit", "fields"];
    excludeFields.forEach((el) => delete queryObj[el])
    console.log(queryObj);

    // JSON.stringify : JavaScript ๊ฐ’์ด๋‚˜ ๊ฐ์ฒด๋ฅผ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
    let queryStr = JSON.stringify(queryObj);
    // Create operators ($gt, $gte, etc)
    queryStr = queryStr.replace(/\b(gt|gte|lt|lte)\b/g, (match) => `$${match}`);
    // JSON.parse : JSON ๋ฌธ์ž์—ด์„ ์ธ์ž๋กœ ๋ฐ›๊ณ  ๊ฒฐ๊ณผ๊ฐ’์œผ๋กœ JavaScript ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜
    let query = Product.find(JSON.parse(queryStr));

    // 2)Sorting => ?sort=-category,-brand
    if (req.query.sort) {
      const sortBy = req.query.sort.split(",").join(" ");
      query = query.sort(sortBy);
    } else {
      query = query.sort("-createdAt");
    }

    // 3) Limiting the fields => ?fields=-title,-price,-category
    if (req.query.fields) {
      const fields = req.query.fields.split(",").join(" ");
      query = query.select(fields);
    } else {
      query = query.select("-__v");
    }

    // 4) pagination  => ?page=4&limit=5
    // ํŽ˜์ด์ง€ ๋‚˜๋ˆ„๊ธฐ, ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ๊ฐ’์œผ๋กœ ๋ฆฌํ„ด๋œ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ถ„ํ• ํ•˜์—ฌ ์ „๋‹ฌ
    const page = req.query.page;
    const limit = req.query.limit;
    const skip = (page - 1) * limit;
    query = query.skip(skip).limit(limit);
    if (req.query.page) {
      const productCount = await Product.countDocuments();
      if (skip >= productCount) {
        throw new Error("This page does not exists");
      }
    }
    // console.log(page, limit, skip);

    const product = await query;
    res.json(product);
  } catch (error) {
    throw new Error(error);
  }
});

// ์œ„์‹œ๋ฆฌ์ŠคํŠธ์— ์ƒํ’ˆ ๋„ฃ๊ธฐ
const addTowishList = asyncHandler(async (req, res) => {
  const { _id } = req.user;
  const { prodId } = req.body;
  try {
    const user = await User.findById(_id);
    const alreadyadded = user.wishList.find((id) => id.toString() === prodId);

    if (alreadyadded) {
      let user = await User.findByIdAndUpdate(_id, {
        $pull: { wishList: prodId },
      }, {
        new: true,
      });
      res.json(user);
    } else {
      let user = await User.findByIdAndUpdate(_id, {
        $push: { wishList: prodId },
      }, {
        new: true,
      });
      res.json(user);
    }
  } catch (error) {
    throw new Error(error);
  }
});

// ์ƒํ’ˆ ๋ณ„์  ๋งค๊ธฐ๊ธฐ
const rating = asyncHandler(async (req, res) => {
  const { _id } = req.user;
  const { star, comment, prodId } = req.body;

  try {
    const product = await Product.findById(prodId);
    let alreadyRated = product.ratings.find(
      (userId) => userId.toString() === _id.toString());

    if (alreadyRated) {
      const updateRating = await Product.updateOne(
        {
          ratings: { $elemMatch: alreadyRated },
        }, {
        $set: { "ratings.$.star": star, "ratings.$.comment": comment },
      }, {
        new: true
      });
      // res.json(updateRating);
    } else {
      const rateProduct = await Product.findByIdAndUpdate(
        prodId,
        {
          $push: {
            ratings: {
              star: star,
              comment: comment,
              postedby: _id,
            },
          },
        });
      // res.json(rateProduct);
    }

    // ์ด ๋ณ„์  ํ‰๊ท ๋‚ด๊ธฐ
    const getallratings = await Product.findById(prodId);
    let totalRating = getallratings.ratings.length;   // ๊ฐœ์ˆ˜
    let ratingsum = getallratings.ratings
      .map((item) => item.star)
      .reduce((prev, curr) => prev + curr, 0);
    let actualRating = Math.round(ratingsum / totalRating);
    let finalproduct = await Product.findByIdAndUpdate(
      prodId, {
        totalrating: actualRating,
    }, {
      new: true
    });
    res.json(finalproduct);
  } catch (error) {
    throw new Error(error);
  }
});

module.exports = {
  createProduct,
  updateProduct,
  deleteProduct,
  getAProduct,
  getAllProduct,
  addTowishList,
  rating,
};

 

โœ… routes/productRoute.js

const express = require("express");
const { createProduct, getAProduct, getAllProduct, updateProduct, deleteProduct, addTowishList, rating } = require("../controllers/productCtrl");
const { isAdmin, authMiddleware } = require("../middlewares/authMiddleware");
const router = express.Router();

router.post("/", authMiddleware, isAdmin, createProduct);
router.get("/:id", getAProduct);
router.put("/wishlist", authMiddleware, addTowishList);
router.put("/rating", authMiddleware, rating);

router.put("/:id", authMiddleware, isAdmin, updateProduct);
router.delete("/:id", authMiddleware, isAdmin, deleteProduct);
router.get("/", getAllProduct);

module.exports = router;

 

โœ… models/Product.js

const mongoose = require("mongoose");

const ProductSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true,
    trim: true,
  },
  slug: {
    type: String,
    // unique: true,
    lowercase: true,
  },
  description: {
    type: String,
    required: true,
  },
  price: {
    type: Number,
    required: true,
  },
  category: {
    type: String,
    required: true,
  },
  brand: {
    type: String,
    // enum: ["Apple", "Samsung", "Lenovo"],
    required: true,
  },
  quantity: {
    type: Number,
    required: true,
  },
  sold: {
    type: Number,
    default: 0,
  },
  images: {
    type: Array,
  },
  color: {
    type: String,
    enum: ["Black", "Brown", "Red", "Pink"],
  },
  ratings: [{
    star: Number,
    comment : String,
    postedby: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
  }],
  totalrating: {
    type: String,
    default: 0,
  },
}, {
  timestamps: true,
  collection: 'product'
});

const Product = mongoose.model("Product", ProductSchema);
module.exports = Product;
728x90
๋ฐ˜์‘ํ˜•
Comments