일단 모든 영상들을 다시 지워준다.
mongodb shell
에서 db.videos.remove({})
를 해준다.
그리고 잠깐 복습하자면
videoSchema.pre("save", async function () {
this.hashtags = this.hashtags[0]
.split(",")
.map((word) => (word.startsWith("#") ? word : `#${word}`));
});
지난 파트에서 했던 부분이다. 어떤 이벤트가 발생하기 전에 중간에서 가로채서
문서를 수정 할수 있다는걸 배웠다. 지난 파트에서 이렇게 title
을 이런식으로 변경하는 법을 했었다.
this.title = "blablabla"
그리고 만약 이대로 두면 저장하는 모든 영상들의 제목은 모두 이렇게 될거다.
pre("save")
도 할수 있고 post("save")
도 있고 pre(validate")
도 있고
많은 middleware
가 있다.
https://mongoosejs.com/docs/middleware.html
이 사이트로 가서 middleware
를 살펴보면 validate
기타 등등 그외에도 엄청 많다.
이제 기회가 있다. 어떤 기회이냐면 findByIdAndUpdate
를 위한 pre mideelware
가 없다.
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.startsWith("#") ? word : `#${word}`)),
});
findByIdAndUpdate
는 findOneAndUpdate
를 호출하는데 findOneAndUpdate
를 위한
middleware
있다.
다만 문제는 findOneAndUpdate
는 save hook
을 호출하지 않는다. 그게 다른 점이다.
그리고 findOneAndUpdate
에서는 업데이트 하려는 문서에 접근을 할수가 없다.
save hook
같은 경우에는 여기 pre("save") middleware
를 보면
videoSchema.pre("save", async function () {
this.hashtags = this.hashtags[0]
.split(",")
.map((word) => (word.startsWith("#") ? word : `#${word}`));
});
여기에서는 update
하는 문서에 접근이 가능하다.
findBy
가 아니라 findOneAndUpdate
의 경우에는 접근 할수가 없다.
여기저기 손 볼 곳이 좀 있다. 그리고 모든게 다 save
에 있을 때 만큼 좋지도 않을 거다.
pre("save")
하는 방법을 배웠다. 좋은 방법이다. 하지만 이번 경우에는 그렇게 도움이 되지는 않는다.
왜냐하면 이 기능이 save
와 update
두 군데에 다 필요 하기 때문이다.
그렇기 때문에 지울거다. 방법이 나빠서가 아니라 더 나은 방법으로 하는게 있기 때문이다.
이제 다시 출발선으로 돌아왔다.
video
를 생성 할때 hashtags
를 직접 수동으로 처리 해야 했다.
나쁘진 않다. 다만 function
을 여기 저기 다 복붙해야 한다는거다.
한가지 옵션이 있는데 그 옵션은 여기에다가 function
을 만드는 거다.
video.js
에서
import mongoose from "mongoose";
export const formatHashtags = (hashtags) =>
hashtags.split(",").map((word) => (word.startsWith("#") ? word : `#${word}`));
방금 만든 formatHashtags
라는 function
을 사용해서 이렇게 해준다.
export const postUpload = async (req, res) => {
const { title, description, hashtags } = req.body;
try {
await Video.create({
title,
description,
hashtags: formatHashtags,
});
그러면 똑같은 결과가 나오게 된다.
(뭐가 문제인지 모르겠지만 formatHashtags
가 import
되지 않아서 직접 했다.)
그리고 이제 해야 할건
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: formatHashtags(hashtags),
});
이곳에다가 formatHashtags
를 입력하고 hashtags
를 필요로 하니까 request.body
에서부터 가져온다.
export const postUpload = async (req, res) => {
const { title, description, hashtags } = req.body;
try {
await Video.create({
title,
description,
hashtags: formatHashtags(hashtags),
이부분도 똑같이 해주면 된다. 이것도 괜찮다. 나쁘지 않은 방법이다.
지금 function
을 하나 가지고 있고 그 function
은 hashtags
를 인수로 받고
이 function
은 해시태그 array
를 return
한다.제대로 작동 하는지 테스트해 본다.
upload video
를 선택하고 각 input
에다가 정보를 입력하고 실행하면 업로드 성공
해시태그를 확인하러 가서 보면 제대로 작동한다.
하지만 다른 방법도 있다. 그걸 알아 보도록 한다.
그건 바로 static
이다. 알다시피 video.findById()
를 굉장히 자주 사용 하고 있다.
video.create()
도 사용하고 있고 video
모델의 function
들을 많이 쓰고 있다.
video.exists()
도 있다.중요한 건 이런 function
들을 만들 수 있고 그걸 어떻게 하는지 보겠다.
https://mongoosejs.com/docs/guide.html
여길 살펴 보면
animalSchema.static('findByBreed', function(breed) { return this.find({ breed }); });
그래서 static
을 생성하기 위해서 필요한 것은 schema.static
이랑 function
과
만들고자 하는 static
의 이름이다. 그러면 준비는 끝이다.
그러니 여기로 와서 videoSchema.static
video.js
에서
videoSchema.static("formatHashtags", function (hashtags) {
return hashtags
.split(",")
.map((word) => (word.startsWith("#") ? word : `#${word}`));
});
const Video = mongoose.model("Video", videoSchema);
export default Video;
static
의 이름은 formatHashtag
이렇게 그리고 function
을 받는다.
document
를 보면 원하는 아무 argument
나 다 받는다.
이 경우에는 hashtags
를 argument
로 받고 그리고 나서 hashtags
를 이대로 가져와서 return
하는 거다.
그리고 전에 썼던 부분은 당연히 필요 없으니 지워 준다. 몇가지 에러가 있다.
존재하지 않는걸 import
하려고 하니까 당연한 거다.
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: Video.formatHashtags(hashtags),
});
return res.redirect(`/videos/${id}`);
};
export const getUpload = (req, res) => {
return res.render("upload", { pageTitle: "Upload Video" });
};
export const postUpload = async (req, res) => {
const { title, description, hashtags } = req.body;
try {
await Video.create({
title,
description,
hashtags: Video.formatHashtags(hashtags),
});
그리고 이제 video.formatHashtags()
가 있으니 적용 해준다.
body
에서 딴 hashtags
를 argument
로 입력하는 거다.
req.body
에서 따온 hashtags
이다.
이제 직접 만든 function
이 formatHashtags
를 통해 접근이 가능해 졌다.
당연히 이름은 똑같아야 하지만 그렇게만 해주면 끝이다.
각자의 특별한 static function
을 만들수 있다는 건 엄청 대단한 거라고 생각한다.
video.findById
,video.exists
등등을 쓸수 있듯이 video.formatHashtags
도 쓸수 있는거다.
다른 function
을 어디서 import
할 필요도 더이상 없다.
Video
만 import
하면 formatHashtags
도 따라 온다.
그럼 다시 한번 잘 작동 하는지 테스트 해보겠다. upload video
를 해본다.
그리고 다시 Edit Video
도 확인한다. 잘 된다.
이 function
은 이제 video
에 포함 되어 있다.
그래서 video.formatHashtags
로 사용 할수 있는거다.
직접 video function
을 아무거나 만들수 있다는건 정말 대단한거다.
물론 middleware
도 훌룡하다. 지난 파트에서 배웠지만 또한 훨씬 유용하다.
예를 들면 만약 유저를 생성해야 한다면 유저를 생성하기 전에 비밀번호를 암호화 해야 던가
하지만 이것도 완전 유용하다. 직접 Video function
을 커스터 마이징 해서 만드는거 말이다.
다시 비디오들을 삭제해 보도록 한다.
db.videos.remove({})
해주면 home
으로 가서 새로고침하면 비디오가 하나도 안 남게 된다.
다시 upload video
를 해본다. 그러면 역시나 function
이 제대로 작동하고 있다.
지금부터 할거는
mixin
을 수정해서hashtags
가 보이도록 한다.
mixin
폴더로 가서 video.pug.
으로 가면
그전에 기억해야 할것이 video
에는 해시태그들이 array
형식으로 저장되어 있다.
아마 이런 걸 할수 있다는걸 기억해야 한다.
mixin video(video)
div
h4
a(href=`/videos/${video.id}`)=video.title
p=video.description
ul
each hashtag in video.hashtags
li=hashtag
small=video.createdAt
hr
이렇게 해주면 이제 video.hashtags
안에 있는 해시태그들이 다 보여지게 된다.
새로고침하면 바로 여러 해시태그들을 보여 주고 있다.
감사하게도(?) 지금 반복되는 코드가 싫어서 지금 현재 필요한 static function
을 만드는 법을 알았다.
이제 Video.formatHashtags() function
이 있다.
그리고 지난 파트에서는 pre("save") middleware
를 사용하는 법도 알았다.