TDL

Mongoose-Many To Many Relationship


BACKEND
NESTJS

Mongoose에서 Many-to-Many 관계 구현하는 방법

Mongoose에서 Many-to-Many(다대다) 관계를 구현하는 방식은 보통 두 개의 컬렉션을 ObjectId를 활용한 ref 방식으로 연결하거나, 중간 테이블(조인 테이블 역할)을 사용하여 구성합니다.

1. 직접 ObjectId 배열을 활용한 Many-to-Many 관계

가장 기본적인 Many-to-Many 관계는 각 모델에서 상대 모델의 ObjectId를 배열로 저장하는 방식입니다.

예제: User와 Group 간 Many-to-Many 관계

  • 하나의 유저(User)는 여러 그룹(Group)에 속할 수 있음.

  • 하나의 그룹(Group)도 여러 유저(User)를 포함할 수 있음.

✅ 장점: 간단한 구조로 쉽게 구현 가능

❌ 단점: User와 Group의 데이터를 동기화하려면 추가적인 코드가 필요 (데이터 정합성 문제 발생 가능)

const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema({
  name: String,
  email: String,
  groups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Group' }] // 여러 개의 그룹을 가질 수 있음
});

const GroupSchema = new mongoose.Schema({
  name: String,
  users: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }] // 여러 명의 유저를 포함할 수 있음
});

const User = mongoose.model('User', UserSchema);
const Group = mongoose.model('Group', GroupSchema);

데이터 저장 예제

async function createData() {
  const user1 = await User.create({ name: 'Alice', email: 'alice@example.com' });
  const user2 = await User.create({ name: 'Bob', email: 'bob@example.com' });

  const group1 = await Group.create({ name: 'Developers' });
  const group2 = await Group.create({ name: 'Designers' });

  // 관계 설정
  user1.groups.push(group1._id, group2._id);
  user2.groups.push(group1._id);

  group1.users.push(user1._id, user2._id);
  group2.users.push(user1._id);

  await user1.save();
  await user2.save();
  await group1.save();
  await group2.save();

  console.log('데이터 저장 완료');
}

Many-to-Many 관계 데이터 조회 (populate 활용)

async function getUserWithGroups(userId) {
  const user = await User.findById(userId).populate('groups');
  console.log(user);
}

async function getGroupWithUsers(groupId) {
  const group = await Group.findById(groupId).populate('users');
  console.log(group);
}

2. 중간 컬렉션(Join Table) 활용 (더 정교한 방식)

Mongoose에서 Many-to-Many 관계 구현하는 방법

Mongoose에서 Many-to-Many(다대다) 관계를 구현하는 방식은 보통 두 개의 컬렉션을 ObjectId를 활용한 ref 방식으로 연결하거나, **중간 테이블(조인 테이블 역할)**을 사용하여 구성합니다.

  1. 직접 ObjectId 배열을 활용한 Many-to-Many 관계

    가장 기본적인 Many-to-Many 관계는 각 모델에서 상대 모델의 ObjectId를 배열로 저장하는 방식입니다.

예제: User와 Group 간 Many-to-Many 관계

하나의 유저(User)는 여러 그룹(Group)에 속할 수 있음.

하나의 그룹(Group)도 여러 유저(User)를 포함할 수 있음.

javascript

복사

편집

const mongoose = require('mongoose');


const UserSchema = new mongoose.Schema({

name: String,

email: String,

groups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Group' }] // 여러 개의 그룹을 가질 수 있음

});


const GroupSchema = new mongoose.Schema({

name: String,

users: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }] // 여러 명의 유저를 포함할 수 있음

});


const User = mongoose.model('User', UserSchema);

const Group = mongoose.model('Group', GroupSchema);

데이터 저장 예제

javascript

복사

편집

async function createData() {

const user1 = await User.create({ name: 'Alice', email: 'alice@example.com' });

const user2 = await User.create({ name: 'Bob', email: 'bob@example.com' });


const group1 = await Group.create({ name: 'Developers' });

const group2 = await Group.create({ name: 'Designers' });


// 관계 설정

user1.groups.push(group1._id, group2._id);

user2.groups.push(group1._id);


group1.users.push(user1._id, user2._id);

group2.users.push(user1._id);


await user1.save();

await user2.save();

await group1.save();

await group2.save();


console.log('데이터 저장 완료');

}

Many-to-Many 관계 데이터 조회 (populate 활용)

javascript

복사

편집

async function getUserWithGroups(userId) {

const user = await User.findById(userId).populate('groups');

console.log(user);

}


async function getGroupWithUsers(groupId) {

const group = await Group.findById(groupId).populate('users');

console.log(group);

}

✅ 장점: 간단한 구조로 쉽게 구현 가능

❌ 단점: User와 Group의 데이터를 동기화하려면 추가적인 코드가 필요 (데이터 정합성 문제 발생 가능)

2. 중간 컬렉션(Join Table) 활용 (더 정교한 방식)

  • 위 방식은 간단하지만, 추가 정보를 저장할 수 없고 데이터 정합성을 유지하기 어렵다는 단점이 있습니다.

  • 이를 해결하기 위해 중간 컬렉션(Join Table 역할)을 사용하여 다대다 관계를 명확하게 정의할 수 있습니다.

✅ 장점: 추가 정보를 저장 가능, 데이터 정합성 유지

❌ 단점: populate() 쿼리가 많아질 경우 성능 저하 가능

예제: User와 Group을 연결하는 Membership 컬렉션 추가

  • Membership 모델을 추가하여 중간 테이블 역할을 수행

  • Membership은 User와 Group을 참조하면서 가입 날짜 등의 추가 정보를 저장 가능

const MembershipSchema = new mongoose.Schema({
  user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
  group: { type: mongoose.Schema.Types.ObjectId, ref: 'Group' },
  joinedAt: { type: Date, default: Date.now } // 가입 날짜 추가
});

const Membership = mongoose.model('Membership', MembershipSchema);

데이터 저장 예제

async function createMembership() {
  const user = await User.create({ name: 'Charlie', email: 'charlie@example.com' });
  const group = await Group.create({ name: 'Frontend Engineers' });

  // Membership 생성
  await Membership.create({ user: user._id, group: group._id });

  console.log('Membership 관계 저장 완료');
}


Many-to-Many 관계 조회

async function getUserGroups(userId) {
  const memberships = await Membership.find({ user: userId }).populate('group');
  console.log(memberships);
}

async function getGroupUsers(groupId) {
  const memberships = await Membership.find({ group: groupId }).populate('user');
  console.log(memberships);
}