Node.js/Sequelize

[ORM] ๐Ÿ“š sequelize-cli ๋ชจ๋“ˆ ์‚ฌ์šฉํ•˜๊ธฐ

์ธํŒŒ_ 2021. 12. 2. 14:16

sequelize-cli

sequelize-cli ๋ชจ๋“ˆ

 

[sequelize-cli]

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ๊ตฌ์ถ•๋˜์ง€์•Š๋”๋ผ๋„, ํ”„๋กœ์ ํŠธ๋งŒ ๋ฐ›์•„์„œ ์„ค์ •ํ•˜๊ณ  ์„œ๋ฒ„์‹คํ–‰๋งŒ ํ•˜๋ฉด ์•Œ์•„์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ” ์ƒ์„ฑํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.
  • ๋ˆ„๊ตฐ๊ฐ€์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ๋ฐ›์•„์„œ ๋น ๋ฅด๊ฒŒ wasํ™˜๊ฒฝ์„ ๋Œ๋ฆด์ƒํ™ฉ์ด๋ฉด ์ข‹์€ ์„ ํƒ์ง€๋‹ค.
  • ๋ชจ๋ธ ์ฝ”๋“œ ์ž‘์„ฑ → ์ž๋™ create๋ฌธ ๋ณ€ํ™˜ → RDB ํ…Œ์ด๋ธ” ์ƒ์„ฑ

 

[sequelize-auto]

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ด๋ฏธ ๊ตฌ์ถ•ํ•œ ์ƒํƒœ๋ผ๋ฉด, ์“ธ๋ชจ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค.
  • ์ด๋ฏธ ๊ตฌ์ถ•๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ orm ๋ชจ๋ธ์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ด์ค€๋‹ค.
  • ํž˜๋“ค๊ฒŒ sql์งœ๊ณ  orm์ฝ”๋”ฉ ํ•˜๋Š” ๋‘๋ฒˆ์˜ ๋ฒˆ๊ฑฐ๋กœ์›€์„ ์—†์• ์ค€๋‹ค.
  • ์ด๋ฏธ ์งœ์—ฌ์ง„ ํ…Œ์ด๋ธ”์„ ๊ธฐ๋ฐ˜์œผ๋กœ → ์ž๋™์œผ๋กœ ๋ชจ๋ธ ์ฝ”๋“œ ์ƒ์„ฑ

sequelize-cli ์‚ฌ์šฉ ์„ค์ •

> npm i sequelize sequelize-cli mysql2
 

sequelize-cli

The Sequelize CLI. Latest version: 6.4.1, last published: 3 months ago. Start using sequelize-cli in your project by running `npm i sequelize-cli`. There are 422 other projects in the npm registry using sequelize-cli.

www.npmjs.com

 

sequelize-cli - ์ดˆ๊ธฐ ์„ค์ •

> npx sequelize init

 

sequelize init ์„ ํ•˜๊ฒŒ๋˜๋ฉด, ๋ช‡๊ฐ€์ง€ ํด๋”์™€, ํŒŒ์ผ๋“ค์ด ์ƒ์„ฑ๋œ๋‹ค.

sequelize-cli

 

config.json

  • DB ์—ฐ๊ฒฐ ์ •๋ณด๋ฅผ ์ €์žฅ
{
   "development": {
      "username": "test",
      "password": "123123",
      "database": "nodebird",
      "host": "127.0.0.1",
      "dialect": "mysql"
   },
   "test": {
      "username": "test",
      "password": "123123",
      "database": "database_test",
      "host": "127.0.0.1",
      "dialect": "mysql"
   },
   "production": {
      "username": "test",
      "password": "123123",
      "database": "database_production",
      "host": "127.0.0.1",
      "dialect": "mysql"
   }
}

 

sequelize-cli - ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ์„ฑ

> npx sequelize db:create

