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

게시글

강좌14 - MongoDB - 일 년 전 등록

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- 무단 전재 및 재배포 금지

댓글

3개의 댓글이 있습니다.
4달 전
정말 좋은 정보 감사드립니다.
populate를 사용해서 데이터를 가져오는 것에는 성공했는데,
그 데이터를 웹에 보내서 웹에서 console.log()를 해 보면 undefind로 나오네요
어떻게 해야 populate된 객체의 값을 보낼 수 있을까요??
4달 전
서버에서 콘솔로그찍으면 되는데 웹에서는 안된다는 거죠? 데이터를 웹에 보낼때 json으로 보내는 게 아닌건가요?
7달 전
감사합니다. 좋은 정보 잘 배웠습니다.
일 년 전
정말 감사합니다 이런 기능이 있는지 모르고 사용하고 있었네요