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

이현정·2022년 4월 15일
1

🔖 강의 범위: 6.20~24

Preview

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

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

핵심 개념

Middleware 🌟

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

기타

  • join()
  • .startsWitdh("") ? :
  • 서버에서 데이터 불러오는 여러가지 mongoose 매서드들
    • .findOne(filter) vs. .findById(id) : 데이터 object 를 서버에서 불러온다. .findOne 은 다양한 조건을 내가 직접 만들 수 있고, .findById() 는 id 로 찾을 수 있다.
    • .exits( filter ) : 실제 object 를 서버에서 불러오는 대신 object 의 프로퍼티가 filter 에 적힌 조건식이 맞는지 아닌지 true / false 값을 리턴한다.
  • 데이터베이스에 전체 비디오 삭제 (2021.10.08 기준)
    db.videos.remove({})가 deprecated됐다고 뜨시는 분들은
    db.videos.deleteMany({})로 전체 비디오를 삭제하시면 됩니다.

강의 내용

step 1. getEdit 기능 만들기

// controllers/ videoController.js

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 });
}

step 2. postEdit 기능 만들기

두 가지 방법이 있다:

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

2-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-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 (👉 먼저 읽어보고 아래 내용 진행하기)

step 3. 업데이트(수정) 전 확인하는 기능 추가

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

3-1 저장 전 확인하기: 몽구스의 middleware, schema.pre() 🌟

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

// models/Video.js

videoSchema.pre("save", async function () {
	해당 기능(여기선 save 기능) 을 사용하기 "전"(pre) 구현하고 싶은 기능 적기
});
// 예시
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(데이터베이스의 데이터) 에 접근할 수 없기 때문이다.
    => 이렇게 middleware(pre, post) 가 힘든 특정 매서드들을 위한 대안으로 몽구스의 statics 기능을 알아보자.

3-2. 저장 전 확인하기: 몽구스의 Static 🌟

.static데이터 형식 내에 자주 사용할 함수를 직접 정의할 수 도 하고 사용을 간단하게 해준다. (즉, 내가 원하는 함수를 직접 커스터마이징 하는 거랄까...)

기본적으로 그냥 함수 선언하고 import, export 해서 쓰는 원리이다. 단, 굳이 import, export 를 해주지 않아도 선언한 함수 이름만으로 사용 가능하다는 점에서 훨씬 유용하다.

사용법

  1. new mongoose.Schema()와 mongoose.model()사이에 아래와 같이 써준다.
    자세한 문법은 사이트 참조.
    Schema이름.static("함수명", function (인자) { 함수 커스터마이징 } );
  2. 함수를 사용할 곳에 Model명.함수명(인자) 를 이용하여 사용해준다.
    끝.
// 예: static 선언 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 사용 in videoController.js

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

추가 공부 ?

  • code: 'BABEL_PARSE_ERROR', reasonCode: 'UnexpectedReservedWord' 해결하기
    : 보통 await 을 쓸 때 매개변수에 async 주는 것을 깜빡하면 이 에러가 떴다.

요약

  • 몽구스의 미들웨어와 스테틱스 기능에 대해서 알아보았다.
  • 미들웨어는 어떤 함수가 특정 기능을 하기 전(ex.db 에 정보 save) 발동되는 함수를 지정하는 것이고, 스테틱스는 데이터모델 내에서 사용 가능한 커스터마이징 함수 또는 그 함수를 만들 수 있는 기능이다.
  • 이 핵심 기능들과 지난 시간 배운 이론들을 합쳐 edit 페이지에서 새로운 수정사항을 POST하는 기능(ex. 해쉬태그 추가하기...) 을 만들어보았다.

0개의 댓글