만약
update
할때 마다 콘솔에서useFindAndModify optionset to false..
같은 경고를 받는다고 가정 한다.
걱정할 필요 없다. 단순하게 useFindAndModify
를 DB
안에 추가하면 된다.
(현재 mongoose
는 useFindAndModify
는 기본값이 false
로 되어 있어서
에러가 뜨지 않는다. 그러므로 따로 useFindAndModify
안해줘도 된다.)
db.js
에서
mongoose.connect("mongodb://127.0.0.1:27017/wetube", {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
});
이제
hook
혹은middleware
라고 불리는 거에 대해 알아본다.
그러니까 Mongoose
에서 middleware
라는건
https://mongoosejs.com/docs/middleware.html
request
를 중간에서 가로채서 뭔가를 하고 이어서 진행하는 거다.
Express
에 있는 어떤 middleware
는 뭔가를 하고 next
를 콜한 다음 request
를 계속 처리 한다.
도움이 될만한 예는 morgan middleware
가 있겠다.
Mongoose
에서도 똑같다. document
에 무슨 일이 생기기 전이나 후에 middleware
를 적용할수 있다.
예를 들면 save
하기 전,후로 middleware
를 적용하거나 function
을 실행 할수 있다.
혹은 update
하기전이나 후에 말이다. 그리고 Express
의 middleware
같이 흐름을 방해하지 않는다.
중간에 뭔가를 할뿐 그후에는 흐름을 이어간다.
일단
database
에 있는 비디오들을 다 삭제 한다.
하지만 계속하기전에 Mongoose
에서 middleware
는 어떻게 생겼는지 본다.
schema.pre('save', async function() {
await doStuff();
await doMoreStuff();
});
사실 굉장히 간단하다. save
그리고 function
들을 실행만 시키면 된다.
그럼이제 model
폴더 안 video.js
로 가서 여기서 첫번째 middleware
를 만들어 본다.
하지만 한가지 중요한 것은 middelware
무조건 model
이 생성 되기전에 만들어야 한다.
const videoSchema = new mongoose.Schema({
title: { type: String, required: true, trim: true, maxLength: 80 },
description: { type: String, required: true, trim: true, maxLength: 20 },
createdAt: { type: Date, required: true, default: Date.now },
hashtags: [{ type: String, trim: true }],
meta: {
views: { type: Number, default: 0, required: true },
rating: { type: Number, default: 0, required: true },
},
});
videoSchema.pre('save', async function(){
})
const Video = mongoose.model("Video", videoSchema);
export default Video;
그러니 위치는 저곳에다가 만들어 준다. middelware
로 호출된 async function
을 보낸다.
이제 설명서를 보면 이 middleware
에 관해 하나있는데
https://mongoosejs.com/docs/middleware.html
여기 있는 middleware
들 중에 현재save
를 만드는 중이고 function
안에 this
라는 키워드가 있다.
그리고 이 this
는 우리가 저장하고자 하는 문서를 가리키는 거다.
그럼 시험삼아 this
를 console.log
해 본다.
videoSchema.pre("save", async function () {
console.log(this);
});
그리고 이젠 새 document
새로운 영상을 만들건데 그것을 console.log
할 거다.
한가지 더 추가한다.
console.log("We are about to save:", this);
이제 준비 되었다. 그럼 이제 생성해 본다. upload video
에서 제목,상세설명,해시태그들을 적어준다.
비디오는 잘 생성되었고 console
을 확인해 보면
GET / 304 169.335 ms - -
GET /videos/upload 200 36.764 ms - 814
We are about to save: {
title: 'Someting with HTML',
description: 'Delicious',
hashtags: [ '#for', '#real', '#now', '#mongo' ],
meta: { views: 0, rating: 0 },
_id: new ObjectId("62575d76b224e2c022b66c88"),
createdAt: 2022-04-13T23:32:06.328Z
}
POST /videos/upload 302 39.178 ms - 46
GET / 200 44.298 ms - 656
GET /videos/62575d76b224e2c022b66c88 200 27.969 ms - 666
잘 되고 있다. 작동 하는걸 확인 했으니 원한다면 이것 저것 바꿔 볼수 있다.
원하는대로 아무거나 바꿔 볼수 있다.
예를 들면 여기에서
videoSchema.pre("save", async function () {
console.log("We are about to save:", this);
this.title = "Hahaha! Im a middleware!!";
});
이런식으로 변경해 본다. document
를 수정 할 수도 있다. 왜냐하면 저 부분은 save
되기전에 실행 되기 때문이다. 다 javascript
에서 하는거다.
그러면 다시 한번 upload video
로 가서 입력하면 제목이 입력한게 아닌 미리 해 놓았던
제목으로 변경이 되어있다. 이런식으로 아무거나 마음대로 바꿀수가 있다.
이제 하려는건
videoController.js
에서
hashtags: hashtags
.split(",")
.map((word) => (word.startsWith("#") ? word : `#${word}`)),
여기 create
에 있는 hashtags funcrtion
을 지운다.
export const postUpload = async (req, res) => {
const { title, description, hashtags } = req.body;
try {
await Video.create({
title,
description,
hashtags,
});
어떤 결과가 나올지 한번 보도록 한다. 이 object
가 어떻게 저장될지 console.log
로 본다.
video.js
에서
videoSchema.pre("save", async function () {
console.log(this);
});
그리고 이렇게 수정해 주고 이제 hashtags
에 대해서 아무 처리도 하지 않을때
video
가 어떻게 저장 되나 본다. 이 경우에 hashtahs
는 단순히 이런 stirng
이 될거다.
hashtags: "1,2,3,,5,46.,",
array
가 아니라 왜냐하면 프론트엔드에서 오는 hashtags
는 그냥 문자열일 뿐이기 때문이다.
새로고침 한다음 Upload Video
를 해주면 제대로 작동하고 그리고 보이듯이
hashtags
에는 당연히 아무런 처리가 되지 않았다. console
을 보면 hashtags
가 저장되는 법도 다르다.
이게 무슨 의미 냐면 만약에 해시태그로 string
을 입력한다면 Mongoose
가 자동으로
array
방식으로 변환 시킨다는 거다. 하지만 나뉘어져 있지는 않다.
단순히 입력된 문자열을 array
의 첫번째 element
로 지정하는 거다.
Mongoose
는 도와주려고 하는거고 에러도 없지만 schema
를 만들때 hashtags
는
string array
라고 했기 때문에 프론트엔드에서 보낸 정보가 그냥 통째로array
로 들어가게 된거다.
이전과는 완전히 다르다. 이전은 4개의 element
를 가진 array
였고
지금은 1개의 string element
만 가진 array
이다.
이제
object
가 데이터베이스에 어떻게 저장되는지 알았으니 바꾸고 싶은건
hashtags array
의 첫element
가 포맷되는 방식이다.
그걸 위해 이렇게 해본다.
videoSchema.pre("save", async function () {
this.hashtags = this.hashtags[0]
.split(",")
.map((word) => (word.startsWith("#") ? word : `#${word}`));
});
this.hashtags = this.hashtags[0]
이게 array
이니깐 여기서 첫번째 elemnet
를 뽑는거다.
이걸 콤마를 기준으로 나눌거고 그리고 map(word)
이전에 했던 거다.
hashtags
기호로 시작하면 word
를 return
할 텐데 아니라면 앞에 hashtags
를 붙여서 return
이다.
다시 말하자면 이렇게 하는 이유는 이렇게 하지 않으면 input
에 입력된 값이 하나의 element
로 array
에 입력 되기 때문이다.
그래서 첫번째 개체를 받은 다음에 #
을 붙이는 걸 하는게 이 부분이다.
그러기전에 일단 데이터베이스에 가서 db.videos.remove
를 해준다.
그러면 이제 아무 영상도 없게 된다. home
으로 가면 아무것도 없다.
이제 Upload Video
에서 제목, 설명, 해시태그들까지 입력하면 해시태그들을 보면 제대로 처리 되어 있다.
그럼 이제 mongodb
에서 videos
를 확인해 본다.
현재 단 하나뿐이 video object
가 있다. 해시태그를 보면 잘 적용 되어 있다.
보다시피 이게 pre middleware
를 save
이벤트에 적용시킨 결과이다.
videoSchema.pre("save", async function () {
this.hashtags = this.hashtags[0]
.split(",")
.map((word) => (word.startsWith("#") ? word : `#${word}`));
});
하지만 이건 update
시에는 아무런 도움도 되지 않는다.
그러니까 update
를 위한 middleware
가 하나 더 필요 하다.