이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

게시글

강좌14 - MongoDB - 6달 전 등록 / 5달 전 수정

Mongoose(몽구스) populate

조회수:
0
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

안녕하세요. 이번 시간에는 몽구스의 편리한 기능 중 하나인 populate에 대해 알아보겠습니다.

몽고DB를 사용하다보면 하나의 다큐먼트가 다른 다큐먼트의 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' }
}

이렇게 세 명의 친구가 있다고 칩시다. 친구와 베프 관계가 위와 같이 나와 있습니다. 만약 zero를 불러온다면

db.users.findOne({ name: 'zero' })

이렇게 해야 합니다. 결과는 당연히 아래와 같겠죠.

{
  _id: { $oid: '574e9c0f9f663a1700fbe06e' },
  name: 'zero',
  friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e8f46c9100617001c9cb9' }],
  bestFriend: { $oid: '574e8f46c9100617001c9cb9' }
}

하지만 friends랑 bestFriend 부분이 ObjectId라서 이게 누군지 쉽게 알 수 없습니다. 각각 조회를 해서 합쳐주어야 합니다.

이를 편하게 해주는 기능이 populate입니다. 먼저 schema의 필드가 다음과 같이 되어있어야 합니다.

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가 속해있는 모델을 넣어줍니다. 현재는 자기 자신(User)을 가리키지만 다른 컬렉션 모델이어도 상관없습니다.

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

이렇게 하면 data가

{
  _id: { $oid: '574e9c0f9f663a1700fbe06e' },
  name: 'zero',
  friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e8f46c9100617001c9cb9' }],
  bestFriend: { 
    _id: { $oid: '574e8f46c9100617001c9cb9' },
    name: 'hero',
    friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e9c0f9f663a1700fbe06e' }],
    bestFriend: { $oid: '59a66f8372262500184b5363' }
  }
}

위와 같이 됩니다. bestFriend 부분의 ObjectId가 실제 객체로 치환되었습니다. populate할 때 bestFriend의 name과 bestFriend만 보고 싶다면

User.findOne({ name: 'zero' }).populate('bestFriend', 'name bestFriend').exec((err, data) => {
  console.log(data);
});

populate의 두 번째 인자로 넣어주면 됩니다. 프로젝션이라고 생각하시면 되겠습니다.

{
  _id: { $oid: '574e9c0f9f663a1700fbe06e' },
  name: 'zero',
  friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e8f46c9100617001c9cb9' }],
  bestFriend: {
    _id: { $oid: '574e8f46c9100617001c9cb9' },
    name: 'hero',
    bestFriend: { $oid: '59a66f8372262500184b5363' }
  }
}

한 번에 friends와 bestFriend 모두 치환하는 것도 가능합니다. populate를 연달아 쓰면 됩니다.

User.findOne({ name: 'zero' }).populate('bestFriend').populate('friends').exec((err, data) => {
  console.log(data);
});
{
  _id: { $oid: '574e9c0f9f663a1700fbe06e' },
  name: 'zero',
  friends: [{
    _id: { $oid: '59a66f8372262500184b5363' },
    name: 'nero',
    friends: [{ $oid: '574e9c0f9f663a1700fbe06e' }, { $oid: '574e8f46c9100617001c9cb9' }],
    bestFriend: { $oid: '574e9c0f9f663a1700fbe06e' }
  }, {
    _id: { $oid: '574e8f46c9100617001c9cb9' },
    name: 'hero',
    bestFriend: { $oid: '59a66f8372262500184b5363' }
  }],
  bestFriend: {
    _id: { $oid: '574e8f46c9100617001c9cb9' },
    name: 'hero',
    bestFriend: { $oid: '59a66f8372262500184b5363' }
  }
}

모든 $oid가 실제 다큐먼트로 바뀌었습니다. 여기서 끝이 아닙니다. 위의 결과를 보시면 populate한 객체 안에 $oid가 들어있습니다. 이것도 바꿀 수 있습니다. populate를 중첩한다고 표현합니다. bestFriend의 bestFriend를 찾아보겠습니다. 아래와 같이 옵션을 주면 됩니다. 참고로 select 속성은 프로젝션 필드입니다.

User.findOne({ name: 'zero' }).populate({
  path: 'bestFriend',
  populate: { path: 'bestFriend', select: 'name friends' },
}).exec((err, data) => {
  console.log(data);
});
{
  _id: { $oid: '574e9c0f9f663a1700fbe06e' },
  name: 'zero',
  friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e8f46c9100617001c9cb9' }],
  bestFriend: {
    _id: { $oid: '574e8f46c9100617001c9cb9' },
    name: 'hero',
    friends: [{ $oid: '59a66f8372262500184b5363' }, { $oid: '574e9c0f9f663a1700fbe06e' }],
    bestFriend: {
      _id: { $oid: '59a66f8372262500184b5363' },
      name: 'nero',
      bestFriend: { $oid: '574e9c0f9f663a1700fbe06e' }
    }
  }
}

zero의 bestFriend로 hero, hero의 bestFriend로 nero가 생성되었습니다. 정말 편하죠?

하지만 populate를 맹신해서는 안 됩니다. populate는 $oid로 모두 조회를 해서 자바스크립트 단에서 합쳐주는 것이지 JOIN처럼 DB 자체에서 합쳐주는 것이 아닙니다. 따라서 성능이 그렇게 좋지는 않습니다. 특히 populate가 중첩되면 성능 문제가 생길 확률이 커집니다.

투표로 게시글에 관해 피드백을 해주시면 많은 도움이 됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright © 2016- 무단 전재 및 재배포 금지

댓글

2개의 댓글이 있습니다.
2달 전
감사합니다. 좋은 정보 잘 배웠습니다.
6달 전
정말 감사합니다 이런 기능이 있는지 모르고 사용하고 있었네요