๐ ๊ณต๋ถํ๋ ์ง์ง์ํ์นด๋ ์ฒ์์ด์ง?
[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:26728x90
๋ฐ์ํ
<๋ณธ ๋ธ๋ก๊ทธ๋ 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
๋ฐ์ํ
'๐ฉโ๐ป ๋ฐฑ์๋(Back-End) > Node js' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Comments