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를 더 많이 사용해서 더 간결한 코드가 될 수 있도록 해야겠다.