db:create CLI ๋ช…๋ น์„ ํ™œ์šฉํ•˜๊ฒŒ ๋˜๋ฉด DB์Šคํ‚ค๋งˆ๋ฅผ mysql์—์„œ ์ƒ์„ฑํ•˜์ง€ ์•Š๋”๋ผ๋„ ๋ช…๋ น์–ด ํ•œ์ค„๋กœ ์ƒ์„ฑํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค. 

config.json์—์„œ ์„ค์ •ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ด๋ฆ„ "nordbird"๊ฐ€ ์Šคํ‚ค๋งˆ์— ์ž๋™์œผ๋กœ ์ž˜ ์ถ”๊ฐ€๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

sequelize-cli

 

sequelize-cli - ํ…Œ์ด๋ธ” ์ƒ์„ฑ

ํ…Œ์ด๋ธ”์— ์—ฐ๊ฒฐํ•  ๋ชจ๋ธ์„ ์ž‘์„ฑํ•œ๋‹ค.

sequelize-cli

 

user.js

const Sequelize = require('sequelize');

module.exports = class User extends Sequelize.Model {
   static init(sequelize) {
      return super.init(
         {
            // ์‹œํ€„๋ผ์ด์ฆˆ๋Š” id ์ž๋™ ์ƒ์„ฑ (auto_increament)
            email: {
               type: Sequelize.STRING(40),
               allowNull: true, // null ํ—ˆ์šฉ
               unique: true, // ์ค‘๋ณต ๋น„ํ—ˆ์šฉ
            },
            nick: {
               type: Sequelize.STRING(15),
               allowNull: false,
            },
            password: {
               type: Sequelize.STRING(100), // ํ•ด์‹œ์•”ํ˜ธํ™”๋ฅผ ํ• ๋•Œ ๋ฌธ์ž๊ฐ€ ๊ธธ์–ด์ง€๋‹ˆ, ์—ฌ์œ ์žˆ๊ฒŒ ์šฉ๋Ÿ‰์„ ์žก์•„์ค€๋‹ค.
               allowNull: true, // ์นด์นด์˜ค ๊ฐ™์€ api๋กœ ๋กœ๊ทธ์ธํ• ๋•Œ, ์ง์ ‘ ํšŒ์›๊ฐ€์ž…ํ•ด์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ ์„ค์ •ํ•œ๊ฒŒ ์•„๋‹ˆ๋‹ˆ ๋น„๋ฒˆ์€ null์ผ์ˆ˜๋„ ์žˆ๋‹ค.
            },
            provider: {
               //? ์–ด๋””๋กœ๋ถ€ํ„ฐ ๋กœ๊ทธ์ธ ํ–ˆ๋Š”์ง€ ์ •๋ณด
               type: Sequelize.STRING(10),
               allowNull: false,
               defaultValue: 'local', // ๋กœ์ปฌ / ์นด์นด์˜ค / ๋„ค์ด๋ฒ„ / ๊ตฌ๊ธ€ ๋กœ๊ทธ์ธ ์„ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•œ ํ•„๋“œ
            },
            snsId: {
               //? sns์œผ๋กœ ๋กœ๊ทธ์ธํ• ๊ฒฝ์šฐ sns์•„์ด๋”” ์ €์žฅ ํ•„๋“œ
               type: Sequelize.STRING(30),
               allowNull: true,
            },
         },
         {
            sequelize,
            timestamps: true, // createdAt, udaptedAt ์ž๋™ ์ƒ์„ฑ
            underscored: false,
            modelName: 'User', // ๋ชจ๋ธ๋ช…
            tableName: 'users', // ํ…Œ์ด๋ธ”๋ช…
            paranoid: true, // deletedAt ์ž๋™ ์ƒ์„ฑ
            charset: 'utf8', // ํ•œ๊ธ€ ์ž…๋ ฅ ์„ค์ •
            collate: 'utf8_general_ci',
         },
      );
   }

   static associate(db) {
      /*
       * ๋”ฐ๋กœ ์™ธ๋ž˜ํ‚ค๋ฅผ ์ง€์ •ํ•˜์ง€์•Š์œผ๋ฉด, ๋ชจ๋ธ๋ช…+๊ธฐ๋ณธํ‚ค ์ปฌ๋Ÿผ์ด ์ƒ์„ฑ๋˜์„œ ์ž๋™์œผ๋กœ ์—ฐ๊ฒฐ๋œ๋‹ค.
       * ์ฆ‰, User์™€ id๊ฐ€ ํ•ฉ์ณ์ ธ์„œ Userid๋ผ๋Š” ํ•„๋“œ๊ฐ€ ์ƒ๊ฒจ์„œ ์ž๋™์—ฐ๊ฒฐํ•ด์ค€๋‹ค.
       * db.User.hasMany(db.Post, { foreignKey: 'Userid', targetKey: 'id' })
       */
      db.User.hasMany(db.Post);

      /*
       * ํŒ”๋กœ์›Œ ์™€ ํŒ”๋กœ์ž‰ ๊ด€๊ณ„
       * ์ž˜ ์ƒ๊ฐํ•ด๋ณด์ž. ํŒ”๋กœ์›Œ,ํŒ”๋กœ์ž‰ ์ •๋ณด๋Š” ๋ชจ๋‘ User๋ชจ๋ธ์—์„œ ๊ฐ€์ ธ์˜ค๋Š” ์ •๋ณด๋“ค์ด๋‹ค. ๋”ฐ๋ผ์„œ ์ž๊ธฐ์ž์‹ ์„ ์ฐธ์กฐํ•˜๋Š” ๊ด€๊ณ„๊ฐ€ ์ƒ๊ฒจ๋‚œ๋‹ค.
       * Follow๋ผ๋Š” ์ค‘๊ฐ„ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค์–ด์„œ M:N๊ด€๊ณ„๋ฅผ ํ˜•์„ฑํ•œ๋‹ค.
       */
      db.User.belongsToMany(db.User, {
         foreignKey: 'followingId', // ํŒ”๋กœ์ž‰ ๋‹นํ•œ ์‚ฌ๋žŒ. ์ฃผ์ฒด (์ฆ‰, ์œ ๋ช…ํ•œ ์‚ฌ๋žŒ. ์—ฐ์˜ˆ์ธ, ์œ ํŠœ๋ฒ„)
         as: 'Followers', // ๋ชจ๋ธ์ฟผ๋ฆฌ์—์„œ ์‚ฌ์šฉ๋  ํ•„๋“œ ๋ณ„๋ช…. sql๋ฌธ์˜ ํ•„๋“œ๋ช… as ๋ณ„๋ช… ๊ณผ ๊ฐ™๋‹ค.
         through: 'Follow',
      });
      db.User.belongsToMany(db.User, {
         foreignKey: 'followerId', // ๊ทธ ์‚ฌ๋žŒ์„ ํŒ”๋กœ์›Œ ํ•œ ์‚ฌ๋žŒ๋“ค
         as: 'Followings',
         through: 'Follow',
      });
   }
};
followerId (ํŒ”๋กœ์›Œ๋ฅผ ํ•œ ์‚ฌ๋žŒ) followingId (ํŒ”๋กœ์ž‰ ๋‹นํ•œ ์‚ฌ๋žŒ. ์ฃผ์ฒด.) (์ฆ‰, ์œ ๋ช…ํ•œ ์‚ฌ๋žŒ. ์—ฐ์˜ˆ์ธ, ์œ ํŠœ๋ฒ„)
1 3
4 3
5 3
2 1
1 2
4 1
  • id๊ฐ€ 3์ธ ์‚ฌ๋žŒ์€ id๊ฐ€ 1,4,5์ธ ์‚ฌ๋žŒ์—๊ฒŒ ํŒ”๋กœ์ž‰ ๋‹นํ–ˆ๋‹ค. ์ฆ‰, id๊ฐ€ 3์ธ ์‚ฌ๋žŒ์€ ํŒ”๋กœ์›Œ ์ˆ˜๊ฐ€ 3๋ช….
  • id๊ฐ€ 1์ธ ์‚ฌ๋žŒ์€ id๊ฐ€ 3๊ณผ 2์ธ ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐ ํ•˜๊ณ  ์ž‡๋‹ค. ์ฆ‰ id๊ฐ€ 1์ธ ์‚ฌ๋žŒ์ด ํŒ”๋กœ์šฐ ํ•œ ์œ ํŠœ๋ฒ„๊ฐ€ 2๋ช…
