./models/product.js
에서 상품 스키마 정의 후 export
// ... product.js
const Product = mongoose.model("Product", productSchema);
module.exports = Product;
## `index.js`에서 product 정보 `require`
// ... index.js
...
const Product = require("./models/product");
// ... seed.js
...
const p = new Product({
name: "Ruby Grapefruit",
price: 1.99,
category: "fruit",
});
p.save()
.then((p) => {
console.log(p);
})
.catch((e) => {
console.log(e);
});
> node seeds.js
MogoDB Connection Open!!!
{
name: 'Ruby Grapefruit',
price: 1.99,
category: 'fruit',
_id: new ObjectId("63d7c3ddf1e423f3d667883c"),
__v: 0
}
## Mongo Shell
> use farmStand
switched to db farmStand
> show collections
products
> db.products.find().pretty()
{
"_id" : ObjectId("63d7c3ddf1e423f3d667883c"),
"name" : "Ruby Grapefruit",
"price" : 1.99,
"category" : "fruit",
"__v" : 0
}
const seedProducts = [...];
// insertMany를 사용할 때 하나라도 유효성 검사를 통과하지 못하면 아무것도 삽입되지 않음에 주의!
Product.insertMany(seedProducts)
.then((res) => console.log(res))
.catch((e) => console.log(e));
> node seeds.js
MogoDB Connection Open!!!
[
{
name: 'Fairy Egglant',
price: 1,
category: 'vegetable',
_id: new ObjectId("63d7ce06fa47c1f0851d61c4"),
__v: 0
},
{
name: 'Organic Goddess Melon',
price: 4.99,
category: 'fruit',
_id: new ObjectId("63d7ce06fa47c1f0851d61c5"),
__v: 0
},
{
name: 'Organic Mini Seedless Watermelon',
price: 3.99,
category: 'fruit',
_id: new ObjectId("63d7ce06fa47c1f0851d61c6"),
__v: 0
},
{
name: 'Organic Celery',
price: 1.5,
category: 'vegetable',
_id: new ObjectId("63d7ce06fa47c1f0851d61c7"),
__v: 0
},
{
name: 'Chocolate Whole Milk',
price: 2.69,
category: 'dairy',
_id: new ObjectId("63d7ce06fa47c1f0851d61c8"),
__v: 0
}
]
## Mongo Shell
> db.products.find().pretty()
{
"_id" : ObjectId("63d7c3ddf1e423f3d667883c"),
"name" : "Ruby Grapefruit",
"price" : 1.99,
"category" : "fruit",
"__v" : 0
}
{
"_id" : ObjectId("63d7ce06fa47c1f0851d61c4"),
"name" : "Fairy Egglant",
"price" : 1,
"category" : "vegetable",
"__v" : 0
}
{
"_id" : ObjectId("63d7ce06fa47c1f0851d61c5"),
"name" : "Organic Goddess Melon",
"price" : 4.99,
"category" : "fruit",
"__v" : 0
}
{
"_id" : ObjectId("63d7ce06fa47c1f0851d61c6"),
"name" : "Organic Mini Seedless Watermelon",
"price" : 3.99,
"category" : "fruit",
"__v" : 0
}
{
"_id" : ObjectId("63d7ce06fa47c1f0851d61c7"),
"name" : "Organic Celery",
"price" : 1.5,
"category" : "vegetable",
"__v" : 0
}
{
"_id" : ObjectId("63d7ce06fa47c1f0851d61c8"),
"name" : "Chocolate Whole Milk",
"price" : 2.69,
"category" : "dairy",
"__v" : 0
}
...
app.get("/products", async (req, res) => {
const products = await Product.find({});
res.render("products/index", { products });
});
// views/products/index.ejs
...
<ul>
<% for (const product of products) { %>
<li><%= product.name %></li>
<% } %>
</ul>
READ
상세정보 페이지 만들기// ... index.js
...
// 상품 상세 정보
// URL을 안전하게 만드는 웹 Slug
// id는 Mongo ID 사용 - "Slug"는 일반적으로 이미 얻은 데이터를 사용하여 유효한 URL을 생성하는 방법
app.get("/products/:id", async (req, res) => {
const { id } = req.params;
const product = await Product.findById(id);
res.render("products/show", { product });
});
http://localhost:3133/products/63d7c3ddf1e423f3d667883c
와 같이 Mongo ID를 이용해 접속CREATE
상품 추가 페이지 만들기// ... index.js
...
// 상품 추가 form input 페이지
app.get("/products/new", (req, res) => {
res.render("products/new");
});
// 추가한 상품 정보 제출
app.post("/products", async (req, res) => {
const newProduct = new Product(req.body); // 유효성 검사를 하지 않으니 req.body에 포함된 정보 중 나타나서는 안되는 추가적인 정보를 확인할 수 없다
await newProduct.save();
res.redirect(`products/${newProduct._id}`); // newProduct._id 언더스코어 Mongoose로부터 실제 정보를 받았을 때만 작동
});
UPDATE
개별 상품 업데이트하기PUT 요청은 PATCH 요청과 달리 객체를 재정의하거나 교체할 때 사용하고
PATCH 요청은 문서나 객체의 일부를 변경할 때 사용
form Method를 PUT으로 할 수 없기 때문에 method-override
설치
> npm i method-override
// ... index.js
...
const methodOverride = require("method-override");
app.use(methodOverride("_method"));
<!-- edit.ejs -->
...
<form action="/products/<%= product._id %>?_method=PUT" method="POST">
// ... index.js
...
// 개별 상품 업데이트하기
app.get("/products/:id/edit", async (req, res) => {
const { id } = req.params;
const product = await Product.findById(id);
res.render("products/edit", { product });
});
app.put("/products/:id", async (req, res) => {
const { id } = req.params;
const product = await Product.findByIdAndUpdate(id, req.body, {
runValidators: true, // default 유효성 검사
new: true,
});
res.redirect(`/products/${product._id}`);
});
<!-- edit.ejs -->
...
<option value="fruit" <%= product.category === "fruit" ? "selected" : "" %> >fruit</option>
<option value="fruit" <%= product.category === "vegetable" ? "selected" : "" %> >vegetable</option>
<option value="fruit" <%= product.category === "diary" ? "selected" : "" %> >diary</option>
option
에 selected
값을 추가해도 되지만 카테고리가 추가될 수록 가독성이 떨어짐// ... index.js
...
const categories = ["fruit", "vegetable", "dairy"];
...
app.get("/products/new", (req, res) => {
res.render("products/new", { categories });
});
<!-- edit.ejs -->
...
<% for(let category of categories) {%>
<option value="<%= category %>" <%= product.category === category ? "selected" : "" %> > <%= category %> </option>
<% } %>
DELETE
상품 정보 삭제하기<!-- show.ejs -->
...
<form action="/products/<%= products._id %>?_method=DELETE" method="POST">
<button>DELETE</button>
</form>
// ... index.js
...
// 상품 정보 삭제
app.delete("/products/:id", async (req, res) => {
const { id } = req.params;
const deleteProduct = await Product.findByIdAndDelete(id);
res.redirect("/products");
});
<!-- show.ejs -->
...
<li>Category : <a href="/products?category=<%= product.category %>"><%= product.category %></a></li>
// ... index.js
...
app.get("/products", async (req, res) => {
const { category } = req.query; // req.query에서 카테고리를 찾은 후 있다면
if (category) {
const products = await Product.find({ category }); // 카테고리 페이지로
res.render("products/index", { products, category });
} else {
const products = await Product.find({}); // 모든 데이터 조회, 조회하는 시간이 필요하기 때문에 이 라우터에 비동기 핸들러를 만들기
res.render("products/index", { products, category: "All" });
}
});
<!-- index.js -->
<h1><%= category %> Products</h1>
<!-- index.ejs -->
<% if(category !== "All") {%>
<a href="/products">All Products</a>
<% } %>