Sequelize Association 관련 문제 해결

otter·2021년 9월 2일
0

우선 문제상황은 Sequelize 환경에서 association을 설정하고자 했을 때 발생했다.

Sequelize 기반 user, url model이 존재하는 환경이었고
userurl model이 1:N의 관계를 갖도록 하고 싶었다.

공식문서를 통해 관계를 표현하는 방법은 알아낼 수 있었다.

  • HasOne
  • BelongsTo
  • HaseMany
  • BelongsToMany

총 네개의 코드로 설정할 수 있었고, 내가 구현하고자 한 부분은

user HasMany url

url BelongsTo user

위와 같이 표현될 수 있다는 것을 알았다.


우선 url model이 user model의 id를 참조하도록 해줄 것이었기 때문에

url model에 foreign key에 해당하는 필드를 새로 생성해줘야 했다.

Sequelize 환경이기에 위 작업을 위해 migration 파일을 생성해줬다.

npx sequelize migration:generate --name add-url-userId-column

add-url-userId-column이라는 이름의 migration 파일을 생성해줬고, 파일 내부의 구조는

'use strict';

module.exports = {
  up: async (queryInterface, Sequelize) => {
    /**
     * Add altering commands here.
     *
     * Example:
     * await queryInterface.createTable('users', { id: Sequelize.INTEGER });
     */
  },

  down: async (queryInterface, Sequelize) => {
    /**
     * Add reverting commands here.
     *
     * Example:
     * await queryInterface.dropTable('users');
     */
  }
};

최초 위와 같이 생성되었다.

up에 해당하는 부분은 column을 생성하고자 할 때 동작하는 부분이고,

down에 해당하는 부분은 해당 column을 삭제하고자 할 때 동작하는 부분이다.

'use strict';

module.exports = {
  up: async (queryInterface, Sequelize) => {
    /**
     * Add altering commands here.
     *
     * Example:
     * await queryInterface.createTable('users', { id: Sequelize.INTEGER });
     */
    return queryInterface.addColumn(
      'urls',  // name of target model
      'userId',  // foreign key's name what we'll make
      {
        type: Sequelize.INTEGER,  // foreign key's type
        references: {
          model: 'users',  // name of source model
          key: 'id'  // Primary key
        },
        onUpdate: 'CASCADE',  // setting for when Primary key is update
        onDelete: 'CASCADE'   // setting for when Primary key is delete
      }
    );
  },

  down: async (queryInterface, Sequelize) => {
    /**
    * Add reverting commands here.
    *
    * Example:
    * await queryInterface.dropTable('users');
    */
    return queryInterface.removeColumn(
      'urls',   // target model
      'userId'  // key we'll remove
    )
  }
};

위처럼 해당하는 부분에 맞는 내용을 채워주고 migrate 해주면 column이 생성된다.

model의 이름 뒤에는 s를 붙여줘야 한다.

Sequelize 환경에서 생성된 model은 자동으로 복수형(~s)이 된다.


npx sequelize db:migrate

migration 파일을 통해 column이 생겨났지만,

model 생성의 지표가 되는 url 파일에는 아직 column을 추가해주지 않아 수동으로 적어주었다.

문제 발생

위에서 userId column을 migration을 통해 생성해줬다.

그리고 migration 파일과 model 생성 파일은 동기화 시켜주는 것이 좋을 것 같다고 생각했고,

url.init({
  url: DataTypes.STRING,
  title: DataTypes.STRING,
  visits: {
    type: DataTypes.INTEGER,
    defaultValue: 0
  },
  userId: DataTypes.INTEGER  // added
},

직접 userId column의 내용을 적어 테스트를 해봤는데 전혀 작동을 하지 않았다..!

그리고 파일을 여기저기 뜯어보며 헤매던 와중 url model 파일에서 충격적인 내용을 발견했다.

문제 해결

static associate(models) {
  // define association here
}

위 코드는 url model을 init 해주는 영역 위에 있던 코드다.

association 관련된 내용을 지정해주는 영역이 대놓고 친절하게 명시되어 있었다.

사실 시작부터 HasMany, BelongsTo를 적어두는 공간을 찾지 못해 의아했었다.

저 부분에서 사용해주면 되는 것이었다.

static associate(models) {
  // define association here
  url.belongsTo(models.user, {
    foreignKey: 'userId'
  })
}

우선 1 : N에서 N에 해당하는 url model에게 BelongsTo를 지정해줬다.

modesl. 뒤에는 데이터를 참조할 model의 이름을 적어주면 된다.

그 아래의 공간에는 foreign key로 사용될 field의 이름을 적어줬다.

static associate(models) {
  // define association here
  user.hasMany(models.url, {
    foreignKey: 'userId',
    as: 'urls'
  })
}

그 뒤에는 1 : N에서 1에 해당하는 user model에 HasMany를 지정해줬다.

models. 뒷 부분은 Primary key를 보내줄 model의 이름을 적어주고,

해당 model에서 사용될 foreign key의 field 이름과 model이 생성됐을 때의 이름을 적어준다.

(Sequelize 환경에서 생성된 model은 자동으로 복수형이 된다.)

user => users

url => urls

0개의 댓글