Skip to content

Mongoose

박재윤 edited this page Nov 20, 2020 · 1 revision

몽구스

MongoDB ODM 중 가장 유명한 ODM이다. document를 DB에서 조회할 때 자바스크립트 객체로 바꿔주는 역할을 한다.

설치

npm i mongoose

몽구스의 장점

  • 강제 schema
  • populate
  • promise 사용 가능
  • query builder

몽구스 스키마

몽고디비에서 document에 아무 값이나 넣어도 문제가 생기지 않는데 이것이 편해보이지만 실제로 개발할 때 문제가 생기는 경우가 많다. 이러한 문제를 해결하기 위해 몽구스에서 스키마를 도입했다.

몽구스는 작성한 스키마를 기준으로 데이터를 먼저 검사한다. 스키마에 어긋나는 데이터가 있으면 에러를 발생시킨다. 스키마를 설정할 때 인덱스까지 생성할 수 있다.

스키마 생성

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true, lowercase: true },
  password: { type: String, required: true, trim: true },
  nickname: String, // 다른 옵션이 없다면 그냥 type을 적어줘도 된다.
  birth: { type: Date, default: Date.now },
  point: { type: Number, default: 0, max: 50},
  image: imageSchema,
  likes: [String],
  any: [mongoose.Schema.Types.Mixed ],
  id: mongoose.Schema.Types.ObjectId,
});
userSchema.index({ email: 1, nickname: 1 });

module.exports = mongoose.model('User', userSchema);

옵션

공식 문서

type

type을 지정한다.

required

필수적인 property인지를 나타낸다.

unique

유일한 값인지를 나타낸다.

lowercase

소문자만 들어간다는 것을 나타낸다.

default

기본값을 지정할 수 있다.

trim

공백을 제거한다.

min, max

최소값과 최대값을 지정한다.

validate

스키마의 한 property 값을 검증한다.

const UserSchema = new mongoose.Schema({
  (...생략...)
  age: {
    type: Number,
    validate(value) {
      if (value < 0) {
        throw new Error("Age must be a postive number");
      }
    },
  },
  (...생략...)

validator library도 있어서 라이브러리를 사용할 수도 있다. validator.js

사용자 정의 메소드

스키마에 사용자 정의 메소드를 붙이면 document에서 사용자 정의 메소드를 사용할 수 있다.

userSchema.methods.comparePassword = function(pw ,cb)  {
  if (this.password === pw) {
    cb(null, true);
  } else {
    cb('password 불일치');
  }
};

// 나중에 user 다큐먼트를 받게 되면
user.comparePassword('비밀번호', function(err, result) {
  if (err) {
    throw err;
  }
  console.log(result);
});

pre, post 메소드

스키마에 붙여 특정 동작의 이전과 이후에 취할 행동을 정의할 수 있다.

// save 이전에 할 동작
userSchema.pre('save', function(next) {
  if (!this.email) { // email 필드가 없으면 에러 표시 후 저장 취소
    throw '이메일이 없습니다';
  }
  if (!this.createdAt) { // createdAt 필드가 없으면 추가
    this.createdAt = new Date();
  }
  next();
});

// find 이후에 할 동작
userSchema.post('find', function(result) {
  console.log('저장 완료', result);
});

model

몽구스 document는 몽고비디의 document와 1대1 매핑이 되어서 표현된다. 각 몽구스 document는 몽구스 model의 인스턴스이다.

mongoose.Modelmongoose.Document를 상속한다.

mongoose.model()

mongoose.model()의 첫번째 인자는 해당 collection을 단수로 나타내는 문자열이다.

몽구스는 첫번째 인자로 컬렉션 이름을 만든다. User이면 소문자화를 한 뒤에 복수형으로 바꿔서 users 컬렉션이 된다. collection의 이름을 명시적으로 지정하고자 할 때는 schema 생성시 option을 지정한다.

모델로 document 생성

모델로 document 인스턴스를 생성한다.

const Tank = mongoose.model('Tank', yourSchema);

const small = new Tank({ size: 'small' });
//small document

메소드

공식문서

  • find, findOne, findById
  • save: 최종적으로 컬렉션에 저장하려면 save 메소드를 호출한다.
  • create: 모델 인스턴스, 즉, document를 생성하지 않고 바로 추가한다.
  • remove

몽구스 sub document

스키마를 다른 스키마에 중첩할 수 있다.

const childSchema = new Schema({ name: 'string' });

const parentSchema = new Schema({
  // Array of subdocuments
  children: [childSchema],
  // Single nested subdocuments. Caveat: single nested subdocs only work
  // in mongoose >= 4.2.0
  child: childSchema
});

populate

몽고디비를 사용하다보면 하나의 document가 다른 document의 ObjectId를 쓰는 경우가 있다. 그럴 때 ObjectId를 실제 객체로 치환하는 작업이 필요하다.

만약 user 컬렉션이 다음과 같으면,

{
  _id: { $oid: '574e9c0f9f663a1700fbe06e' },
  name: 'zero',
  friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e8f46c9100617001c9cb9' }],
  bestFriend: { $oid: '574e8f46c9100617001c9cb9' }
}
{
  _id: { $oid: '574e8f46c9100617001c9cb9' },
  name: 'hero',
  friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e9c0f9f663a1700fbe06e' }],
  bestFriend: { $oid: '59a66f8372262500184b5363' } 
}
{
  _id: { $oid: '59a66f8372262500184b5363' },
  name: 'nero',
  friends: [{ $oid: '574e9c0f9f663a1700fbe06e' }, { $oid: '574e8f46c9100617001c9cb9' }],
  bestFriend: { $oid: '574e9c0f9f663a1700fbe06e' }
}
const userSchema = new mongoose.Schema({
  name: String,
  friends: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
  bestFriend: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
});
mongoose.model('User', userSchema);

ref에 해당 ObjectId가 속해있는 모델을 넣어준다. 그리고 document를 가져올 때 어느 부분을 실제 객체로 치환해서 가져올지 정한다.

User.findOne({ name: 'zero' }).populate('bestFriend').exec((err, data) => {
  console.log(data);
}); // 또는 populate({ path: 'bestFriend' })도 가능

populate가 DB 단에서 join을 해주는 것이 아니라 oid로 모두 조회를 해서 자바스크립트 단에서 합쳐주는 것이기 때문에 성능이 좋지 않다.

query builder

쉽게 쿼리를 만들어주는 역할을 한다.

메소드

  • where: 어떤 필드를 조작할지 정하는 쿼리
  • equals, ne
  • exists
  • gt, lt, gte, lte
  • in, nin, all
  • and, or, nor
  • size: 배열의 요소가 일치하는지 확인
  • mod: 나머지가 일치하는지 확인
  • slice: 배열의 특정 부분만 가져온다.
  • distinct: 지정한 필드 값에서 중복을 제거해서 가져온다.
  • regex: 정규식에 일치하는 것을 가져온다.
  • select: 특정 필드만 가져오는 기능
  • skip: 앞의 몇 개를 건너뛴다.
  • limit: 개수를 제한한다.
  • sort
Clone this wiki locally