ํŒ”๋กœ์šฐ
์นœ๊ตฌ๋ฅผ ๋งŒ๋“œ๋Š” ํ–‰์œ„. ์ธ์Šคํƒ€์—์„œ ํŒ”๋กœ์šฐ๋ฅผ ํ•˜๋ฉด ์นœ์ถ” ์ถ”๊ฐ€๋ฅผ ์˜๋ฏธ.

ํŒ”๋กœ์›Œ
์ƒ๋Œ€๋ฐฉ์ด ๋‚˜๋ฅผ ๋จผ์ € ํŒ”๋กœ์šฐ ํ•˜๊ฒŒ๋˜๋ฉด ํŒ”๋กœ์›Œ์— ์ˆซ์ž๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค. ์ฆ‰ ๋‚˜๋ฅผ ์นœ๊ตฌ๋กœ ์ถ”๊ฐ€ํ•œ ์‚ฌ๋žŒ์ด๋ž€ ์˜๋ฏธ์ด๋‹ค.

ํŒ”๋กœ์ž‰
๋‚ด๊ฐ€ ๋จผ์ € ์ƒ๋Œ€๋ฐฉ์„ ํŒ”๋กœ์šฐ ํ•˜๊ฒŒ ๋˜๋ฉด ํŒ”๋กœ์ž‰์— ์ˆซ์ž๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค. ์ฆ‰ ๋‚ด๊ฐ€ ์นœ๊ตฌ๋กœ ์ถ”๊ฐ€ํ•œ ์‚ฌ๋žŒ์ด๋ž€ ์˜๋ฏธ์ด๋‹ค.

 

