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
_idto 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