Sequelize Class model에서 hooks 사용해보기

IT공부중·2020년 11월 11일
0

웹 전반

목록 보기
5/7

sequelize에 hooks라는 것이 있다. 공식문서에 의하면 라이프사이클 이벤트로 알려져있고, sequelize가 실행되기 전이나 후에 실행되는 함수 같은 것이다. 예를 들어 모델에 값을 저장하기 전에 언제나 어떤 값을 넣고 싶을 수 있다. 이럴 때 beforeUpdate hook을 사용하면 된다고 한다.

Sequelzie 공식문서
기본적인 hook들이 많이 있다. 공식문서에는 class 를 선언하고 그 밑에 class에 addHook 을 이용해서 주게 되어있다. 하지만 class로 model을 사용할 때 이것보다 더 나은 방법을 찾고 있었고 그걸 찾은거 같아서 공유한다. 그리고 다른 모델에 접근해야할 때 예시가 거의 없어서 불편했는데 스택오버플로우를 찾아보다 알게 되었다.

간단하게 예시를 만들어보려고 한다. User가 여러 개의 Monster를 가질 수 있고 한 Monster는 한 User에게 속하는 1:N 관계라고 하겠다. User가 회원가입을 하면 기본 몬스터 한마리가 주어진다고 할 때 어떻게 해야할까?
물론 User create 후 Monster를 create 하는 로직을 따로 작성해둘 수도 있겠지만 User에 afterCreate hook을 사용해서 할 수 있을 것 같다.

Monster 모델을 간단하게 작성하면 이렇게 될 것 같다.

models/Monster

const Sequelize = require('sequelize');

module.exports = class Monster extends Sequelize.Model {
  static init(sequelize) {
    return super.init(
      {
        name: {
          type: Sequelize.STRING(20),
          allowNull: false,
        },
      },
      {
        sequelize,
        modelName: 'Monster',
        tableName: 'monster',
        charset: 'utf8',
        collate: 'utf8_general_ci',
        // 여기에 hook 추가!!
      }
    );
  }

  static associate(db) {
    this.belongsTo(db.User, {
      foreignKey: { name: 'userId', allowNull: false },
      targetKey: 'id',
    });
  }
};

User에 속하고 속성으로는 name을 가지고 있다. 그리고 Monster의 주인이 되는
User 모델을 간단하게 작성하면 이렇게 작성할 수 있을 것 같다.
그리고 가입을 했을 때 간단한 기본 Monster를 추가하는 hooks를 추가하도록 하겠다.

models/User

const Sequelize = require('sequelize');

module.exports = class User extends Sequelize.Model {
  static init(sequelize) {
    return super.init(
      {
        name: {
          type: Sequelize.STRING(20),
          allowNull: false,
        },
        password: {
          type: Sequelize.STRING(100),
          allowNull: true,
        },
      },
      {
        sequelize,
        modelName: 'User',
        tableName: 'user',
        charset: 'utf8',
        collate: 'utf8_general_ci',
        // 여기에 hook 추가!!
        hooks: {
          //afetCreate를 통해 user가 생성되고 나면 다음 함수가 실행되도록함.
          // 생성된 user의 정보가 첫번째 인자로 들어온다.
          afterCreate: (user, options) => {
            // user.Update 등을 사용할 수도 있고 다른 모델에 접근하려면 
            //init에서 받은 sequelize를 활용하여 사용가능하다.
            sequelize.models.Monster.create({
              name: '기본 몬스터1',
              userId: user.id,
            });
          },
        },
      }
    );
  }

  static associate(db) {
    this.hasMany(db.Monster, {
      foreignKey: { name: 'userId', allowNull: false },
      sourceKey: 'id',
    });
  }
};

이렇게 설정한 파일은 index.js에서 불러와서 설정 후 사용된다.
아래 코드처럼 짜면 models에 model 코드들을 추가하면 자동으로 다른 설정 필요없이 편하게 사용할 수 있다.
models/index.js

const Sequelize = require('sequelize');
const fs = require('fs');
const path = require('path');
const basename = path.basename(__filename);

const env = process.env.NODE_ENV || 'development';
const config = require('../config/config');

const sequelize = new Sequelize(
  config[env].database,
  config[env].username,
  config[env].password,
  config[env],
);

const db = fs
  .readdirSync(__dirname)
  .filter((file) => {
    return (
      file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js'
    );
  })
  .reduce((total, file) => {
    const [filename] = file.split('.');
    total[filename] = require(path.join(__dirname, file));
    return total;
  }, {});

const Models = Object.keys(db);

Models.forEach((model) => {
  if (db[model].init) db[model].init(sequelize);
});

Models.forEach((model) => {
  if (db[model].associate) db[model].associate(db);
});

module.exports = { sequelize, db };

다음에는 hooks를 더 많이 사용해서 더 간결한 코드가 될 수 있도록 해야겠다.

profile
3년차 프론트엔드 개발자 문건우입니다.

0개의 댓글