알아두어야 할 맥락
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
- https://velog.io/@cadenzah/sequelize-document-4
- https://velog.io/@jiheon/Node.js-Sequelize-Associate
- https://velog.io/@josworks27/Back-end-Sequelize%EC%9D%98-cascade-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0
- https://kyounghwan01.github.io/blog/etc/sequelize/sequelize-cascade/#mysql-config-%E1%84%83%E1%85%A6%E1%84%8B%E1%85%B5%E1%84%90%E1%85%A5-%E1%84%80%E1%85%AE%E1%84%89%E1%85%A5%E1%86%BC
- https://velog.io/@josworks27/Sequelize-migration-%ED%95%98%EA%B8%B0