post.js

const Sequelize = require('sequelize');

module.exports = class Post extends Sequelize.Model {
   static init(sequelize) {
      return super.init(
         {
            content: {
               type: Sequelize.STRING(140),
               allowNull: false,
            },
            img: {
               type: Sequelize.STRING(200),
               allowNull: true,
            },
         },
         {
            sequelize,
            timestamps: true,
            underscored: false,
            modelName: 'Post',
            tableName: 'posts',
            paranoid: false, // ๊ฒŒ์‹œ๊ธ€ ์‚ญ์ œํ•˜๋ฉด, ํœด์ง€ํ†ต ์•ˆ๊ฑฐ์น˜๊ณ  ์นผ์‚ญ์ œ
            charset: 'utf8mb4', // ์ด๋ชจํ‹ฐ์ฝ˜ ๊ฐ€๋Šฅ ์„ค์ •
            collate: 'utf8mb4_general_ci',
         },
      );
   }

   static associate(db) {
      db.Post.belongsTo(db.User);
      db.Post.belongsToMany(db.Hashtag, { through: 'PostHashtag' });
   }
};

 

hashtag.js

const Sequelize = require('sequelize');

module.exports = class Hashtag extends Sequelize.Model {
   static init(sequelize) {
      return super.init(
         {
            title: {
               type: Sequelize.STRING(15),
               allowNull: false,
               unique: true,
            },
         },
         {
            sequelize,
            timestamps: true,
            underscored: false,
            modelName: 'Hashtag',
            tableName: 'hashtags',
            paranoid: false,
            charset: 'utf8mb4',
            collate: 'utf8mb4_general_ci',
         },
      );
   }

   static associate(db) {
      db.Hashtag.belongsToMany(db.Post, { through: 'PostHashtag' });
   }
};

 

index.js : ๋ชจ๋ธ

  •  ํ…Œ์ด๋ธ” ์—ฐ๊ฒฐ์ •์˜
const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env];

//? ๋ชจ๋ธ ๋ชจ๋“ˆ
const User = require('./user');
const Post = require('./post');
const Hashtag = require('./hashtag');

