Node.js/Sequelize

[ORM] ๐Ÿ“š ์‹œํ€„๋ผ์ด์ฆˆ ์ดˆ๊ธฐ ๊ตฌ์„ฑ & DB ์—ฐ๊ฒฐ (MySQL)

์ธํŒŒ_ 2021. 11. 10. 10:04

์‹œํ€„๋ผ์ด์ฆˆ

Sequelize ๋ž€?

์‹œํ€„๋ผ์ด์ฆˆ๋Š” nodejs์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ, ORM(Object-relational Mapping)์œผ๋กœ ๋ถ„๋ฅ˜๋œ๋‹ค.

sql ์ž‘์„ฑ๋ฒ•์„ ๋ชจ๋ฅด๋”๋ผ๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

ORM์ด๋ž€ ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋งคํ•‘(์—ฐ๊ฒฐ)ํ•ด์ฃผ๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.
๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•œ๋‹ค.

์‹œํ€„๋ผ์ด์ฆˆ๋Š” MySQL ์™ธ์—๋„ MariaDB, PostgreSQL ๋“ฑ๋“ฑ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๋„ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

๋ฌธ๋ฒ•์ด ์–ด๋Š ์ •๋„ ํ˜ธํ™˜๋˜๋ฏ€๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ๋‹ค๋ฅธ SQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ์ „ํ™˜ํ•  ๋•Œ๋„ ํŽธ๋ฆฌํ•˜๋‹ค.

 

์‹œํ€„๋ผ์ด์ฆˆ๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ตฌ๋ฌธ์„ ์•Œ์•„์„œ SQL๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค.

๊ทธ๋ž˜์„œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋งŒ์œผ๋กœ MySQL์„ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ์–ด, SQL ์–ธ์–ด๋ฅผ ๋ชฐ๋ผ๋„ MySQL์„ ์–ด๋Š ์ •๋„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด  ์žˆ๋‹ค.

์‹œํ€„๋ผ์ด์ฆˆ


์‹œํ€„๋ผ์ด์ฆˆ ํ”„๋กœ์ ํŠธ ๊ตฌ์„ฑ

์‹œํ€„๋ผ์ด์ฆˆ์— ํ•„์š”ํ•œ morgan, sequelize, sequelize-cli, mysql2 ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•œ๋‹ค.

๋˜, ๊ฐœ๋ฐœ ๋ชจ๋“œ๋กœ nodemon์„ ์„ค์น˜ํ•ด์ฃผ์ž.

$ npm i express morgan sequelize sequelize-cli mysql2
$ npm i -D nodemon

ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ–ˆ๋‹ค๋จผ ์‹œํ€„๋ผ์ด์ฆˆ๋ฅผ initํ•˜์—ฌ ์‹œํ€„๋ฆฌ์ด์ฆˆ ๊ตฌ์กฐ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

$ npx sequelize init

์‹œํ€„๋ผ์ด์ฆˆ

๋ช…๋ น ์‹คํ–‰ ์ดํ›„์— ํด๋”ํŠธ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๋ณด๋ฉด config, models, migrations, seeders ํด๋”๊ฐ€ ์ƒ์„ฑ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ ์šฐ๋ฆฌ๊ฐ€ ํ”„๋กœ์ ํŠธ์— ์“ฐ์ผ ํด๋”๋“ค๋„ ์ง์ ‘ ๋งŒ๋“ค์–ด์ฃผ๋ฉด ์ข‹๋‹ค. public, routes, views, app.js ๋“ฑ๋“ฑ

 

models ํด๋” ๋‚ด์˜ index.js๊ฐ€ ์ƒ์„ฑ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

sequelize-cli๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ด์ฃผ๋Š” ์ฝ”๋“œ๋Š” ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ๋•Œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ํ•„์š”์—†๋Š” ๋ถ€๋ถ„๋„ ๋งŽ์œผ๋ฏ€๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์ค€๋‹ค.

 

models/index.js

const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development'; // ์ง€์ •๋œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์—†์œผ๋ฉด '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; 

// ๋ชจ๋“ˆ๋กœ ๊บผ๋‚ธ๋‹ค.
module.exports = db;

 

