혼자 적어보는 노트

[Node.js] sequelize 관계 설정(association)하기 / 1:1 / 1:M / N:M 본문

NodeJS

[Node.js] sequelize 관계 설정(association)하기 / 1:1 / 1:M / N:M

jinist 2022. 3. 17. 02:59

 

이전 포스팅에서 모델을 정의하고 MySQL을 연결하는 것 까지 진행했었다.

데이터를 연결하면서 관계설정에 대해 알게 되었는데 약간 개념이 헷갈리게되어

예시 상황을 만들고 정리해보면서 개념을 다시 제대로 이해해보기로 했다.

 

독립적으로 사용하는 데이터가 있는 반면

보통 연관성이 있는 데이터가 많다.

 

게시판을 구현하기 위한 간단한 User, Post, Comment 세 가지의 모델끼리만 해도

User은 자신이 등록한 게시물인 Post, 자신이 작성한 Comment의 정보를 가지고 있어야하고

Post는 게시물을 등록한 User과 포스트에 달린 Comment의 정보를 가지고 있어야한다.

또한 Comment는 댓글을 단 User와 어느 포스트에 달린 댓글인지에 대한 Post의 정보를 가지고 있어야한다. 

 

이렇게 연관성 있는 데이터끼리의 관계 설정(association)을 해주어야 한다.

 

assosiate

  Post.associate = (db) => {
  
  };

모델 객체에 associate 프로퍼티를 함수를 할당하고 함수 내부에 관계설정을 해주면 된다.

 

 

모델의 관계 설정 통합

models/index.js안에는 기본적으로 관계설정을 취합하는 코드가 있다.

models 폴더 안의 모델 파일들에 관계를 정의해 놓으면 index.js파일에서 관계를 통합하게된다.

 

models/index.js

