Sequelize Association

황지웅·2022년 6월 1일
0

데이터베이스

목록 보기
5/5
post-custom-banner

알아두어야 할 맥락

1. migration & model

  • migrations은 MySQL에 실제 이식될 테이블의 형태를 결정하며, models는 ORM을 통해 개발환경에서 MySQL를 다루기 위한 것이라고 생각하면 좋을 듯 하다.그렇기 때문에, migrations의 속성과 models의 속성은 동일해야 한다.
    model과 migration을 직접 정의할수도있고 sequelize cli의 명령어를 통해서도 작성할수있다(cli이용할 것임)

  • Sequelize에서는 Model.init()및 sequelize.define() 두 가지 동등한 방법으로 모델을 정의할 수 있습니다.유일한 차이점은 하나는 객체 지향 프로그래밍 패턴을 따르고 다른 하나는 함수형 프로그래밍 패턴을 따른다는 것입니다.

  • 모델명은 복수가 아닌 단수를 사용하고, 첫 글자는 대문자를 권장

2.Association

  • 연결관계는 각각의 모델들에 모두 정의해주어야 합니다(연결관계 메서드이용)

  • 연결 관계를 정의하는 메서드사용시 함수를 호출하는 모델 객체가 source, 메서드 안에 인자로 전달되는 모델 객체가 target 입니다. 어떤 메서드를 사용하더라도 기본적으로 적용됩니다.
    User.hasOne(Project).=> User :source , Project:target

  • sequelize에서 외래 키는 모델 정의할 때에 만들어지는 것이 아니라, 두 모델의 연결 관계를 정의 할 때에, 연결 관계에 따라 Sequelize가 적절한 모델에 컬럼을 자동으로 만들어줍니다. 이 작업과 관련된 설정을 Sequelize에게 맡길 수도 있고, 몇몇 옵션(foreignkey)을 주어서 사용자가 제어할 수도 있습니다.

모델에서 1:N 관계에 사용되는 메서드및 옵션

1. 메서드

  • BelongsTo
    • 외래키가 source모델에 존재하는 연결관계
  • hasMany()
    • 외래 키가 target모델에 존재하는 관계

2. 옵션

  • as : target 모델의 별명
    • include 사용 시 쿼리 결과 값이 모델명으로 지정되는데, 이는 foreignKey에서 AS 사용으로 include 에서도 같은 AS 명을 사용해야 함
  • foreignKey : 외래 키 정의, 생략 시 모델명 + Id 로 컬럼을 생성
    • foreignKey 옵션을 주면, as 옵션을 모두 무시하고 foreignKey 옵션의 값에 따라 외래 키 컬럼을 이름짓습니다.(자동생성x)
    • foreignKey 설정 시 두 모델에서 동일한 설정이 필요, 하나의 모델에서만 정의하는 경우 자동으로 컬럼을 생성하여 중복 이슈가 일어남
    • 외래 키 자동 생성의 대원칙은 [target 모델의 이름 + target 모델의 주요 키 이름]입니다
    • 기본 명명 규칙은 camelCase이지만, source 모델이 underscored: true로 설정되어있다면, 외래 키는 snake_case로 명명됩니다.
  • onUpdate, onDelete
    • RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL 선택
    • 1:N 관계로 이루어진 테이블에서 '생성'의 경우는 각각 테이블에 연결하여 생성하면 아무런 문제가 없지만 1의 관계에 있는 테이블 row가 삭제되는 경우 문제가 발생합니다.
      cascade 옵션을 설정한 후 부모 테이블의 row를 제거하면 부모를 FK로 가진 자식 그리고 자식을 FK로 가지고 있는 자손 즉, 부모와 연결된 모든 테이블의 row는 자동으로 삭제됩니다
    • sequelize 및 다른 docs에서는 cascade는 하위(N)의 관계 옵션에 넣어줘야 한다고 합니다. 왜떄문에그런지 모르지만 부모와 자식 모두 옵션을 넣어줘야 정상작동하는경우가 있기때문에 가급적 모두 넣어주시는게 좋을듯

모델코드

1.모델 및 마이그레이션 생성

# users
$ sequelize model:generate --name User --attributes email:string,password:string,name:string

# reviews
$ sequelize model:generate --name Review --attributes users_id:integer,content:string

2.테이블간의 관계 생각해보기
예시인 users와 reviews 테이블 간의 관계는
하나의 유저는 여러 개의 리뷰를 가질 수 있으며,
하나의 리뷰는 하나의 유저에 속함
1 : N 관계

3.작성코드

  • user모델
// models/user.js

"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
  class User extends Model {
    static associate(models) {
      this.hasMany(models.Review, {
        as: "reviews",
        foreignKey: "userId",
        // onUpdate: defaults to CASCADE
        onDelete: "cascade",
      });
    }
  }
  User.init(
    {
      email: {
        type: DataTypes.STRING,
        unique: true,
        allowNull: false,
      },
      password: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      name: {
        type: DataTypes.STRING,
        allowNull: false,
      },
    },
    {
      sequelize,
      modelName: "User",
    }
  );
  return User;
};
  • Review모델
// models/review.js

"use strict";
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
  class Review extends Model {
    static associate(models) {
      this.belongsTo(models.User, {
        as: "users",
        foreignKey: "userId",
        onDelete: "cascade",
      });
    }
  }
  Review.init(
    {
      userId: {
        field: "users_id",
        type: DataTypes.INTEGER,
        allowNull: false,
      },
      content: {
        type: DataTypes.STRING,
        allowNull: false,
      },
    },
    {
      sequelize,
      modelName: "Review",
    }
  );
  return Review;
};

마이그레이션에서 1:M 관계

  • references: {} 로 관계를 정의
references: {
	model : 참조하는 테이블 정의
	key : 참조하는 키 정의
} 

마이그레이션 코드

  • user 마이그레이션
// migrations/<timestamp>-create-user.js

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable("users", {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER,
		onDelete: "cascade",
      },
      email: {
        allowNull: false,
        unique: true,
        type: Sequelize.STRING,
      },
      password: {
        allowNull: false,
        type: Sequelize.STRING,
      },
      name: {
        allowNull: false,
        type: Sequelize.STRING,
      },
    });
  },
  down: async (queryInterface) => {
    await queryInterface.dropTable("users");
  },
};
  • review마이그레이션
// migrations/<timestamp>-create-review.js

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.createTable("reviews", {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER,
      },
      users_id: {
        allowNull: false,
        type: Sequelize.INTEGER,
        references: {
          model: "users",
          key: "id",
        },
        onDelete: "cascade",
      },
      content: {
        allowNull: false,
        type: Sequelize.STRING,
      },
    });
  },
  down: async (queryInterface) => {
    await queryInterface.dropTable("reviews");
  },
};

Reference

post-custom-banner

0개의 댓글