const db = {};
const sequelize = new Sequelize(config.database, config.username, config.password, config);

//? db๊ฐ์ฒด์— ๋ชจ๋ธ ์ •๋ณด๋“ค ๋„ฃ์Œ
db.sequelize = sequelize;
db.User = User;
db.Post = Post;
db.Hashtag = Hashtag;

//? ๋ชจ๋ธ - ํ…Œ์ด๋ธ” ์—ฐ๊ฒฐ
User.init(sequelize);
Post.init(sequelize);
Hashtag.init(sequelize);

//? ๋ชจ๋ธ ๊ด€๊ณ„ ์„ค์ •
User.associate(db);
Post.associate(db);
Hashtag.associate(db);

module.exports = db;

 

sequelize-cli - ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ

๋ชจ๋ธ์„ ์„ค์ •ํ•ด์คฌ์œผ๋ฉด, ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ ์‹œ์ผœ ์—ฐ๊ฒฐํ•˜๋„๋ก ํ•˜์ž.

 

app.js

//* DB ์—ฐ๊ฒฐ ๋ฐ ์ƒ์„ฑ
sequelize
    .sync({ force: false })
    .then(() => {
    	console.log('๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์„ฑ๊ณต');
    })
    .catch(err => {
    	console.error(err);
    });

[sequelize.sync ์˜ต์…˜]  

  • force: true
    • ๋ชจ๋ธ์„ ์ˆ˜์ •ํ•˜๋ฉด, ์ด๋ฅผ db์— ๋ฐ˜์˜ํ•˜๊ธฐ ์œ„ํ•œ ์˜ต์…˜์ด๋‹ค.
    • ๋‹จ, ํ…Œ์ด๋ธ”์„ ์ง€์› ๋‹ค ๋‹ค์‹œ ์ƒ์„ฑํ•˜๋Š” ๊ฑฐ๋ผ์„œ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚ ์•„๊ฐ„๋‹ค.

  • alter: true
    • ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ ํ…Œ์ด๋ธ”์„ ์—…๋ฐ์ดํŠธ ํ• ์ˆ˜์žˆ๋‹ค.
    • ๋‹ค๋งŒ, ํ•„๋“œ๋ฅผ ์ƒˆ๋กœ ์ถ”๊ฐ€ํ• ๋•Œ ํ•„๋“œ๊ฐ€ notnull์ด๋ฉด ์ œ์•ฝ์กฐ๊ฑด์— ๋”ฐ๋ผ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜๋Š” ๋“ฑ ๋Œ€์ฒ˜๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค.
    • ๊ทธ๋ƒฅ ์›Œํฌ๋ฒค์น˜ ๋“ค์–ด๊ฐ€์„œ ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ๋ชจ๋ธ์„ ๋…ธ๊ฐ€๋‹ค๋ผ๋„ ์ˆ˜์ •ํ•˜๋Š”๊ฒƒ์ด ๋” ์•ˆ์ „ํ•˜๋‹ค.
    • ์ด๋Ÿฌํ•œ ์˜ต์…˜์€ ์–ด๋””๊นŒ์ง€๋‚˜ developmentํ™˜๊ฒฝ์ผ๋•Œ๋งŒ ์ ์šฉํ•œ๋‹ค.

 

์„œ๋ฒ„๋ฅผ ์‹คํ–‰ ์‹œํ‚ค๊ณ  mysql์›Œํฌ๋ฒค์น˜์— ๋“ค์–ด๊ฐ€์„œ ํ™•์ธํ•ด๋ณด๋ฉด, ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋˜์–ด์žˆ๊ณ , ์™ธ๋ž˜ํ‚ค ๊ด€๊ณ„๋„ ์ž˜ ์„ค์ •๋จ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

sequelize-cli
sequelize-cli

 

๋‹ค์Œ์€ ์ƒ์„ฑํ•œ ํ…Œ์ด๋ธ”์˜ ERD ์ด๋‹ค.

sequelize-cli