코딩 공부/공부

Sequelize 관계 설정 1 : N, N : M(1대다, 다대다)

fullfish 2022. 5. 4. 01:59

trip : diary = 1 : N

diart : hashtag = N : M

인 경우의 관계 설정

 

관계 설정방법은 2가지가 있다

마이그레이션과 모델 모두 이용하는 방법과

마이그레이션을 하지않고 모델만 이용하는 방법이 있는데

마이그레이션을 이용하는 방법으로 하겠다

 

1 : N

우선 trip, diary, hashtag의 migration과 model파일들을 다 만든 상황에서

 

일반적으로 migration 파일을 만들때

npx sequelize-cli model:generate --name user --attributes name:string

처럼 만드는데 이렇게 말고

npx sequelize-cli migration:generate --name fk-diary

이런식으로 model이 생성되지 않고 migration파일만 생성되게 만든다

 

trip과 diary는 1 : N이고

diary에 trip_id가 외래키로 존재해야하는데 맨 처음 diary migration파일을 만들때 trip_id를 직접 만들어주지 않는다

fk-diary 파일을

// /migrations/20220412004755-fk-diary.js
"use strict";

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.addColumn("diary", "trip_id", {
      type: Sequelize.INTEGER,
      allowNull: true,
      references: {
        model: "trip",
        key: "id",
      },
      onUpdate: "CASCADE",
      onDelete: "CASCADE",
    });
  },

  down: async (queryInterface, Sequelize) => {
    await queryInterface.removeColumn("diary", "trip_id");
  },
};

위 코드처럼 만들어 준다

diary 테이블에 trip_id칼럼을 생성하는데 해당 칼럼은 trip model의 id값을 참조한다는 의미이다

또한 관계 설정의 꽃인 onUpdate와 onDelete를 CASCADE해주면

trip을 지우거나 업데이트할떄 해당 trip에 귀속되어있는 diary도 삭제되거나 업데이트의 영향을 받는다

그리고 마지막으로

models/index에서

// /models/index.js
db.trip.hasMany(db.diary, { foreignKey: "trip_id", sourceKey: "id" });
db.diary.belongsTo(db.trip, { foreignKey: "trip_id", targetKey: "id" });

위 코드를 추가해줘서 관계 설정을 마무리해준다

trip은 hasMany 많이 가지고 있고

diary는 어디에 소속되어있다는 뜻이다

diary의 구조를 보면

diary테이블을 생성하는 migrate를 할때 만들어 주지 않았던 trip_id가 생성되어 있으며

MUL이 찍혀있는것을 확인 할 수 있다

 

N : M

N : M의 관계는 중간에 징검다리역할을 하는 join table이 필요하다

위에서와 마찬가지로 diary와 hashtag 테이블이 생성된것을 기준으로 설명하자면

npx sequelize-cli migration:generate --name fk-diary_hashtag

위 코드처럼 조인테이블역할을 할 migration을 생성해준다

// /migrations/20220411075551-fk-diary_hashtag.js
"use strict";

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.addColumn("diary_hashtag", "diary_id", {
      type: Sequelize.INTEGER,
      allowNull: true,
      references: {
        model: "diary",
        key: "id",
      },
      onUpdate: "CASCADE",
      onDelete: "CASCADE",
    });
    await queryInterface.addColumn("diary_hashtag", "hashtag_id", {
      type: Sequelize.INTEGER,
      allowNull: true,
      references: {
        model: "hashtag",
        key: "id",
      },
      onUpdate: "CASCADE",
      onDelete: "CASCADE",
    });
  },

  down: async (queryInterface, Sequelize) => {
    await queryInterface.removeColumn("diary_hashtag", "diary_id");
    await queryInterface.removeColumn("diary_hashtag", "hashtag_id");
  },
};
// /models/index.js
db.diary.belongsToMany(db.hashtag, {
  through: "diary_hashtag",
  foreignKey: "diary_id",
  sourceKey: "id",
  onDelete: "CASCADE",
  onUpdate: "CASCADE",
});
db.hashtag.belongsToMany(db.diary, {
  through: "diary_hashtag",
  foreignKey: "hashtag_id",
  sourceKey: "id",
  onDelete: "CASCADE",
  onUpdate: "CASCADE",
});

그리고 migration과 model 파일을 위에처럼 만들어준다

belongtToMany를 서로한테 사용해서 N : M 관게임을 명시해준다

그러면

join table인 diary_hashtag 테이블이 생성되며

trip을 삭제시

하위의 diary가 삭제되며

삭제된 diary 하위의 diary_hashtag가 삭제된다

하지만 hashtag까지는 삭제안되는데 N : M 관계이므로

방금 삭제된 diary가 가지고 있었던 hastag가 다른 diary에서도 쓰일 수 있으므로 삭제안되는것이 정상이지만

아무 diary에서도 참조 안하는 hashtag가 존재해도 (즉, join table내에 hashtag_id값이 없어도) 해당 hashtag는 자동으로 안지워지는데 이게 자동으로 지워져야한다고 생각해서 이것저것 시도하다가 결국에는 자동으로 삭제가 안되는것으로 판명났다

 

N : M 관계에대한 고찰 : https://fullfish.tistory.com/93?category=1054038 

 

Sequelize N : M (다대다)관계에 대한 고찰

diary : hashtag = N : M 일때 처음에는 await diary.create() await diary_hashtag.create() await hashtag.create() 로 각각 3개의 테이블에 데이터를 만들어 줬는데 belongsToMany로 관계설정을 해줬으니까 각..

fullfish.tistory.com