What is mongoose?

mongoose in action

mongoose가 가진 가장 큰 장점은, 기존에 MongoDB로 제작할 때 작성했던 boilerplate들을 모두 mongoose가 해결해준다는 점이다.

아래와 같이 모델을 정의해보자.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const productSchema = new Schema({
  title: {
    type: String,
    required: true,
    minlength: 1,
    maxlength: 100
  },
  price: {
    type: Number,
    required: true,
    min: 0
  },
  description: {
    type: String,
    required: true
  },
  imageUrl: String
});

module.exports = mongoose.model('Product', productSchema);

윗 모델은, 보기와 같이 Validation 과정이 같이 포함되어 있다. type 필드 지정 뿐만 아니라, 별도로 requiredmin / max와 같은 내용까지 지정할 수 있다.

나아가, 기존의 controller 부분을 아래와 같이 보완할 수 있다.

exports.postEditProduct = (req, res, next) => {
  const prodId = req.body.productId;
  const updatedTitle = req.body.title;
  const updatedPrice = req.body.price;
  const updatedImageUrl = req.body.imageUrl;
  const updatedDesc = req.body.description;

  Product.findById(prodId)
    .then(product => {
      /**
       * By using mongoose, saving an existing document
       * will not trigger creating a new one.
       * Instead, it will update the document.
       *
       */
      product.title = updatedTitle;
      product.price = updatedPrice;
      product.description = updatedDesc;
      product.imageUrl = updatedImageUrl;
      return product.save();
    })
    .then(result => {
      console.log('UPDATED PRODUCT!');
      res.redirect('/admin/products');
    })
    .catch(err => console.log(err));
};

edit을 할 경우에도, 기존의 MongoDB 구현 방식과는 조금 다른게, 기존의 MongoDB를 사용할 때는, Product 클래스의 _id 변수에 값이 할당되어 있는지를 확인하여 edit과 insert 유무를 결정하였다.

mongoose의 경우, Cursor가 아닌 Query 결과물을 반환하기 때문에, 바로 사용할 수 있다.

위와 같이 productsave()를 바로 호출할 수 있다. 이 경우, 새로운 것을 만드는게 아니라, 기존의 값을 수정한다.

Populate

exports.getProducts = (req, res, next) => {
  Product.find()
    .populate('userId')
    .then(products => {
      // ...do something
    });
};

MongoDB의 $lookup과 같은 join 명령을 수행하는 집합 연산자와 유사하지만, 훨씬 더 강력하다고 볼 수 있다. 다른 컬렉션에 존재하는 도큐먼트를 레퍼런스 할 수 있도록 해준다.

위 예제의 경우, Product 모델에 존재하는 userId 필드는 User 모델을 레퍼런스 하도록 설정되어 있다. populate() 메소드를 호출함으로써, 단순히 userId에 해당하는 값만 호출하는 것이 아니라, 해당 userId를 갖는 User 모델의 도큐먼트를 반환한다.