config/config.json

์‹œํ€„๋ผ์ด์ฆˆ

์œ„ ์ฝ”๋“œ์˜ models/index.js ํŒŒ์ผ์—์„œ env ๋ณ€์ˆ˜์— process.env.NODE_ENV ๋˜๋Š” 'development'๋กœ ์„ค์ •ํ•ด ์ฃผ์—ˆ๋‹ค.

process.env.NODE_ENV ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๋”ฐ๋กœ ์ง€์ •ํ•˜์ง€ ์•Š๋Š”ํ•œ ๋ณ€์ˆ˜env์— ๊ธฐ๋ณธ์ ์œผ๋กœ 'development'๊ฐ€ ์˜ค๊ธฐ ๋•Œ๋ฌธ์—, development์˜ ํ™˜๊ฒฝ ์„ค์ •์„ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋œ๋‹ค.

์ถ”ํ›„ ๋ฐฐํฌํ•  ๋•Œ๋Š” process.env.NODE_ENV๋ฅผ production์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.


์‹œํ€„๋ผ์ด์ฆˆ MySQL ์—ฐ๊ฒฐ

์ด์ œ ์‹œํ€„๋ผ์ด์ฆˆ๋ฅผ ํ†ตํ•ด ์ต์Šคํ”„๋ ˆ์Šค ์•ฑ๊ณผ MySQL์„ ์—ฐ๊ฒฐํ•ด๋ณด์ž.

app.js๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ต์Šคํ”„๋ ˆ์Šค์™€ ์‹œํ€„๋ผ์ด์ฆˆ ์—ฐ๊ฒฐ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•œ๋‹ค.

 

app.js

const express = require('express');
const path = require('path');
const morgan = require('morgan');

// index.js์— ์žˆ๋Š” db.sequelize ๊ฐ์ฒด ๋ชจ๋“ˆ์„ ๊ตฌ์กฐ๋ถ„ํ•ด๋กœ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.
const { sequelize } = require('./models');
const app = express();

app.set('port', process.env.PORT || 3000);

// PUG ์„ค์ •
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

sequelize.sync({ force: false })
   .then(() => {
      console.log('๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ๋จ.');
   }).catch((err) => {
      console.error(err);
   });

app.use(morgan('dev')); // ๋กœ๊ทธ
app.use(express.static(path.join(__dirname, 'public'))); // ์š”์ฒญ์‹œ ๊ธฐ๋ณธ ๊ฒฝ๋กœ ์„ค์ •
app.use(express.json()); // json ํŒŒ์‹ฑ
app.use(express.urlencoded({ extended: false })); // uri ํŒŒ์‹ฑ

// ์ผ๋ถ€๋Ÿฌ ์—๋Ÿฌ ๋ฐœ์ƒ์‹œํ‚ค๊ธฐ TEST์šฉ
app.use((req, res, next) => {
   const error = new Error(`${req.method} ${req.url} ๋ผ์šฐํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.`);
   error.status = 404;
   next(error);
});

// ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ฏธ๋“ค์›จ์–ด
app.use((err, req, res, next) => {
   // ํ…œํ”Œ๋ฆฟ ๋ณ€์ˆ˜ ์„ค์ •
   res.locals.message = err.message;
   res.locals.error = process.env.NODE_ENV !== 'production' ? err : {}; // ๋ฐฐํฌ์šฉ์ด ์•„๋‹ˆ๋ผ๋ฉด err์„ค์ • ์•„๋‹ˆ๋ฉด ๋นˆ ๊ฐ์ฒด

   res.status(err.status || 500);
   res.render('error'); // ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ๋ Œ๋”๋ง ํ•˜์—ฌ ์‘๋‹ต
});

// ์„œ๋ฒ„ ์‹คํ–‰
app.listen(app.get('port'), () => {
   console.log(app.get('port'), '๋ฒˆ ํฌํŠธ์—์„œ ๋Œ€๊ธฐ ์ค‘');
});

 

[sequelize.sync() ์˜ต์…˜]

์‹œํ€„๋ผ์ด์ฆˆ

 

