유뷰트 클로닝 #6 CRUD (2): UPLOAD(EDIT)

7323 단어 CRUDyoutube강의CRUD

🔖 강의 범위: 6.20~24

Preview

지난 시간에는 mongoose.Schema, mongoose.Model, mongoose.find, mongoose.findById 등의 다양한 몽구스의 매서드를 이용해 데이터를 생성하고 서버에 저장하며, 다시 저장한 데이터를 불러오는 CRUD 의 C,R 부분을 공부해보았다. 이 과정에서 callback 함수와 promise 구문에 대해서도 알게 되었다.

watch 페이지를 만들 C,R 기능 만드는 법을 공부했으니,
edit 페이지를 만들며 U(update) 기능 넣는 법을 배워보자.

강의 내용

1. getEdit

getEdit 페이지를 만들어보자.

export const getEdit = async (req, res) => {
  const { id } = req.params; // const id = req.params.id; 의 es6 버전.
  const video = await Video.findById(id);
  if (!video) {
    return res.render("404", { pageTitle : "404, Not Found"});
  } 
  return res.render("edit", { pageTitle : `Edit ${video.title}`, video });
}

2. postEdit

postEdit 페이지를 만들어 데이터 변경사항을 수정해보자.
두 가지 방법이 있다.

1) 직접 데이터베이스에서 저장된 값과 req.body 에 전송된 값을 불러와 일일이 수정하는 방법
2) .findByIdAndUpadte( id, { 수정할거 : 수정사항 } ) 몽구스 메서드를 이용하는 방법

2.1.1 첫 번째 방법: 직접 수정하기 + .save()

 // 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.startsWith("#") ? word : `#${word}`));
 await video.save();
 return res.redirect(`/videos/${id}`);
};
  • 일일이 데이터의 title, description, hashtags 를 req.body 에서 받아온 값으로 수정한뒤,
  • video.save() 로 마무리 해주고 있다.

2.1.2 두 번째 방법: 몽구스의 함수 이용하기 : .findByIdAndUpadte() 🌟

// 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." });
 }
 await Video.findByIdAndUpdate(id, {
   title,
   description,
   hashtags: hashtags
     .split(",")
     .map((word) => (word.startsWith("#") ? word : `#${word}`)),
 });
 return res.redirect(`/videos/${id}`);
};
  • 이 방법을 사용하는 경우, .save() 기능을 따로 넣지 않은 걸 확인할 수 있다. .findByIdAndUpdate() 가 자동으로 서버에 수정된 정보를 저장하기 때문이다.
  • 편리하기도 하지만, 저장 전에 그 정보가 맞는지 아닌지 확인할 기능을 입력하기 위해서는 => middleware 사용이 불가피하다. 몽구스에서 지원하는 middleware 기능을 알아보자.
    몽구스의 미들웨어 공식 doc: https://mongoosejs.com/docs/middleware.html#middleware (👉 먼저 읽어보고 아래 내용 진행하기)

2.2.0 저장 전 확인하는 기능 추가

2가지 방법이 있다.
1) 몽구스의 middleware(미들웨어) 기능 사용
2) 몽구스의 static(정적인) 기능 사용

2.2.1 저장 전 확인하기: 몽구스의 middleware, schema.pre()

미들웨어는 데이터모델을 만든 파일에서 생성해주며, 반드시 데이터모델을 생성하기 "전"에 생성 해야 한다.

//Video.js

videoSchema.pre("save", async function () {
	해당 기능(여기선 save 기능) 을 사용하기 "전"(pre) 구현하고 싶은 기능 적기
});
//Video.js

videoSchema.pre('save', async function () {
    this.hashtags = this.hashtags[0]
    .split(",")
    .map((word) => (word.startsWith("#") ? word : `#${word}`));
});
  • 이렇게 "저장", 즉 form 에서 submit 후 각종 정보들이 constroller 에 설정한대로 설정되고 데베에 "저장" 되기 직전에, 위와 같은 미들웨어가 끼어들어 자기 역할을 실행하게 될 것이다.
  • 문제는, 똑같이 form submit 이더라도 upload 시, 즉 Model.create() 시에는 이러한 미들웨어가 먹히는 반면, edit 화면, 즉 Model.findByIdAndUpadate() 에선 해당 미들웨어가 작동하지 않는 걸 확인할 수 있을 것이다.
    => 그 이유는 Model.findByIdAndUpadate() 는 4가지 미들웨어 중 쿼리 미들웨어인 .findOneAndUpdate() 를 불러오는데, 이 .findOneAndUpdate() 가 document 에 접근할 수 없기 때문이다.

