본문으로 바로가기

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

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

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

 

 

지금까지 mongoose를 이용하는 데에 있어 다음과 같이 스키마를 정의하고, 모델을 만든 다음 export하여 사용했을 것이다.

 

저장하는 값에 조작(해시 암호화, 난독화 등등...)은 node의 컨트롤러 단에서 처리했을 것인데 사실 mongoose를 이용하여 먼저 전에 처리할 수도 있다. 왜 이렇게 하냐면 라우터, 컨트롤러 단을 깔끔하게 처리하여 코드의 가독성을 높일 수 있기 때문이다.

// 지금까지의 사용법

const mongoose = require("mongoose");

const userSchema = mongoose.Schema({
  name: {
    type: String,
    maxlength: 50,
  }
});

const User = mongoose.model("User", userSchema);

module.exports = { User };

 

 

 - schema.pre, isModified (미들웨어)

 

스키마 클래스에 있는 pre 메서드를 활용하여 비밀번호를 암호화한 후 넘겨보자. (https://www.npmjs.com/package/bcrypt)

 

pre('save', ...)는 save 메서드 이전에 처리할 로직을 지정한다.

이를 pre hook이라고 부른다. 갈고리로 낚아채듯. 미들웨어라고 몽구스에서 안내하고 있다.

 

MovieSchema.pre("findOneAndUpdate", function(next) {
  // 여기서 this는 findOneAndUpdate 메서드를 실행한 객체겠죠
  // 혼동을 피해 const image = this; 라고도 많이 쓰는데 전 그냥 this를 쓰겠습니다.
  
  if (this._update.title.length < 3) {
    throw Error;
  }

  next();
});

 

const mongoose = require("mongoose");
const bcrypt = require("bcrypt");

const saltRounds = 10;

const userSchema = mongoose.Schema({
  name: {
    type: String,
    maxlength: 50,
  },
  password: {
    type: String,
    minlength: 5,
  },
});

// save 전에 (pre) 처리할 로직
userSchema.pre("save", function (next) {
  // 여기서 this를 사용합니다. 당연히 this는 save를 적용한 구체적인 객체일 것입니다.
  // user.save()라면 this는 user겠죠
  
  // 사진, 이메일 변경에는 비밀번호 난독화를 할 필요 없으므로 비밀번호 수정에만 해쉬화.
  if (this.isModified("password")) {
    bcrypt.genSalt(saltRounds, function (err, salt) {
      if (err) return next(err);

      bcrypt.hash(this.password, salt, function (err, hashedPassword) {
        if (err) return next(err);
        this.password = hashedPassword;
        next();
      });
    });
  }
  else {
    next();
  }
});

const User = mongoose.model("User", userSchema);

module.exports = { User };

 

 

- schema.methods (인스턴스 메서드)

 

찾아보니 mongoose quick start에서도 소개되어 있더군요.

// NOTE: methods must be added to the schema before compiling it with mongoose.model()
kittySchema.methods.speak = function () {
  const greeting = this.name
    ? "Meow name is " + this.name
    : "I don't have a name";
  console.log(greeting);
}

const Kitten = mongoose.model('Kitten', kittySchema);
const fluffy = new Kitten({ name: 'fluffy' });
fluffy.speak(); // "Meow name is fluffy"

 

 

주의할점은 this를 통해 해당 메서드를 불러온 객체의 값을 이용해야 하는데 화살표 함수를 사용하게 되면 lexical this를 사용하게 되어 해당 메서드를 이용하는데 불편하게 됩니다. 그러니까 화살표 함수를 사용하지 맙시다.

 

  • Do not declare methods using ES6 arrow functions (=>). Arrow functions explicitly prevent binding this, so your method will not have access to the document and the above examples will not work.

 

모델 인스턴스에서 사용할 메서드를 model 단에서 정의할 수 있습니다. return으로 콜백함수를 돌려줘서 다른 곳에서 콜백 형태로 사용할 수 있게 합시다.

userSchema.methods.comparePassword = function (plainPassword, cb) {
  // 입력한 값이 암호화된 값과 같은가?
  bcrypt.compare(plainPassword, this.password, function (err, isMatch) {
    if (err) return cb(err);
    return cb(null, isMatch);
  });
};


// user = New User와 같이 인스턴스 생성 후 메서드를 사용해보았습니다.
user.comparePassword(req.body.password, (err, isMatch) => {
  if (!isMatch) {
    return res.json({
      loginSuccess: false,
      message: "비밀번호가 일치하지 않습니다",
    });
  }

 

 

- schema.statics  (클래스 스태틱 메서드)

 

userSchema.statics.findbyToken = function (token, cb) {
  const user = this;
  // 토큰 복호화 및 복호화한 토큰 값으로 _id를 넣어 두었으니 해당 값이 일치하는 유저 찾기
  jwt.verify(token, "shhh", function (err, decoded) {
    user.findOne({ _id: decoded.id, token: token }, (err, user) => {
      if (err) return cb(err);
      cb(null, user);
    });
  });
};


// statics로 정의했으므로 클래스에서 곧장 써야 한다
// statics로 정의한 메서드들이 그러하듯 인스턴스를 생성한 후에 사용하려고 하면 안된다.

User.findbyToken(token, (err, user) => {
  if (err) throw err;
  // 유저가 없으면 팅겨내기
  
  if (!user) return res.json({ isAuth: false, error: true });
  // db에 유저가 있으면 인증 통과, 다음 단에 토큰과 유저 정보 넘겨주기
  req.token = token;
  req.user = user;
  next();
});

 

여기도 마찬가지로 this를 통해 해당 메서드를 불러온 객체의 값을 이용해야 하는데 화살표 함수를 사용하게 되면 lexical this를 사용하게 되어 해당 메서드를 이용하는데 불편하게 됩니다. 그러니까 화살표 함수를 사용하지 맙시다.

 

  • Do not declare methods using ES6 arrow functions (=>). Arrow functions explicitly prevent binding this, so your method will not have access to the document and the above examples will not work.

 


darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체