Node.js/Express Mongoose(몽구스)-CRUD-Detail/Update/Delete
25 May 2021디렉토리 구조나 파일 구조 등은 이전 포스트에서 이어짐
☄ Video Detail
몽구스가 생성한 video.id
가 24자리 16진수로 이루어져 있기 때문에 라우팅 파라미터의 id
정규표현식을 바꿔 줘야 함
- regex :
/[0-9a-f]{24}/
그래야 /video/upload
로 접속 시 upload
가 id
파라미터로 매핑되는 일이 없어짐.
// routers/videoRouter.js
// ... 생략
videoRouter.get("/:id([0-9a-f]{24})", watch);
videoRouter.route("/:id([0-9a-f]{24})/edit").get(getEdit).post(postEdit);
videoRouter.route("/upload").get(getUpload).post(postUpload);
// ... 생략
수정하는 김에 edit도 같이 수정
그 다음 req.params
가 받아온 id
값을 이용해서 Video.findById
쿼리 실행(async
await
빼먹지 않게 조심)
// controllers/videoController.js
// ... 생략
export const watch = async (req, res) => {
const { id } = req.params;
const video = await Video.findById(id);
return res.render("watch", { pageTitle: video.title, video });
};
// ... 생략
Video.findById().exec()
는 promise
객체를 리턴해 주는데, async
await
를 사용하면 없어도 됨.
마지막으로 존재하지 않는 Video
에 대한 예외 처리를 해줘야 한다.
간단하게 404.pug
페이지를 만든 뒤, if
문으로 예외처리
// views/404.pug
extends base
// controllers/videoController.js 위 내용 수정
if (!video) {
return res.render("404", { pageTitle: "Video Not Found" });
}
return res.render("watch", { pageTitle: video.title, video });
☄ Edit Video
위 watch
컨트롤러와 마찬가지로 비디오 수정 페이지를 렌더링하는 getEdit
컨트롤러 수정
// controllers/videoController.js
// ... 생략
export const getEdit = async (req, res) => {
const { id } = req.params;
const video = await Video.findById(id);
if (!video) {
return res.render("404", { pageTitle: "Video Not Found" });
}
return res.render("videoEdit", { pageTitle: `Edit, ${video.title} `, video });
};
// ... 생략
추가적으로 videoEdit.pug도 수정
// views/videoEdit.pug
extends base
block content
form(method="post")
input(name="title", placeholder="Video Title", value=`${video.title}`, required)
input(type="text", name="description", placeholder="New discription", required, maxlength="100", value=`${video.description}`)
input(type="text", name="hashtags", placeholder="hashtags, seperated by ,(comma)", required, style="width: 50%", value=`${video.hashtags.join()}`)
input(value="save", type="submit")
post request로 전달된 req.body
데이터를 DB에 저장하기
가장 간단한 방법은 model
객체의 각 필드 값에 대입한 후 save
메서드 호출이다.
// controllers/videoController.js
// ... 생략
export const postEdit = async (req, res) => {
const { id } = req.params;
const { title, description, hashtags } = req.body;
const video = await Video.findById(id);
if (!video) {
return res.render("404", { pageTitle: "Video Not Found" });
}
video.title = title;
video.description = description;
video.hashtags = hashtags
.split(",")
.map((word) =>
word.trim()[0] === "#" ? `${word.trim()}` : `#${word.trim()}`
);
await video.save();
return res.redirect(`/videos/${id}`);
};
또 다른 방법은 Model.findByIdAndUpdate()
메서드 호출이다.
// controllers/videoController.js
// ... 생략
export const postEdit = async (req, res) => {
const { id } = req.params;
const { title, description, hashtags } = req.body;
const video = await Video.exists({ _id: id }); // 변경
if (!video) {
return res.render("404", { pageTitle: "Video Not Found" });
}
// 변경
await Video.findByIdAndUpdate(id, {
title,
description,
hashtags: hashtags
.split(",")
.map((word) =>
word.trim()[0] === "#" ? `${word.trim()}` : `#${word.trim()}`
),
});
return res.redirect(`/videos/${id}`);
};
Model
객체는 다양한 쿼리를 다룰 수 있는 메서드들을 제공한다.
→ https://mongoosejs.com/docs/api/model.html
Parameters
- id «Object/Number/String» value of
_id
to query by- [update] «Object»
- [options] «Object» optional see
Query.prototype.setOptions()
- 첫 번째 파라미터는
id
로document
객체를 구분할 수 있는 필드 값이다. - 두 번째 파라미터는
Object
로 업데이트할 내용들을 객체로 전달하면 된다.
- 그러면 Mongoose는 DB에서
Model
에 해당하는collection
을 찾고, - 전달된
id
값과 일치하는document
를 조회한 뒤, - 전달된
Object
객체의 필드 값을 매칭시켜 내용을 업데이트 한 후 save
까지 호출해 저장한다.
그리고 Model.exists
라는 메서드도 사용했는데,
Parameters
- filter «Object»
- [callback] «Function» callback
필터 Object
와 일치하는 document
를 찾아 true
or false
를 반환한다.
다양한 필터 조건을 편라하게 Object
로 구성해 document
객체 존재 여부를 알 수 있다.
☄ Delete Video
비디오를 삭제하는 쿼리 함수는 매우 간단하다. findOneAndDelete()
함수나 id
를 이용해 더 간단하게 줄인 findByIdAndDelete()
를 사용하면 된다.
// controllers/videoController.js
// ... 생략
export const deleteVideo = async (req, res) => {
const { id } = req.params;
await Video.findByIdAndDelete(id);
return res.redirect("/");
};
사실 데이터를 삭제하는 쿼리 함수는 findOneAndDelete()
외에 remove()
가 하나 더 있다(있었다). 그런데 이제 거의 모든 경우에 findOneAndDelete()
를 사용하도록 대체됐다.
findOneAndDelete()
에 대한 몽구스 docs 설명
This function differs slightly from
Model.findOneAndRemove()
in thatfindOneAndRemove()
becomes a MongoDBfindAndModify()
command, as opposed to afindOneAndDelete()
command. For most mongoose use cases, this distinction is purely pedantic. You should usefindOneAndDelete()
unless you have a good reason not to.
Reference
https://mongoosejs.com/docs/api/model.html
https://mongoosejs.com/docs/api.html#model_Model.findOneAndDelete