2.2.2 저장 전 확인하기: 몽구스의 Static

.static 은 데이터 형식 내에 내가 직접 자주 사용할 함수도 선언하고 간단하게 사용할 수 있게 해준다.
기본적으로 그냥 함수 선언하고 import, export 해서 쓰는 원리이다. 단, 굳이 import, export 를 해주지 않아도 선언한 함수 이름만으로 사용 가능하다는 점에서 훨씬 유용하다.

사용법
1) new mongoose.Schema() 지정 후, mongoose.model() 지정 전 사이 아래와 같이 써준다. 자세한 문법은 사이트 참조.

Schema이름.static("함수명", function (인자) { 함수 커스터마이징 } );

2) 함수를 사용할 곳에 Model명.함수명(인자) 를 이용하여 사용해준다. 끝.

// 예를 들면,

//  static function 선언 in Video.js

const videoSchema = new mongoose.Schema({
    title: { type: String, required: true, uppercase: true },
    description: { type: String, required: true },
    createdAt: { type: Date, required:true , default: Date.now },
    hashtags: [{type:String, trim:true}],
    meta: {
        views: { type: Number, default: 0 },
        rating: { type: Number, default: 0 },
    },
});

videoSchema.static("formatHashtags", function (hashtags) {
    return hashtags
    .split(",")
    .map((word) => word.startsWith("#") ? word : `#${word}`)
});

const Video = mongoose.model("Video", videoSchema);
export default Video;


// static function 사용 in videoController.js

await Video.create ({
      title,
      description,
      hashtags: Video.formatHashtags(hashtags),
});

추가 공부 ?

  • join()
  • code: 'BABEL_PARSE_ERROR', reasonCode: 'UnexpectedReservedWord' 해결하기
    : 보통 await 을 쓸 때 매개변수에 async 주는 것을 깜빡하면 이 에러가 떴다.
  • .startsWitdh("") ? :
  • 서버에서 데이터 불러오는 여러가지 mongoose 매서드들
    • .findOne(filter) vs. .findById(id) : 데이터 object 를 서버에서 불러온다. .findOne 은 다양한 조건을 내가 직접 만들 수 있고, .findById() 는 id 로 찾을 수 있다.
    • .exits( filter ) : 실제 object 를 서버에서 불러오는 대신 object 의 프로퍼티가 filter 에 적힌 조건식이 맞는지 아닌지 true / false 값을 리턴한다.
  • Middleware

미들웨어(pre또는 post훅이라고도 불림)는 비동기 함수를 실행하는 동안 제어가 전달되는 함수입니다.
몽구스는 document middleware, model middleware, aggregate middleware, query middleware 4가지 미들웨어가 있습니다.
https://mongoosejs.com/docs/middleware.html#middleware

model middleware가 지원하는 기능
document middleware함수에서 this는 현재 document를 참조합니다. https://mongoosejs.com/docs/middleware.html#types-of-middleware

데이터베이스에 전체 비디오 삭제 (2021.10.08 기준)
db.videos.remove({})가 deprecated됐다고 뜨시는 분들은
db.videos.deleteMany({})로 전체 비디오를 삭제하시면 됩니다.

Pre
주의! pre안에 콜백함수로 화살표 함수 쓰게 되면 this의 대상이 달라지기 때문에 function(){}으로 써야합니다.

pre("save", async () => {}); (X)

https://mongoosejs.com/docs/middleware.html#pre

Statics
모델에 static 함수를 추가할 수도 있습니다.
스키마에서 컴파일된 모델에 정적 "class" 메서드를 추가합니다.

Static 사용하는 두 가지 방법

// Assign a function to the "statics" object of our animalSchema
animalSchema.statics.findByName = function(name) {
return this.find({ name: new RegExp(name, 'i') });
};
// Or, equivalently, you can call `animalSchema.static()`.
animalSchema.static('findByBreed', function(breed) { return this.find({ breed }); });

https://mongoosejs.com/docs/guide.html#statics

요약

좋은 웹페이지 즐겨찾기