본문 바로가기
개발자 도전기/[STUDY] DATABASE

mongoDB | node.js | mongoose | populate()로 여러 컬렉션 사용하기

by 답수 2023. 1. 10.
728x90
반응형

몽고디비는 관계형 데이터베이스가 아니기 때문에 테이블들을 join한다는 개념이 없다. 대신 aggregate의 $lookup 연산자를 이용하여 컬렉션들을 조인할 수 있고, 이외에 mongoose의 populate()를 이용하는 방법도 있다.

 

Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s). We may populate a single document, multiple documents, a plain object, multiple plain objects, or all objects returned from a query.

 

Mongoose 공식 문서에 의하면, population이란 도큐먼트에 지정된 경로를 다른 컬렉션의 도큐먼트를 채우는 기능을 말한다. 즉 다른 컬렉션의 도큐먼트를 참조하여 그 도큐먼트의 데이터를 가져오는 기능을 한다.

다음 예시를 들어보자.

const personSchema = new mongoose.Schema({
  name: String,
  age: Number,
  pets: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: "Pet"
  }]
});

const petSchema = new mongoose.schema({
  name: String,
  type: String
});

const Person = mongoose.model("Person", personSchema);
const Pet = mongoose.model("Pet", petSchema);

populate() 메서드는 mongoDB의 ref속성을 사용하여 다른 도큐먼트와 연결을 지정할 수 있다. 위의 코드를 보면 ref를 통해 Person 모델과 Pet 모델 사이의 참조 관계를 지정한다.

이후 Person 모델에서 Pets 필드를 채우기 위해 populate() 메서드를 사용할 수 있다.

Person.findOne({
  name: "John"
}).populate("Pets")
  .exec({error) => {
    console.log(person.pets);
  });

즉 populate() 메서드는 ref를 통해 연결된 도큐먼트를 채우는 데 사용된다. 일반적으로 스키마의 각 필드는 참조 관계가 있는 다른 도큐먼트의 ObjectId를 저장한다.

 

그런데 일하면서 뭔가 난감한 코드를 발견했다. 어떤 컬렉션의 정적 메서드에서 populate 메서드를 이용하는데, ref 옵션을 사용하지 않음에도 불구하고 정상적으로 작동이 된다는 것이다.

 

코드 예시는 다음과 같다.

const userSchema = new Schema(
  {
    email: { type: String, required: true, unique: true },
    password: { type: String },
  },
);

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

const A_Schema = new Schema(
  {
    name: { type: String, required: true },
    master: { type: Types.ObjectId, required: true, unique: true },
    members: { type: [memberSchema], default: () => [] },
  },
  { strict: true, versionKey: false }
);

A_Schema.statics.findGroupUserBelongs = function (userId) {
  return this.findOne({
    members: { $elemMatch: { user: userId } },
  }).populate({ path: "master", model: "user", select: "email" });
};

 

user 스키마를 보면, A_스키마를 참조하는 ref가 설정되어 있지 않고, A_스키마에도 역시 user스키마를 참조하는 속성이 없다. 그럼에도 이는 정상적으로 작동한다.

 

populate() 메서드를 사용할 때, path옵션을 사용하면 A_ 스키마의 members 필드에 ref 속성을 사용하지 않아도 정상적으로 작동한다. 이는 A_ 스키마의 members 필드와 user 스키마 사이의 참조관계가 path나 model로 선언되면서 이미 참조 관계가 정의되었기 때문이다.

 

하지만 ref 속성을 사용하지 않으면 mongoose는 해당 필드에 저장된 값이 어떤 문서와 연결되는지 모르기 때문에 문제가 발생할 수 있다고 한다. 즉 path나 model 옵션을 사용하면 ref 속성이 사용되지 않아도 괜찮지만, 일반적으로는 스키마에서 ref를 사용하여 필드가 어떤 모델과 연결되어 있는지 지정하는 것이 좋다고 한다.

728x90
반응형
LIST

댓글