์ด์ œ npx nodemon app.js๋กœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ด๋ณด์ž.

์‹œํ€„๋ผ์ด์ฆˆ

์œ„์™€ ๊ฐ™์€ ๋ฉ”์‹œ์ง€๊ฐ€ ๋œจ๋ฉด ์„ฑ๊ณต์ด๋‹ค.

์—ฐ๊ฒฐ์— ์‹คํŒจํ•œ ๊ฒฝ์šฐ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ ๋กœ๊น…๋œ๋‹ค.

์—๋Ÿฌ๋Š” ์ฃผ๋กœ MySQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜, ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ‹€๋ ธ๊ฑฐ๋‚˜, ์„ค์ • ํŒŒ์ผ์„ ์ž˜๋ชป ๋ถˆ๋Ÿฌ์™”์„ ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค.


ERR_INVALID_ARG_TYPE

๋งŒ์ผ ๋‹ค์Œ๊ณผ  ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ ํ•œ๋‹ค๋ฉด,

(node:16387) [SEQUELIZE0004] DeprecationWarning: A boolean value was passed to options.operatorsAliases. This is a no-op with v5 and should be removed.
internal/crypto/hash.js:70
    throw new ERR_INVALID_ARG_TYPE('data',
    ^

TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be one of type string, Buffer, TypedArray, or DataView. Received type number
    at Hash.update (internal/crypto/hash.js:70:11)
    at sha1 (/Users/noel/Documents/project/node/node.js_book/6.1/learn-sequelize/node_modules/mysql2/lib/auth_41.js:31:8)
    at Object.token [as calculateToken] (/Users/noel/Documents/project/node/node.js_book/6.1/learn-sequelize/node_modules/mysql2/lib/auth_41.js:65:18)
    at new HandshakeResponse (/Users/noel/Documents/project/node/node.js_book/6.1/learn-sequelize/node_modules/mysql2/lib/packets/handshake_response.js:28:26)
    at ClientHandshake.sendCredentials (/Users/noel/Documents/project/node/node.js_book/6.1/learn-sequelize/node_modules/mysql2/lib/commands/client_handshake.js:51:31)
    at ClientHandshake.handshakeInit (/Users/noel/Documents/project/node/node.js_book/6.1/learn-sequelize/node_modules/mysql2/lib/commands/client_handshake.js:136:12)
    at ClientHandshake.execute (/Users/noel/Documents/project/node/node.js_book/6.1/learn-sequelize/node_modules/mysql2/lib/commands/command.js:39:22)
    at Connection.handlePacket (/Users/noel/Documents/project/node/node.js_book/6.1/learn-sequelize/node_modules/mysql2/lib/connection.js:408:32)
    at PacketParser.onPacket (/Users/noel/Documents/project/node/node.js_book/6.1/learn-sequelize/node_modules/mysql2/lib/connection.js:70:12)
    at PacketParser.executeStart (/Users/noel/Documents/project/node/node.js_book/6.1/learn-sequelize/node_modules/mysql2/lib/packet_parser.js:75:16)
    at Socket.<anonymous> (/Users/noel/Documents/project/node/node.js_book/6.1/learn-sequelize/node_modules/mysql2/lib/connection.js:77:25)
    at Socket.emit (events.js:210:5)
    at addChunk (_stream_readable.js:308:12)
    at readableAddChunk (_stream_readable.js:289:11)
    at Socket.Readable.push (_stream_readable.js:223:10)
    at TCP.onStreamRead (internal/stream_base_commons.js:182:23) {
  code: 'ERR_INVALID_ARG_TYPE'
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! learn-sequelize@0.0.0 start: `node ./bin/www`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the learn-sequelize@0.0.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/noel/.npm/_logs/2019-10-26T16_07_07_216Z-debug.log

config/config.json์—์„œ

password ๊ฐ€ "123456" ์ด์–ด์•ผ ํ•˜๋Š”๋ฐ 123456 ์ธ์ง€ ํ™•์ธํ•œ๋‹ค.

json์€ ๋ฌด์กฐ๊ฑด ๋ฌธ์ž์—ด!!!