Object.keys(db).forEach((modelName) => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

* 여기서 associate(db)를 호출하는데 이 db가 Post.associate =(db) => {} 여기의 db로 가게된다.

 


 

관계설정 

관계설정에는 1:1(일대일), 1:M(일대다), N:M(다대다)가 존재한다.

 


 

🙍‍♀️🙍‍♂️ 1:1(일대일)관계

예를들어 유저와 유저의 정보의 테이블이 있어서 1:1으로 매칭해야 하는 상황.

// User
  User.associate = (db) => {
    db.User.hasOne(db.UserInfo, {foreignKey: "userId"};
  };
// UserInfo
  UserInfo.associate = (db) => {
    db.UserInfo.belongsTo(db.User, {foreignKey: "userId"};
  };

User은 UserInfo를 하나 가질 수 있고 (hasOne)

Profile은 User에 속한다는 의미인 belongsTo()를 사용한다.

 

 


 

🙍‍♀️ 👨‍👩‍👧‍👧1:M(일대다) 관계

 

Post모델과 Comment 모델의 관계

 

한 게시글은 여러개의 댓글을 가질 수 있으며, 댓글은 한 게시글에 속해있다.

게시글과 댓글은 1 : M 관계이다.

 

 

models/post.js

module.exports = (sequelize, DataTypes) => {
  const Post = sequelize.define(
    "Post",
    {
      title:{
        type: DataTypes.STRING(50),
        allowNull: false,
      }
      content: {
        type: DataTypes.TEXT,
        allowNull: false,
      },
    },
  );
  Post.associate = (db) => {
    db.Post.hasMany(db.Comment);
  };
  return Post;
};

1:M 관계에서는 hasMany()를 사용하고 인자로는 M이되는 모델을 작성한다.

 

그리고 M이되는 Comment에서도 Post와의 관계를 매핑해주어야 한다.

 

models/comment.js

module.exports = (sequelize, DataTypes) => {
  const Comment = sequelize.define(
    "Comment",
    {
      content: {
        type: DataTypes.TEXT,
        allowNull: false,
      },
    },
  );
  Comment.associate = (db) => {
    db.Comment.belongsTo(db.Post, { foreignKey: "PostId"} );
  };
  return Comment;
};

여기서 Comment는 하나의 Post에 포함되어 있으며

이렇게 포함이 되어 있는 모델에는 belongsTo()를 사용한다.

 

Comment는 하나의 post안에 속해있어야 하는데

어떤 게시글(게시글이 여러개기 때문)에 포함되어 있는지 알아야 한다.

그래서 PostId라는 정보가 필요하다.

belongsTo를 사용하면 Comment의 테이블에 PostId라는 column이 생긴다.

 

* foreignkey는 새로 생성되는 column의 이름을 변경하는 속성이고

foreignkey를 생략해도 column이 생성되고 이름 또한 기본으로 PostId로 생성된다. 

 

 

위의 개념들을 토대로 User, Post, Comment의 associate를 작성해보자.

 

  // User
  User.associate = (db) => {
    db.User.hasMany(db.Post); // 여러개의 Post를 가짐
    db.User.hasMany(db.Comment); // 여러개의 Comment를 가짐
  };
  
  // Post
  Post.associate = (db) => {
    db.Post.belongsTo(db.User); // User에 속해 있음
    db.Post.hasMany(db.Comment); // 여러개의 Comment를 가짐
  };

  // Comment
  Comment.associate = (db) => {
    db.Comment.belongsTo(db.User); // User에 속해 있음
    db.Comment.belongsTo(db.Post); // Post에 속해 있음
  };

위와 같이 작성을 하여 관계 설정을 해주었다. *foreignKey는 생략

 

이렇게 되면 Post는 UserId를 가지고 있고

Comment는 UserId, PostId를 가지고 있게 된다.

 

 


 

👩‍👩‍👧‍👦👨‍👩‍👧‍👧 N:M(다대다) 관계

 

다대 다의 관계에서는 belongsToMany()를 사용한다.

belongsToMany()를 사용하면 각 테이블의 foreignKey를 공유하는 새로운 또 하나의 테이블이 생성된다.

 

예시 상황을 만들어보자

 

1. 다른 모델끼리의 N:M

: 예약시스템 중에서 하나의 유저가 여러 서비스를 예약할 수 있고

하나의 서비스가 여러 유저를 보유 할 수 있는 상황.

 

  User.associate = (db) => {
    db.User.belongsToMany(db.Service, {
      through: 'UserConcert',
      foreignKey: 'userId'
    });
  };

through는 새로 생성할 테이블의 이름을 설정할 수 있다.

 

  Service.associate = (db) => {
    db.Service.belongsToMany(db.User, {
      through: 'UserConcert',
      foreignKey: 'ServiceId'
    });
  };

위와 같이 두 모델 서로  belongsToMany(db.상대모델, {through: "중간테이블이름"}) 적어준다.

 

 

2. 같은 모델 안에서의 N:M

: 하나의 유저가 여러 유저를 팔로잉 하고 있고

하나의 유저가 여러 팔로워를 가지는 상황 (* 헷갈림주의)

 

  User.associate = (db) => {
    db.User.belongsToMany(db.User, {
      through: 'follow',
      as: 'followers'
      foreignKey: 'followingId'
    });
    db.User.belongsToMany(db.User, {
      through: 'follow',
      as: 'followings'
      foreignKey: 'followersId'
    });
  };

 

같은 모델 안에서 2개를 선언하는 방식이며

through 속성을 이용하여 새로생성되는table이름을 지정해주고

as속성을 같은 모델 안에서 구분을 할 수 있는 이름을 설정해준다.

foreignKey는 반대로 입력을 해준다.

 

jay가 hey와 bey를 팔로잉 하고 있다고 생각하고

테이블 구조를 그려보자 (hey와 bey는 jay 팔로잉 X)

               

user          followings     followers

1: jay             2              1

2: hey            3              1

3: Bey

 

여기서 jay가 팔로잉 하고 있는 사람을 찾으려면 follower를 먼저 찾아야 하는데

먼저 찾아야 하는것을 foreignKey로 설정해야한다.

 


 

📑 요약

 

1:1
hasOne(), belongsTo() 사용


1:M
hasMany(), belongsTo() 사용

 

N:M
연관된 모델 각각 belongsToMany() 사용.

foreignkey과 as는 반대로 입력

 

belongsTo() 사용 시 해당 모델에 Id column 생성
belongsToMany() 사용 시 연관 테이블 생성, Id column 생성

through: 새로운 테이블의 name 지정 생략 시 연관된 두 테이블이 합쳐진 이름이 생성된다. (예: UserPost)

* 같은 테이블 안에서의 새롭게 table을 생성했을 때 UserId와 UserId로 생성되기 때문에 구분을 위해 지정해야 한다.


foreignKey: 생성된 column의 name 지정, 생략 시 소속되는 모델명+Id가 생성된다. (ex: UserId)
as : 같은 모델 안에서 관계가 헷갈릴경우 as를 사용하여 별칭을 붙여준다.

 

 

 

참고 - https://www.inflearn.com/course/%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC

참고 - https://lgphone.tistory.com/86

공식문서 - https://sequelize.org/master/manual/associations.html

Comments