[ORM] ๐ ์ํ๋ผ์ด์ฆ - ๋ชจ๋ธ(ํ ์ด๋ธ) ์ ์ํ๊ธฐ
Mysql ํ ์ด๋ธ ์์ฑ ๊ตฌ๋ฌธ
create schema `nodejs` default character set utf8;
use nodejs;
drop table if exists comments;
drop table if exists users;
create table nodejs.users (
id int not null primary key auto_increment,
name varchar(20) not null,
age smallint unsigned not null,
married tinyint not null, -- tinyint๋ 0๊ณผ 1 ๋ถ๋ฆฌ์ธ ์ฉ
comment text null, -- ์๊ธฐ ์๊ฐ
created_at datetime not null default now(),
unique index name_unique (name asc)
)
comment = "์ฌ์ฉ์ ์ ๋ณด"
default character set = utf8
engine = InnoDB;
create table nodejs.comments (
id int not null primary key auto_increment,
commenter int not null,
comment varchar(100) not null, -- ๋๊ธ
created_at datetime not null default now(),
index commenter_idx(commenter ASC),
constraint commenter foreign key(commenter) references nodejs.users(id) on delete cascade on update cascade
)
comment = "๋๊ธ"
default charset = utf8mb4 -- mb4๋ ์ด๋ชจํฐ์ฝ๋ ๋ฃ์ ์ ์์
engine = InnoDB;
insert into users(name, age, married, comment) values('zero', 24, 0, '์๊ธฐ์๊ฐ1');
insert into users(name, age, married, comment) values('nero', 32, 1, '์๊ธฐ์๊ฐ2');
Sequelize ๋ชจ๋ธ ์ ์ํ๊ธฐ
์ด์ MySQL์์ ์ ์ํ ํ ์ด๋ธ์ ์ํ๋ผ์ด์ฆ์์๋ ์ ์ํด์ผ ํ๋ค.
MySQL์ ํ ์ด๋ธ์ ์ํ๋ผ์ด์ฆ์ ๋ชจ๋ธ๊ณผ ๋์๋๋ค.
์ํ๋ผ์ด์ฆ๋ ๋ชจ๋ธ๊ณผ MySQL์ ํ ์ด๋ธ์ ์ฐ๊ฒฐํด์ฃผ๋ ์ญํ ์ ํ๋ค.
์์์ ํ ์ด๋ธ์ ์์ฑํ์ผ๋,
User ์ Comment ๋ชจ๋ธ์ ๋ง๋ค์ด users ํ ์ด๋ธ๊ณผ comments ํ ์ด๋ธ์ ์ฐ๊ฒฐํด๋ณด์.
์ํ๋ผ์ด์ฆ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ธ ์ด๋ฆ์ ๋จ์ํ (User), ํ ์ด๋ธ ์ด๋ฆ์ ๋ณต์ํ (users) ์ผ๋ก ์ฌ์ฉํ๋ค.
models/user.js
const Sequelize = require('sequelize');
class User extends Sequelize.Model {
// ์คํํฑ ๋ฉ์๋
// ํ
์ด๋ธ์ ๋ํ ์ค์
static init(sequelize) {
return super.init(
{ // ์ฒซ๋ฒ์งธ ๊ฐ์ฒด ์ธ์๋ ํ
์ด๋ธ ํ๋์ ๋ํ ์ค์
name: {
type: Sequelize.STRING(20),
allowNull: false,
unique: true,
},
age: {
type: Sequelize.SMALLINT,
allowNull: false,
},
married: {
type: Sequelize.BOOLEAN,
allowNull: false,
},
comment: {
type: Sequelize.TEXT,
allowNull: true,
},
created_at: {
type: Sequelize.DATE,
allowNull: false,
defaultValue: Sequelize.NOW,
},
},
{ // ๋๋ฒ์งธ ๊ฐ์ฒด ์ธ์๋ ํ
์ด๋ธ ์์ฒด์ ๋ํ ์ค์
sequelize, /* static init ๋ฉ์๋์ ๋งค๊ฐ๋ณ์์ ์ฐ๊ฒฐ๋๋ ์ต์
์ผ๋ก, db.sequelize ๊ฐ์ฒด๋ฅผ ๋ฃ์ด์ผ ํ๋ค. */
timestamps: false, /* true : ๊ฐ๊ฐ ๋ ์ฝ๋๊ฐ ์์ฑ, ์์ ๋ ๋์ ์๊ฐ์ด ์๋์ผ๋ก ์
๋ ฅ๋๋ค. */
underscored: false, /* ์นด๋ฉ ํ๊ธฐ๋ฒ์ ์ค๋ค์ดํฌ ํ๊ธฐ๋ฒ์ผ๋ก ๋ฐ๊พธ๋ ์ต์
*/
modelName: 'User', /* ๋ชจ๋ธ ์ด๋ฆ์ ์ค์ . */
tableName: 'users', /* ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ
์ด๋ธ ์ด๋ฆ. */
paranoid: false, /* true : deletedAt์ด๋ผ๋ ์ปฌ๋ผ์ด ์๊ธฐ๊ณ ์ง์ด ์๊ฐ์ด ๊ธฐ๋ก๋๋ค. */
charset: 'utf8', /* ์ธ์ฝ๋ฉ */
collate: 'utf8_general_ci'
}
);
}
// ๋ค๋ฅธ ๋ชจ๋ธ๊ณผ์ ๊ด๊ณ
static associate(db) { // ์ธ์๋ก index.js์์ ๋ง๋ ์ฌ๋ฌ ํ
์ด๋ธ์ด ์ ์ฅ๋์ด์๋ db๊ฐ์ฒด๋ฅผ ๋ฐ์ ๊ฒ์ด๋ค.
db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id', onDelete: 'cascade', onUpdate: 'cascade' });
// db.User (hasMany) db.Comment = 1:N ๊ด๊ณ ์ด๋ค.
// db.User๋ ๊ฐ์ง๊ณ ์๋ค. ๋ง์ด. db.Comment๋ฅผ
}
};
module.exports = User;
models/comment.js
const Sequelize = require('sequelize');
class Comment extends Sequelize.Model {
static init(sequelize) {
return super.init(
{
comment: {
type: Sequelize.STRING(100),
allowNull: false,
},
created_at: {
type: Sequelize.DATE,
allowNull: true,
defaultValue: Sequelize.NOW,
},
},
{
sequelize,
timestamps: false,
modelName: 'Comment',
tableName: 'comments',
paranoid: false,
charset: 'utf8mb4',
collate: 'utf8mb4_general_ci'
});
}
static associate(db) {
db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'id', onDelete: 'cascade', onUpdate: 'cascade'});
// db.Comment (belongTo) db.User = N:1 ๊ด๊ณ ์ด๋ค.
// db.Comment๋ ์ํด์๋ค. db.User์๊ฒ
}
};
module.exports = Comment;
๋ชจ๋ธ์ Sequelize.Model์ ํ์ฅํ ํด๋์ค๋ก ์ ์ธํ๋ค.
๋ชจ๋ธ์ static init ๋ฉ์๋์ static associate ๋ฉ์๋๋ก ๋๋๋๋ฐ,
init ๋ฉ์๋์์๋ ํ ์ด๋ธ์ ๋ํ ์ค์ ์ ํ๊ณ ,
associate ๋ฉ์๋์๋ ๋ค๋ฅธ ๋ชจ๋ธ๊ณผ์ ๊ด๊ณ(1:1, 1:N)๋ฅผ ์ ๋๋ค.
init ๋ฉ์๋์ ๋ถ๋ชจ ์ฝ๋ฐฑ super.init ๋ฉ์๋๋
์ฒซ ๋ฒ์งธ ์ธ์๋ ํ ์ด๋ธ ์ปฌ๋ผ์ ๋ํ ์ค์ ์ด๊ณ ,
๋ ๋ฒ์งธ ์ธ์๋ ํ ์ด๋ธ ์์ฒด์ ๋ํ ์ค์ ์ด๋ค.
์ํ๋ผ์ด์ฆ๋ ์์์ id๋ฅผ ๊ธฐ๋ณธ ํค๋ก ์ฐ๊ฒฐํ๋ฏ๋ก, id ์ปฌ๋ผ์ ๋ฐ๋ก ์ ์ด์ค ํ์๋ ์๋ค.
์ํ๋ผ์ด์ฆ์ ์๋ฃํ
์ํ๋ผ์ด์ฆ์ ์๋ฃํ์ MySQL๊ณผ ์กฐ๊ธ ๋ค๋ฅด๋ค.
์๋ ๋น๊ต๋ฅผ ํตํด ๋ง๋ ์ต์ ์ ์ ๋ ฅํ์.
MySQL | Sequelize |
VARCHAR(100) | STRING(100) |
INT | INTEGER |
TINYINT | BOOLEAN |
DATETIME | DATE |
INT UNSIGNED | INTEGER.UNSIGNED |
NOT NULL | allowNull: false |
UNIQUE | unique: true |
DEFAULT now() | defaultValue: Sequelize.NOW |
ZEROFILL | INTEGER.ZEROFILL |
* ์ฐธ๊ณ
https://sequelize.org/v5/manual/data-types.html
์ํ๋ผ์ด์ฆ ์ต์
super.init์ ๋ ๋ฒ์งธ ์ธ์๋ ํ ์ด๋ธ ์ต์ ์ด๋ค.
- sequelize:
static init ๋ฉ์๋์ ๋งค๊ฐ๋ณ์์ ์ฐ๊ฒฐ๋๋ ์ต์ ์ผ๋ก, db.sequelize ๊ฐ์ฒด๋ฅผ ๋ฃ์ด์ผ ํ๋ค.
๋์ค์ model/index.js์์ ์ฐ๊ฒฐํ๋ค. - timestamps:
์ด ์์ฑ ๊ฐ์ด true๋ฉด ์ํ๋ผ์ด์ฆ๋ createdAt๊ณผ updatedAt ์ปฌ๋ผ์ ์ถ๊ฐํ๋ฉฐ, ๊ฐ๊ฐ ๋ก์ฐ๊ฐ ์์ฑ๋ ๋์ ์์ ๋ ๋์ ์๊ฐ์ด ์๋์ผ๋ก ์ ๋ ฅ๋๋ค.
(๊ทธ๋ฌ๋ ์์ ์์ ์ง์ created_at ์ปฌ๋ผ์ ๋ง๋ค์์ผ๋ฏ๋ก ์ง๊ธ์ timestamps ์์ฑ์ด ํ์์๋ค. ๋์ค์ ๊ผญ true๋ก ํ๊ณ ํ์) - underscored:
์ํ๋ผ์ด์ฆ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํ ์ด๋ธ๋ช ๊ณผ ์ปฌ๋ผ๋ช ์ ์นด๋ฉ ํ๊ธฐ๋ฒ (camel case) ์ผ๋ก ๋ง๋ ๋ค.
์ด๋ฅผ ์ค๋ค์ดํฌ ํ๊ธฐ๋ฒ (snake case) ์ผ๋ก ๋ฐ๊พธ๋ ์ต์ ์ด๋ค (์๋ฅผ๋ค์ด updatedAt ์ updated_at ์ผ๋ก). - modelName:
๋ชจ๋ธ ์ด๋ฆ์ ์ค์ ํ ์ ์๋ค. ๋ ธ๋ ํ๋ก์ ํธ์์ ์ฌ์ฉํ๋ค. - tableName:
์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ ์ด๋ธ ์ด๋ฆ.
๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ธ ์ด๋ฆ์ ์๋ฌธ์ ๋ฐ ๋ณต์ํ์ผ๋ก ๋ง๋ ๋ค.
์๋ฅผ ๋ค์ด ๋ชจ๋ธ ์ด๋ฆ์ด User ๋ผ๋ฉด ํ ์ด๋ธ ์ด๋ฆ์ users ์ด๋ค. - paranoid:
true๋ก ์ค์ ํ๋ฉด deletedAt์ด๋ผ๋ ์ปฌ๋ผ์ด ์๊ธด๋ค.
๋ก์ฐ๋ฅผ ์ญ์ ํ ๋ ์์ ํ ์ง์ฐ์ง ์๊ณ , deletedAt์ ์ง์ด ์๊ฐ์ด ๊ธฐ๋ก๋๋ค.
๋ก์ฐ๋ฅผ ์กฐํํ๋ ๋ช ๋ น์ ๋ด๋ ธ์ ๊ฒฝ์ฐ deletedAt์ ๊ฐ์ด null์ธ ๋ก์ฐ๋ฅผ ์กฐํํ๋ค.
์ด๋ ๊ฒ ํ๋ ์ด์ ๋ ํ์ ๋ก์ฐ๋ฅผ ๋ณต์ํ๊ธฐ ์ํด์๋ค. ๋ก์ฐ๋ฅผ ๋ณต์ํด์ผ ํ ์ํฉ์ด ์๊ธธ ๊ฒ ๊ฐ๋ค๋ฉด ๋ฏธ๋ฆฌ true๋ก ์ค์ ํด๋์. - charset / collate:
๊ฐ๊ฐ utf8 ๊ณผ utf8_general_ci ๋ก ์ค์ ํด์ผ ํ๊ธ์ด ์ ๋ ฅ๋๋ค.
์ด๋ชจํฐ์ฝ๊น์ง ์ ๋ ฅํ ์ ์๊ฒ ํ๊ณ ์ถ๋ค๋ฉด utf8mb4 ์ utf8mb4_general_ci ๋ฅผ ์ ๋ ฅํ๋ค.
์ด์ models/index.js์ ๋ ํ ์ด๋ธ ํ์ผ์ ์ฐ๊ฒฐ์์ผ์ฃผ์.
const Sequelize = require('sequelize');
// ํด๋์ค๋ฅผ ๋ถ๋ฌ์จ๋ค.
const User = require('./user')
const Comment = require('./comment')
const env = process.env.NODE_ENV || 'development';
// config/config.json ํ์ผ์ ์๋ ์ค์ ๊ฐ๋ค์ ๋ถ๋ฌ์จ๋ค.
// config๊ฐ์ฒด์ env๋ณ์(development)ํค ์ ๊ฐ์ฒด๊ฐ๋ค์ ๋ถ๋ฌ์จ๋ค.
// ์ฆ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ค์ ์ ๋ถ๋ฌ์จ๋ค๊ณ ๋งํ ์ ์๋ค.
const config = require("../config/config.json")[env]
const db = {};
// new Sequelize๋ฅผ ํตํด MySQL ์ฐ๊ฒฐ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค.
const sequelize = new Sequelize(config.database, config.username, config.password, config)
// ์ฐ๊ฒฐ๊ฐ์ฒด๋ฅผ ๋์ค์ ์ฌ์ฌ์ฉํ๊ธฐ ์ํด db.sequelize์ ๋ฃ์ด๋๋ค.
db.sequelize = sequelize;
// ๋ชจ๋ธ ํด๋์ค๋ฅผ ๋ฃ์.
db.User = User;
db.Comment = Comment;
// ๋ชจ๋ธ๊ณผ ํ
์ด๋ธ ์ข
ํฉ์ ์ธ ์ฐ๊ฒฐ์ด ์ค์ ๋๋ค.
User.init(sequelize);
Comment.init(sequelize);
// db๊ฐ์ฒด ์์ ์๋ ๋ชจ๋ธ๋ค ๊ฐ์ ๊ด๊ณ๊ฐ ์ค์ ๋๋ค.
User.associate(db);
Comment.associate(db);
// ๋ชจ๋๋ก ๊บผ๋ธ๋ค.
module.exports = db;
db๋ผ๋ ๊ฐ์ฒด์ User๊ณผ Comment ๋ชจ๋ธ์ ๋ด์๋ค.
์์ผ๋ก db ๊ฐ์ฒด๋ฅผ requireํ์ฌ User๊ณผ Comment ๋ชจ๋ธ์ ์ ๊ทผํ ์ ์์ ๊ฒ์ด๋ค.
๋ํ User.init๊ณผ Comment.init์ ๊ฐ๊ฐ์ ๋ชจ๋ธ์ static.init ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉฐ, init์ด ์คํ๋์ด์ผ ํ ์ด๋ธ์ด ๋ชจ๋ธ๋ก ์ฐ๊ฒฐ๋๋ค.
๋ค๋ฅธ ํ ์ด๋ธ๊ณผ์ ๊ด๊ณ๋ฅผ ์ฐ๊ฒฐํ๋ associate ๋ฉ์๋ ์ญ์ ์คํํด๋๋ค.
Sequelize ๊ด๊ณ ์ ์ํ๊ธฐ
์ด๋ฒ์ users ํ ์ด๋ธ๊ณผ comments ํ ์ด๋ธ ๊ฐ์ ๊ด๊ณ๋ฅผ ์ ์ํด๋ณด์.
์ฌ์ฉ์ ํ ๋ช ์ ๋๊ธ์ ์ฌ๋ฌ ๊ฐ ์์ฑํ ์ ์์ง๋ง, ๋๊ธ ํ๋์ ์์ฑ์๊ฐ ์ฌ๋ฌ ๋ช ์ผ ์๋ ์๋ค.
์ด๋ฌํ ๊ด๊ณ๋ฅผ ์ผ๋๋ค (1:N) ๊ด๊ณ๋ผ๊ณ ํ๋ค. ์ ๊ด๊ณ์์๋ ์ฌ์ฉ์๊ฐ 1์ด๊ณ ๋๊ธ์ธ N์ด๋ค.
๋ค๋ฅธ ๊ด๊ณ๋ก๋ ์ผ๋์ผ (1:1), ๋ค๋๋ค (N:M) ๊ด๊ณ๊ฐ ์๋ค.
์ผ๋์ผ ๊ด๊ณ๋ก๋ ์ฌ์ฉ์์ ์ฌ์ฉ์์ ๋ํ ์ ๋ณด ํ ์ด๋ธ์ ์๋ก ๋ค ์ ์์ผ๋ฉฐ,
๋ค๋๋ค ๊ด๊ณ๋ก๋ ๊ฒ์๊ธ ํ ์ด๋ธ๊ณผ ํด์ํ๊ทธ (#) ํ ์ด๋ธ ๊ด๊ณ๋ฅผ ์๋ก ๋ค ์ ์๊ฒ ๋ค.
ํ ๊ฒ์๊ธ์ ์ฌ๋ฌ ํด์ํ๊ทธ๊ฐ ๋ฌ๋ฆด ์ ์์ผ๋ฉฐ, ํ ํด์ํ๊ทธ ๋ํ ์ฌ๋ฌ ๊ฒ์๊ธ์ ๋ฌ๋ฆด ์ ์๋ค.
↓๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ด๊ณ ๋ชจ๋ธ์ ๋ ์๊ณ ์ถ๋ค๋ฉด ?
MySQL์์๋ JOIN์ด๋ผ๋ ๊ธฐ๋ฅ์ผ๋ก ์ฌ๋ฌ ํ ์ด๋ธ ๊ฐ์ ๊ด๊ณ๋ฅผ ํ์ ํด ๊ฒฐ๊ณผ๋ฅผ ๋์ถํ๋ค.
์ํ๋ผ์ด์ฆ๋ JOIN ๊ธฐ๋ฅ๋ ์์์ ๊ตฌํํ๋ค.
๋์ ํ ์ด๋ธ ๊ฐ์ ์ด๋ ํ ๊ด๊ณ๊ฐ ์๋์ง ์ํ๋ผ์ด์ฆ์ ์๋ ค์ค์ผ ํ๋ค.
1:N ๊ด๊ณ (hasMany, belongsTo)
์ํ๋ผ์ด์ฆ์์๋ 1:N ๊ด๊ณ๋ฅผ hasMany ๋ฉ์๋๋ก ํํํ๋ค.
users ํ ์ด๋ธ์ ๋ก์ฐ ํ๋๋ฅผ ๋ถ๋ฌ์ฌ ๋ ์ฐ๊ฒฐ๋ comments ํ ์ด๋ธ์ ๋ก์ฐ๋ค๋ ๊ฐ์ด ๋ถ๋ฌ์ฌ ์ ์๋ค.
๋ฐ๋๋ก belongsTo ๋ฉ์๋๋ ์๋ค.
์ด๋ comments ํ ์ด๋ธ์ ๋ก์ฐ๋ฅผ ๋ถ๋ฌ์ฌ ๋ ์ฐ๊ฒฐ๋ users ํ ์ด๋ธ์ ๋ก์ฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.
๋ณดํต ์ธ๋ํค๊ฐ ๋ถ์ ํ ์ด๋ธ์ด belongTo๊ฐ ๋๋ค.
์ด์ ๋ชจ๋ธ ๊ฐ๊ฐ์ static associate ๋ฉ์๋์ ์ ์๋ฅผ ํด์ค์ผ ํ๋ค.
/* user.js */
static associate(db) {
db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id', onDelete: 'cascade', onUpdate: 'cascade' });
// db.User (hasMany) db.Comment = 1:N ๊ด๊ณ ์ด๋ค.
// ๋จ(db.Comment)์ ์ปฌ๋ผ commenter๊ฐ ๋ด(db.User) ์ปฌ๋ผ id๋ฅผ ์ฐธ์กฐ ํ๊ณ ์๋ค.
}
};
// comment.js
static associate(db) {
db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'id', onDelete: 'cascade', onUpdate: 'cascade'});
// db.Comment (belongTo) db.User = N:1 ๊ด๊ณ ์ด๋ค.
// ๋ด(db.Comment)์ ์ปฌ๋ผ commenter๋ ๋จ(db.User) ์ปฌ๋ผ id์ ์ํด ์๋ค.
}
};
๊ฐ๋จํ User๊ฐ ๋ง์ ๋๊ธ์ ๊ฐ์ง ์ ์์ผ๋ User.hasMany๊ฐ ๋๋ ๊ฒ์ด๊ณ ,
Comment๋ ํ User์ ์ํ ์ ์์ผ๋ Comment.belongsTo๊ฐ ๋๋ ๊ฒ์ด๋ค.
๋์ด ์ํตํ๋ ํค๋ foreignKey์ธ commenter์ด๋ฉฐ,
User์ sourceKey๋ ๊ณง Commenter์ targetKey๊ฐ ๋๋ค (hasMany์์๋ sourceKey, belongsTo์์๋ targetKey).
foreignKey๋ฅผ ๋ฐ๋ก ์ง์ ํ์ง ์๋๋ค๋ฉด ์ด๋ฆ์ด ๋ชจ๋ธ๋ช +๊ธฐ๋ณธ ํค์ธ ์ปฌ๋ผ์ด ๋ชจ๋ธ์ ์์ฑ๋๋ค.
์ฆ, ์๋ฅผ ๋ค์ด ์ ์์ ์์ commenter๋ฅผ foreignKey๋ก ์ค์ ํด ์ฃผ์ง ์์๋ค๋ฉด, ์ ์ฝ์กฐ๊ฑด์ ๋ฐ๋ผ ๋ชจ๋ธ๋ช ์ธ User๊ณผ ๊ธฐ๋ณธ ํค์ธ id๊ฐ ํฉ์ณ์ง UserId๊ฐ foreignKey๋ก ๋ฐ๋ก ์์ฑ๋๋ค.
1:1 ๊ด๊ณ (hasOne, belongsTo)
1:1 ๊ด๊ณ์์๋ hasMany ๋์ hasOne์ ์ฌ์ฉํ๋ค.
foriegnKey, sourceKey, targetKey์ ์ฌ์ฉ๋ฒ์ 1:N ๊ด๊ณ์ ๊ฐ๋ค.
hasOne๋ 1:1, belongsTo๋ 1:1์ด๋ฉด, ๋๊ฐ ๊ธฐ์ค์ผ๋ก ๋ ์ง ์ ๋งคํ ๋๊ฐ ์๋ค.
์ด๋ ์ธ๋ํค๊ฐ ๋ถ์ ๋ชจ๋ธ์ belongsTo๋ก ๊ธฐ์ค์ ์ก์์ฃผ๋ฉด ๋๋ค.
N:M ๊ด๊ณ (belongsToMany)
์ํ๋ผ์ด์ฆ์๋ N:M ๊ด๊ณ๋ฅผ belongsToMany ๋ฉ์๋๋ก ํํํ๋ค.
์ด ๊ฒฝ์ฐ์ ์ด๋ ํ ํ ์ด๋ธ์ด ์ด๋ ๋ค๋ฅธ ํ ์ด๋ธ์ ์ข ์๋๋ ๊ด๊ณ๊ฐ ์๋๋ค.
์๋ฅผ ๋ค์ด Post ๋ชจ๋ธ๊ณผ Hashtag ๋ชจ๋ธ์ด ์๋ค๊ณ ํ ๋, ๋ค์๊ณผ ๊ฐ์ด ํํํ ์ ์๋ค.
// Post
db.Post.belongsToMany(db.Hashtag, { through: 'PostHashtag' });
// Hashtag
db.Hashtag.belongsToMany(db.Post, { through: 'PostHashtag' });
N:M ๊ด๊ณ์ ํน์ฑ์ ์๋ก์ด ๋ชจ๋ธ์ด ๋ค์๊ณผ ๊ฐ์ด ์์ฑ๋๋ฉฐ, through ์์ฑ์ ๊ทธ ์ด๋ฆ์ ์ ์ผ๋ฉด ๋๋ค.
์๋ก ์์ฑ๋ PostHashtag ๋ชจ๋ธ์๋ ๊ฒ์๊ธ๊ณผ ํด์ํ๊ทธ์ ์์ด๋๊ฐ ์ ์ฅ๋๋ค.
N:M์์๋ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ๋ ์ฌ๋ฌ ๋จ๊ณ๋ฅผ ๊ฑฐ์ณ์ผ ํ๋ค.
์๋ฅผ ๋ค์ด #๋ ธ๋ ํด์ํ๊ทธ๋ฅผ ์ฌ์ฉํ ๊ฒ์๋ฌผ์ ์กฐํํ๋ ๊ฒฝ์ฐ, ๋จผ์ #๋ ธ๋ ํด์ํ๊ทธ๋ฅผ Hashtag ๋ชจ๋ธ์์ ์กฐํํ๊ณ , ๊ฐ์ ธ์จ ํ๊ทธ์ ์์ด๋์ธ '1'์ ๋ฐํ์ผ๋ก PostHashtag ๋ชจ๋ธ์์ hashtagId๊ฐ 1์ธ postId๋ค์ ์ฐพ์ Post ๋ชจ๋ธ์์ ํด๋น ์ ๋ณด๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ค.
๋ํ, ์๋์ผ๋ก ๋ง๋ค์ด์ง ๋ชจ๋ธ๋ค๋ ๋ค์๊ณผ ๊ฐ์ ์ ๊ทผํ ์ ์๋ค.
db.sequelize.models.PostHashtag
Reference