Notice
Recent Posts
Recent Comments
Link
ยซ   2024/05   ยป
์ผ ์›” ํ™” ์ˆ˜ ๋ชฉ ๊ธˆ ํ† 
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
๊ด€๋ฆฌ ๋ฉ”๋‰ด

lgvv98

[Node.js] #7 MySQL ๋ณธ๋ฌธ

๐Ÿ›ฐ๏ธ Node.js

[Node.js] #7 MySQL

๐Ÿฅ• ์บ๋Ÿฟ๋งจ 2023. 7. 23. 15:31

MySQL

 

๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€์ˆ˜์— ์ €์žฅํ–ˆ๋‹ค๋Š” ๊ฒƒ์€ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•œ๋‹ค๋Š” ๊ฒƒ.

๋ฉ”๋ชจ๋ฆฌ๋Š” ํœ˜๋ฐœ์„ฑ์ด๋ผ ์ข…๋ฃŒํ•˜๋ฉด ์‚ฌ๋ผ์ง.

์˜๊ตฌ์ €์žฅ์„ ์œ„ํ•ด์„œ๋Š” ๋ฐ๋ฒ ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

๋‹ค์–‘ํ•œ DB๊ฐ€ ์žˆ์ง€๋งŒ MySQL๊ณผ ๋ชฝ๊ณ ๋””๋น„ ๋‘๊ฐ€์ง€๋ฅผ ์ด์šฉํ•  ์˜ˆ์ •.

 

# mysql ์„ค์น˜

brew install mysql
brew services start mysql
mysql_secure_installation

 

# ์›Œํฌ๋ฒค์น˜ ์„ค์น˜

์ฝ˜์†”๋กœ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํ•œ๋ˆˆ์— ๋ณด๊ธฐ์— ๋ฌด๋ฆฌ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์›Œํฌ๋ฒค์น˜๋ผ๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ๋ฒ  ๋‚ด๋ถ€์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์„œ ํŽธ๋ฆฌํ•จ.

 

 

# ํ…Œ์ด๋ธ” ์ƒ์„ฑํ•˜๊ธฐ

๋Œ€๋ถ€๋ถ„ ์•„๋Š”๊ฑฐ๋ผ ๋Œ€์ถฉ ๋‚˜๋งŒ ์•Œ์•„๋ณด๋„๋ก ์ •๋ฆฌ

 

VARCHAR(์ž๋ฆฟ์ˆ˜): ๊ฐ€๋ณ€๊ธธ์ด 

CHAR(์ž๋ฆฟ์ˆ˜): ๊ณ ์ •๊ธธ์ด

 - ๋‘˜๋‹ค ์ž๋ฆฟ์ˆ˜๊ฐ€ 10์ด๋ผ๊ณ  ํ•˜๋ฉด, VARCHAR๋Š” 0~10 CHAR๋Š” ๋ถ€์กฑํ•œ ๊ณต๊ฐ„์„ ์ŠคํŽ˜์ด์Šค๋กœ ์ฑ„์›Œ ๋„ฃ์Œ.

TEXT: ๊ธด ๊ธ€์„ ์‚ฌ์šฉํ•  ๋•Œ ์‚ฌ์šฉ

 - VARCHAR๋Š” ์ˆ˜๋ฐฑ ์ž ์ด๋‚ด์˜ ๋ฌธ์ž์—ด์— ์‚ฌ์šฉ.

TINYINT: -128๋ถ€ํ„ฐ 127๊นŒ์ง€์˜ ์ •์ˆ˜๋ฅผ ์ €์žฅํ•  ๋•Œ ์‚ฌ์šฉ. 1๋˜๋Š” 0๋งŒ ์ €์žฅํ•œ๋‹ค๋ฉด boolean๊ฐ™์€ ์—ญํ• .

DATETIME์€ ๋‚ ์งœ์™€ ์‹œ๊ฐ„์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๋‹ด๋Š”๋‹ค.

AUTO_INCREMENT: ์ƒ์„ฑ๋  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์ˆซ์ž 1 ์˜ฌ๋ฆผ.

ZEROFILL: ์ˆซ์ž์˜ ์ž๋ฆฟ์ˆ˜๊ฐ€ ๊ณ ์ •๋˜์–ด ์žˆ์„ ๋•Œ ์‚ฌ์šฉ๋˜์–ด ์žˆ์Œ. ZEROFILL์„ ํ•˜๋ฉด ์ˆซ์ž๊ฐ€ ๋น„์—ˆ์„ ๋•Œ ๋ชจ๋‘ 0์„ ๋„ฃ๋Š”๋‹ค

 - INT(4)์ธ๋ฐ ์ˆซ์ž 1์„ ๋„ฃ์—ˆ๋‹ค๋ฉด 0001์ด ๋œ๋‹ค.

DEFAULT: MySQL ๊ธฐ๋ณธ๊ฐ’์„ ๋„ฃ๋Š”๋‹ค.

ํ•ด๋‹น ์ปฌ๋Ÿผ์ด ๊ธฐ๋ณธํ‚ค์ธ ๊ฒฝ์šฐ์— PRIMARY KEY ์˜ต์…˜์„ ์„ค์ •

UNIQUE_INDEX: ๊ณ ์œ ํ•ด์•ผ ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ์˜ต์…˜์ด๋ฉฐ, PRIMARY KEY๋‚˜ UNIQUE INDEX์˜ ๊ฒฝ์šฐ์—๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ๋ณ„๋„๋กœ ์ปฌ๋Ÿผ์„ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ ์กฐํšŒ ์‹œ ์†๋„๊ฐ€ ๋นจ๋ผ์ง„๋‹ค. ๊ธฐ๋ณธํ‚ค๋„ ์‚ฌ์‹ค ๊ณ ์œ ํ•ด์•ผ ํ•˜์ง€๋งŒ UNIQUE_INDEX๋ฅผ ์ž๋™์œผ๋กœ ํฌํ•จํ•˜๋ฏ€๋กœ ๋”ฐ๋กœ ์ ์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

COMMENT ํ…Œ์ด๋ธ”์— ๋Œ€ํ•œ ๋ณด์ถฉ ์„ค๋ช…์„ ์˜๋ฏธ ํ…Œ์ด๋ธ”์ด ๋ฌด์Šจ ์—ญํ• ์„ ํ•˜๋Š”์ง€ ์ ์–ด๋‘๋ฉฐ ๋˜๊ณ , ํ•„์ˆ˜๋Š” ์•„๋‹ˆ๋‹ค.

ENGINE์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์˜๋ฏธ๊ฐ€ ์žˆ์ง€๋งŒ MyISAM๊ณผ InnoDB ์ œ์ผ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค. ์ด๋ฒˆ์—๋Š” InnoDB๋ฅผ ์—”์ง„์œผ๋กœ ์‚ฌ์šฉ.

 

์›Œํฌ๋ฒค์น˜๋ผ๋Š” ํˆด์„ ์“ธ๊ฑด๋ฐ, MySQL์„ ์ง์ ‘ ์ž‘์„ฑํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์ผ๋‹จ ๋…ธ๋“œjs ์–ด๋–ป๊ฒŒ ์“ฐ๋Š”์ง€๊ฐ€ ์ดˆ์ ์ด๋ผ ์›จํฌ๋ฒค์น˜ ํˆด๋กœ ๋š๋”ฑ๋š๋”ฑ ๋งŒ๋“ค์–ด๋ณด์ž.

 

์›Œํฌ๋ฒค์น˜ ์ด๋ฏธ์ง€

 

PK: ํ”„๋ผ์ด๋จธ๋ฆฌ ํ‚ค ์—ฌ๋ถ€

NN: ๋นˆ์นธ์„ ํ—ˆ์šฉํ• ์ง€ ์—ฌ๋ถ€

UQ(์œ ๋‹ˆํฌ ์ธ๋ฑ์Šค)

UN์€ Unsigned, ZF๋Š” ์ œ๋กœ ํ•„

Default/Expression์€ ๊ธฐ๋ณธ๊ฐ’์„ ์„ค์ •ํ•ด๋‘๋Š”๊ฒƒ

 

์ฝ”๋ฉ˜ํŠธํ…Œ์ด๋ธ”

Foreign Keys์—์„œ CASCADE๋ฅผ ํ•ด๋‘๋ฉด ๋‹ค๋ฅธ์ชฝ์—์„œ ์ง€์šฐ๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ์ฝ”๋ฉ˜ํŠธ ํ…Œ์ด๋ธ”์—๋„ ๋ฐ˜์˜๋œ๋‹ค.

 

# CRUD ์ƒ์„ฑํ•˜๊ธฐ

Create, Read, Update, Delete์˜ ์•ฝ์ž๋กœ.

 

ํŽ˜์ด์ง€ ๋„ค์ด์…˜ ๊ตฌํ˜„์— ํ•„์š”ํ•œ OFFSET๋งŒ ๋ณด์ž.

OFFSET [๊ฑด๋„ˆ๋›ธ ์ˆซ์ž]

์ฒ˜์Œ 1~20์„ ์กฐํšŒํ•˜๋ฉด ๋‹ค์Œ์€ 21์—์„œ 40๊นŒ์ง€ ์กฐํšŒํ•œ๋‹ค

mysql> 
SELECT id, name FROM nodejs.users
ORDER BY age DESC
LIMIT 1 
OFFSET 1;

 

update

UPDATE nodejs.users
SET comment = '๋ฐ”๊ฟ€ ๋‚ด์šฉ' WHERE id = 2;

 

delete

DELETE FROM nodejs.users WHERE id = 2'

 

# ์‹œํ€„๋ผ์ด์ฆˆ ์‚ฌ์šฉํ•˜๊ธฐ

 

์‹œํ€„๋ผ์ด์ฆˆ๋Š” ORM(Object-relational Mapping)์œผ๋กœ ๋ถ„๋ฅ˜๋œ๋‹ค. ORM์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฆด๋ ˆ์ด์…˜์„ ๋งคํ•‘ํ•ด์ฃผ๋Š” ๋„๊ตฌ

์‹œํ€„๋ผ์ด์ฆˆ๋ฅผ ๋‹จ์ง€ MySQL๊ณผ ๊ฐ™์ด์จ์•ผํ•˜๋Š”๊ฑด ์•„๋‹ˆ๋‹ค. MariaDB, PostgreSQL, SQLite, MSSQL ๋“ฑ ๋‹ค๋ฅธ ๋ฐ๋ฒ ๋ž‘๋„ ๊ฐ™์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‹ค.์‹œํ€„๋ผ์ด์ฆˆ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” SQL์–ธ์–ด๋ฅผ ์ง์ ‘ ์‚ฌ์šฉ์•ˆํ•ด๋„ ์ž์Šค๋กœ ๋ฐ”๊ฟ”์ฃผ๊ธฐ ๋•Œ๋ฌธ.

 

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

sequelize-cli๋Š” ์‹œํ€„๋ผ์ด์ฆˆ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ํŒจํ‚ค์ง€์ด๋ฏ€๋กœ mysql2๋Š” MySQL๊ณผ ์‹œํ€„๋ผ์ด์ฆˆ๋ฅผ ์ด์–ด์ฃผ๋Š” ๋“œ๋ผ์ด๋ฒ„์ด๋‹ค.

mysql2 ์ž์ฒด๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ”„๋กœ๊ทธ๋žจ์ด ์•„๋‹ˆ๋ฏ€๋กœ ์˜คํ•ดํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

 

์„ค์น˜ ์™„๋ฃŒ ํ›„ sequelize init ๋ช…๋ น์–ด๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์•ˆ๋œ๋‹ค. ์ „์—ญ ์„ค์น˜ ์—†์ด ๋ช…๋ น์–ด๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์•ž์— npx๋ฅผ ๋ถ™์ด๋ฉด ๋œ๋‹ค.

npx sequelize init

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

 

์ด๊ฑด ๋’ค์— ๋‚˜์˜ค๋ฏ€๋กœ ๊ทธ๋•Œ index.js ํŒŒ์ผ์„ ๋ณด์ž

MySQL์— ์ •์˜ํ•œ ๊ฑฐ ์‹œํ€„๋ผ์ด์ฆˆ์—๋„ ์ •์˜ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

 

 

const Sequelize = require('sequelize');

module.exports = class User extends Sequelize.Model {
  static init(sequelize) {
    return super.init({
      name: {
        type: Sequelize.STRING(20),
        allowNull: false,
        unique: true,
      },
      age: {
        type: Sequelize.INTEGER.UNSIGNED,
        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 initiate ๋ฉ”์„œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์—ฐ๊ฒฐ๋˜๋Š” ์˜ต์…˜์œผ๋กœ db.sequelize ๊ฐ์ฒด๋ฅผ ๋„ฃ์–ด์•ผ ํ•œ๋‹ค.
      timestamps: false, // โœ… true ์‹œํ€„๋ผ์ด์ฆˆ๊ฐ€ ์ž๋™์œผ๋กœ create_at๊ณผ update_at, timestamps ์†์„ฑ์ด ํ•„์š” ์—†๋‹ค,
      underscored: false, // โœ… ์‹œํ€„๋ผ์ด์ฆˆ ๊ธฐ๋ณธ์ ์œผ๋กœ ํ…Œ์ด๋ธ”๋ช…๊ณผ ์นผ๋Ÿผ๋ช…์„ ์บ๋ฉ€ ์ผ€์ด์Šค๋กœ ๋งŒ๋“ ๋‹ค. ์ด๋ฅผ ์Šค๋„ค์ดํฌ ์ผ€์ด์Šค๋กœ ๋งŒ๋“œ๋Š” ๊ฑฐ๋‹ค.
      modelName: 'User', // โœ… ๋ชจ๋ธ ์ด๋ฆ„
      tableName: 'users', // โœ… ๋ณดํ†ต ์†Œ๋ฌธ์ž ๋ฐ ๋ณต์ˆ˜ํ˜•
      paranoid: false, // โœ… true๋ฉด deletedAt์ด๋ผ๋Š” ์นผ๋Ÿผ ์ƒ๊น€ ๋กœ์šฐ๋ฅผ ์‚ญ์ œํ•  ๋–„ ์™„์ „ํžˆ ์ง€์›Œ์ง€์ง€ ์•Š๊ณ  ์ง€์šด ์‹œ๊ฐ ๊ธฐ๋ก. row ์กฐํšŒ์‹œ null์ธ ๊ฒƒ๋งŒ ์กฐํšŒ๋œ๋‹ค. ๋‚˜์ค‘์— row ๋ณต์›ํ•ด์•ผ ํ•  ๋•Œ.
      charset: 'utf8', // โœ… ํ•œ๊ธ€์ž…๋ ฅ ์œ„ํ•ด์„œ
      collate: 'utf8_general_ci', // โœ… ํ•œ๊ธ€์ž…๋ ฅ ์œ„ํ•ด์„œ
    });
  }

  static associate(db) {
    db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id' });
  }
};

 

User ๋ชจ๋ธ์„ ๋งŒ๋“ค๊ณ  ๋ชจ๋“ˆ๋กœ exportsํ–ˆ๋‹ค. User ๋ชจ๋ธ์€ Sequelize.Model์„ ํ™•์žฅํ•œ ํด๋ž˜์Šค๋กœ ์„ ์–ธํ•œ๋‹ค.

ํด๋ž˜์Šค ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ์ง€์‹์ด ์—†์–ด๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

 

static initate ๋ฉ”์„œ๋“œ์™€ static associate ๋ฉ”์„œ๋“œ๋กœ ๋‚˜๋‰˜์–ด ์ง‘๋‹ˆ๋‹ค.

 

๋ชจ๋ธ.init ๋ฉ”์„œ๋“œ์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ˆ˜๊ฐ€ ํ…Œ์ด๋ธ” ์นผ๋Ÿผ์— ๋Œ€ํ•œ ์„ค์ •์ด๊ณ , ๋‘ ๋ฒˆ์งธ ์ธ์ˆ˜๊ฐ€ ํ…Œ์ด๋ธ” ์ž์ฒด์— ๋Œ€ํ•œ ์„ค์ •

์‹œํ€„๋ผ์ด์ฆˆ๋Š” ์•Œ์•„์„œ id๋ฅผ ๊ธฐ๋ณธ ํ‚ค๋กœ ์—ฐ๊ฒฐํ•˜๋ฏ€๋กœ id ์ปฌ๋Ÿผ์€ ์ ์–ด์ค„ ํ•„์š”๊ฐ€ ์—†๋‹ค

๋‚˜๋จธ์ง€ ์ปฌ๋Ÿผ์˜ ์ŠคํŽ™์„ ์ž…๋ ฅํ•œ๋‹ค. MySQL์˜ ์ž๋ฃŒํ˜•๊ณผ๋Š” ๋‹ค๋ฅผ ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.

 

๋‹จ, ์‹œํ€„๋ผ์ด์ฆˆ์˜ ์ž๋ฃŒํ˜•์€ MySQL์˜ ์ž๋ฃŒํ˜•๊ณผ๋Š” ๋‹ค๋ฅด๋‹ค. ์‹œํ€„๋ผ์ด์ฆˆ๋Š” MySQL ์ด์™ธ์— ๋‹ค๋ฅธ ๋ฐ๋ฒ ๋„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ MySQL์˜ ์ž๋ฃŒํ˜•๊ณผ๋Š” ๋‹ค๋ฅผ ์ˆ˜ ๋ฐ–์— ์—†๋‹ค.

 

VARCHAR๋Š” STRING์œผ๋กœ

INT๋Š” INTEGER๋กœ

TINYINT๋Š” BOOLEAN์œผ๋กœ

DATETIME์€ DATE๋กœ ์ ๋Š”๋‹ค.

 

INTEGER.UNSIGNED๋Š” UNSIGNED ์˜ต์…˜์ด ์ ์šฉ๋œ INT๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ์—ฌ๊ธฐ์— ZEROFILL ์˜ต์…˜๋„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด INTEGER.UNSIGNED.ZEROFILL์„ ์ ๋Š”๋‹ค.

 

allowNull์€ NOT NULL ์˜ต์…˜๊ณผ ๋™์ผํ•˜๋‹ค.

unique๋Š” UNIQUE ์˜ต์…˜์ด๋‹ค.

defaultValue๋Š” ๊ธฐ๋ณธ๊ฐ’(default)๋ฅผ ์˜๋ฏธํ•œ๋‹ค. Squelize.NOW๋กœ ํ˜„์žฌ ์‹œ๊ฐ„์„ ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  SQL now()์™€ ๊ฐ™๋‹ค.

 

const Sequelize = require('sequelize');

module.exports = 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' });
  }
};

users ํ…Œ์ด๋ธ”๊ณผ ์—ฐ๊ฒฐ๋œ commenter ์นผ๋Ÿผ์ด ์—†๋‹ค. ์ด ๋ถ€๋ถ„์€ ๋ชจ๋ธ์„ ์ •์˜ํ•  ๋–„ ๋„ฃ์–ด๋„ ๋˜์ง€๋งŒ ์‹œํ€„๋ผ์ด์ฆˆ ์ž์ฒด์—์„œ ๊ด€๊ณ„๋ฅผ ๋”ฐ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด์— ๋Œ€ํ•ด์„œ๋Š” ๋’ค์—์„œ ์•Œ์•„๋ณด์ž.

 

const Sequelize = require('sequelize');
const User = require('./user'); // โœ… User์™€ Comment ๋ชจ๋ธ์„ ๋‹ด์•„๋‘์—ˆ๋‹ค.
const Comment = require('./comment'); // โœ… User์™€ Comment ๋ชจ๋ธ์„ ๋‹ด์•„๋‘์—ˆ๋‹ค. 

const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env];
const db = {};

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

db.sequelize = sequelize;
db.Sequelize = Sequelize;

db.User = User; 
db.Comment = Comment;

User.init(sequelize); 
Comment.init(sequelize);

User.associate(db);
Comment.associate(db);

module.exports = db;

 

initiate ๋ฉ”์„œ๋“œ๋Š” ๊ฐ๊ฐ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๋ชจ๋ธ.init์ด ์‹คํ–‰๋˜์–ด์•ผ ํ…Œ์ด๋ธ”์ด ๋ชจ๋ธ๋กœ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ํ…Œ์ด๋ธ”๊ณผ ๊ด€๊ณ„๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” static associate ๋ฉ”์„œ๋“œ๋„ ๋ฏธ๋ฆฌ ์‹คํ–‰ํ•ด๋‘”๋‹ค.

 

# ๊ด€๊ณ„ ์ •์˜ํ•˜๊ธฐ

 

users์™€ comments ํ…Œ์ด๋ธ” ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์ •์˜ํ•ด๋ณด์ž.

ํ•ด์‹œํƒœ๊ทธ ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” N: M ๊ด€๊ณ„

 

1:N ๊ด€๊ณ„

User -- hasMany --> Comment

User <-- belongsTo -- Comment

static associate(db) {
    db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id' });
}

static associate(db) {
    db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'id' });
}

์™œ db๋ผ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ƒ๋ฉด ์ตœ์ƒ๋‹จ์— const Comment = require('./comment')์‹์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๋ฉด ์•ˆ๋ ๊นŒ ์ƒ๊ฐํ•˜๋‚˜ ์ด ๊ฒฝ์šฐ์—๋Š” ์ˆœํ™˜์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•จ.

 

index.js์—์„œ ๊ฐ ๋ชจ๋ธ์„ ๋ถˆ๋Ÿฌ์™€ db๋กœ ๋ถˆ๋Ÿฌ์™€ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋„˜๊ธฐ๋Š” ๋ฐฉ์‹์„ ์ทจํ•จ.

foreignKey๋กœ '๋ชจ๋ธ๋ช…+๊ธฐ๋ณธ ํ‚ค'์˜ ์ปฌ๋Ÿผ์ด ๋ชจ๋ธ์— ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ๋“ค์–ด commenter๋ฅผ ์™ธ๋ž˜ ํ‚ค๋กœ ๋„ฃ์–ด์ฃผ์ง€ ์•Š์•˜๋‹ค๋ฉด user(๋ชจ๋ธ๋ช…) +๊ธฐ๋ณธ ํ‚ค(id)๊ฐ€ ํ•ฉ์ณ์ง„ userId๊ฐ€ ์™ธ๋ž˜ํ‚ค๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

 

1:1

hasOne ์‚ฌ์šฉ. 

User -- hasOne --> Info

User <-- belongsTo -- Info

 

static associate(db) {
    db.User.hasOne(db.Info, { foreignKey: 'UserId', sourceKey: 'id' });
}

static associate(db) {
    db.Info.belongsTo(db.User, { foreignKey: 'UserId', targetKey: 'id' });
}

 

N: M

N:M๊ด€๊ณ„

 

static associate(db) {
    db.Post.belongsToMany(db.HashTag, , { through: 'PostHashTag' });
}

static associate(db) {
    db.HashTag.belongsToMany(db.Post, { through: 'PostHashTag' });
}

 

์–‘์ชฝ ๋ชจ๋ธ ๋ชจ๋‘˜์— belongsToMany ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

PostHashTag ๋ชจ๋ธ์—๋Š” ๊ฒŒ์‹œ๊ธ€๊ณผ ํ•ด์‹œํƒœ๊ทธ์˜ ์•„์ด๋””๊ฐ€ ์ €์žฅ๋œ๋‹ค.

 

N: M ๊ด€๊ณ„ ํ…Œ์ด๋ธ”์—์„œ๋Š” ์กฐํšŒํ•  ๋•Œ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์ณ์•ผ ํ•จ.

#๋…ธ๋“œ ํ•ด์‹œํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒŒ์‹œ๋ฌผ์„ ์กฐํšŒํ•˜๋Š” ๊ฒฝ์šฐ ์ƒ๊ฐํ•ด๋ณด์ž.

๋จผ์ € #๋…ธ๋“œ ํ•ด์‹œํƒœ๊ทธ๋ฅผ Hashtag ๋ชจ๋ธ ์กฐํšŒํ•˜๊ณ  ๊ฐ€์ ธ์˜จ ํƒœ๊ทธ์˜ ์•„์ด๋””(1)์„ ๋ฐ”ํƒ•์œผ๋กœ PostHashtag ๋ชจ๋ธ์—์„œ hastagId๊ฐ€ 1์ธ postId๋“ค์„ ์ฐพ์•„ Post ๋ชจ๋ธ์—์„œ ๊ฐ€์ ธ์˜จ๋‹ค.

์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ๋ชจ๋ธ๋“ค๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

db.sequelize.models.PostHashtag

 

N: M

 

 

# ์ฟผ๋ฆฌ ์•Œ์•„๋ณด๊ธฐ

์‹œํ€„๋ผ์ด์ฆˆ CRUD ์ž‘์—…ํ•˜๋ ค๋ฉด ๋จผ์ € ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค. SQL๋ฌธ์„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ.

SQL๋ฌธ์— ์ƒ์‘ํ•˜๋Š” ์˜ต์…˜๋“ค์ด ์žˆ์Œ. ์ฟผ๋ฆฌ๋Š” ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ then์„ ๋ถ™์—ฌ ๊ฒฐ๊ด๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. async/await ๋ฌธ๋ฒ•๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋กœ์šฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฟผ๋ฆฌ๋ถ€ํ„ฐ ์•Œ์•„๋ณด์ž.

 

INSERT INTO nodejs.users (name, age, married, comment) VALUES ('zero', 24, 0, '์ž๊ฐœ์†Œ๊ฐœ1');
const { User } = require('../models');
User.create({
	name: 'zero',
    age: 24,
    married: false,
    comment: '์ž๊ธฐ์†Œ๊ฐœ1'
})

models ๋ชจ๋“ˆ์—์„œ User ๋ชจ๋ธ์„ ๋ถˆ๋Ÿฌ์™€ create ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์•ž์œผ๋กœ๋Š” User ๋ชจ๋ธ์„ ๋ถˆ๋Ÿฌ์™”๋‹ค๋Š” ์ „์ œํ•˜์— ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

 

์‹œํ€„๋ผ์ด์ฆˆ ๋ชจ๋ธ์— MySQL์ด ์•„๋‹ˆ๋ผ ์ •์˜ํ•œ ์ž๋ฃŒํ˜•๋Œ€๋กœ ๋„ฃ์–ด์•ผ ํ•œ๋‹ค.

์ด๊ฒƒ์ด married๊ฐ€ 0์ด ์•„๋‹ˆ๋ผ false์ธ ์ด์œ .

 

์ž๋ฃŒํ˜•์ด๋‚˜ ์˜ต์…˜์— ๋ถ€ํ•ฉํ•˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์—ˆ์„ ๋•Œ๋Š” ์‹œํ€„๋ผ์ด์ฆˆ๊ฐ€ ์—๋Ÿฌ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

 

 

๋‹ค์Œ์€ users ํ…Œ์ด๋ธ”์˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” SQL๋ฌธ์ด๋‹ค. 

SELECT * FROM nodejs.users;
User.findAll({});

 

 

๋‹ค์Œ์€ ํ•˜๋‚˜๋งŒ ๊ฐ€์ ธ์˜ค๋Š”๊ฑฐ๋‹ค.

 

 

 

SELECT * FROM nodejs.users LIMIT 1;
User.findOne({});โ€‹

 

attributes ์˜ต์…˜ ์‚ฌ์šฉํ•ด์„œ ์›ํ•˜๋Š” ์ปฌ๋Ÿผ๋งŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

SELECT name, married FROM nodejs.users;
User.findAll({
	attributes: ['name', 'married'],
});

 

where ์˜ต์…˜์ด ์กฐ๊ฑด๋“ค์„ ๋‚˜์—ดํ•˜๋Š” ์˜ต์…˜

SELECT name, age FROM nodejs.users WHERE marreid = 1 AND age > 30;
const { Op } = require('sequelize');
const { User } = require('../models');

User.findAll({
	attributes: ['name', 'age'],
    where: { 
    	married: true,
        age: { [Op.gt]: 30 },  // ES2015 ๋ฌธ๋ฒ•์ด๋‹ค. ํŠน์ˆ˜ ์—ฐ์‚ฐ์ž์ธ๋ฐ ์‹œํ€„๋ผ์ด์ฆˆ ๋‚ด๋ถ€์˜ Op๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
    },
});

Op.gt(์ดˆ๊ณผ), Op.gte(์ด์ƒ), Op.1t(๋ฏธ๋งŒ), Op.1te(์ดํ•˜), Op.ne(๊ฐ™์ง€ ์•Š์Œ), Op.or(๋˜๋Š”), Op.in(๋ฐฐ์—ด ์š”์†Œ ์ค‘ ํ•˜๋‚˜), Op.notIn(๋ฐฐ์—ด ์š”์†Œ์™€ ๋ชจ๋‘ ๋‹ค๋ฆ„) ๋“ฑ

 

SELECT id, name FROM users ORDER BY age DESC;
User.findAll({
	attrigutes: ['id', 'name'],
    order: [['age', 'DESC']],
    limit: 1,
});

์‹œํ€„๋ผ์ด์ฆˆ์˜ ์ •๋ ฌ ๋ฐฉ์‹์ž„. ์ •๋ ฌ์€ ๊ผญ ์นผ๋Ÿผ ํ•˜๋‚˜๋กœ๋งŒ ํ•˜๋Š”๊ฒŒ ์•„๋‹˜. ์ œํ•œ๋„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

SELECT id, name FROM users ORDER BY age DESC LIMIT 1 OFFSET 1;
User.findAll({
	attributes: ['id', 'name'],
    order: ['age, 'DESC'],
    limit: 1,
    offset: 1,
});

๋ฆฌ๋ฐ‹๋ง๊ณ  ์˜คํ”„์…‹์œผ๋กœ๋„ ๊ตฌํ˜„ ๊ฐ€๋Šฅ

 

 

์ด๋ฒˆ์—๋Š” ๋กœ์šฐ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ์ฟผ๋ฆฌ.

SELECT nodejs.users SET comment = '๋ฐ”๊ฟ€ ๋‚ด์šฉ' WHERE id = 2;
User.update({
	comment: '๋ฐ”๊ฟ€ ๋‚ด์šฉ'
}, { 
	where: { id: 2 },
});

๋ฉ”์„œ๋“œ๋ฅผ ์ด๋ ‡๊ฒŒ ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

 

์ด๋ฒˆ์—๋Š” ์‚ญ์ œํ•ด๋ณด์ž. ์กฐ๊ฑด where์— ๊ฑธ์–ด์„œ ์‚ญ์ œ ๊ฐ€๋Šฅ

DELETE FROM nodejs.users WHERE id = 2;
User.detroy({
	where: { id: 2 },
});

 

 

findOne์ด๋‚˜ findAll ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ํ”„๋กœ๋ฏธ์Šค์˜ ๊ฒฐ๊ณผ๋กœ ๋ชจ๋ธ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

const user = await User.findOne({});

console.log(user.nick); // ์‚ฌ์šฉ์ž ๋‹‰๋„ค์ž„.

 

User ๋ชจ๋ธ์˜ ์ •๋ณด์—๋„ ๋ฐ”๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋” ํŽธ๋ฆฌํ•œ ์ ์€ ๊ด€๊ณ„ ์ฟผ๋ฆฌ๋ฅผ ์ง€์›ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

MySQL๋กœ ๋”ฐ์ง€๋ฉด JOIN ๊ธฐ๋Šฅ์ด๋‹ค. ๋งŒ์•ฝ ํŠน์ • ์‚ฌ์šฉ์ž๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด์„œ ๊ทธ ์‚ฌ๋žŒ์˜ ๋Œ“๊ธ€๊นŒ์ง€ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ๋‹ค๋ฉด?

include์‚ฌ์šฉํ•ด์„œ ๊ฐ€์ ธ์˜จ๋‹ค.

const user = await User.findOne({
	include: [{
    	model: Comment,
    }]
});
console.log(user.Comments); // ์‚ฌ์šฉ์ž ๋Œ“๊ธ€

 

์–ด๋–ค ๋ชจ๋ธ๊ณผ ๊ด€๊ณ„๊ฐ€ ์žˆ๋Š”์ง€๋ฅผ include ๋ฐฐ์—ด์— ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

๋ฐฐ์—ด์ธ ์ด์œ ๋Š” ๋‹ค์–‘ํ•œ ๋ชจ๋ธ๊ณผ ๊ด€๊ณ„๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋Œ“๊ธ€์€ ์—ฌ๋Ÿฌ ๊ฐœ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ

const user = await User.findOne{()};
const comments = await user.getComments();
console.log(comments); // ์‚ฌ์šฉ์ž ๋Œ“๊ธ€

 

๊ด€๊ณ„๋ฅผ ์„ค์ •ํ–ˆ๋‹ค๋ฉด getComments(์กฐํšŒ) ์™ธ์—๋„ setComments(์ˆ˜์ •) addComment(ํ•˜๋‚˜ ์ƒ์„ฑ), addComments(์—ฌ๋Ÿฌ ๊ฐœ ์ƒ์„ฑ), removeComments(์‚ญ์ œ) ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›

๋™์‚ฌ ๋’ค์— ๋ชจ๋ธ์ด ๋ถ™๋Š” ํ˜•์‹

 

// ๊ด€๊ณ„ ์„ค์ •ํ•  ๋•Œ as๋กœ ๋“ฑ๋ก
db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id', as: 'Answer'});


// ์ฟผ๋ฆฌํ•  ๋•Œ๋Š”
const user = await User.findOne{()};
const comments = await user.getAnswers();
console.log(comments); // ์‚ฌ์šฉ์ž ๋Œ“๊ธ€

 

as๋ฅผ ์„ค์ •ํ•˜๋ฉด includeํ•  ๋•Œ ์ถ”๊ฐ€๋˜๋Š” ๋Œ“๊ธ€ ๊ฐ์ฒด๋„ user.Answers๋กœ ๋ฐ”๋€๋‹ˆ๋‹ค.

include๋‚˜ ๊ด€๊ณ„ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ์—๋„ where์ด๋‚˜ attributes ๊ฐ™์€ ์˜ต์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

const user = await User.findOne({
   include: [{
   model: Comment,
   where: {
   id: 1,
   attributes: ['id'],
   }]
});

// or
const comments = await user.getComments({
   where: {
      id: 1,
   },
   attributes: ['id'],
});

๋Œ“๊ธ€์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ๋Š” id๊ฐ€ 1์ธ ๋Œ“๊ธ€๋งŒ ๊ฐ€์ ธ์˜ค๊ณ , ์ปฌ๋Ÿผ๋„ id ์ปฌ๋Ÿผ๋งŒ ๊ฐ€์ ธ์˜ค๋„๋ก ํ•˜๊ณ  ์žˆ๋‹ค.

๊ด€๊ณ„ ์ฟผ๋ฆฌ ์‹œ ์กฐํšŒ๋Š” ์œ„์™€ ๊ฐ™์ด ํ•˜์ง€๋งŒ ์ˆ˜์ •, ์ƒ์„ฑ, ์‚ญ์ œ ๋•Œ๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅธ ์ ์ด ์žˆ๋‹ค.

 

const user = await User.findOne({});
const comment = await Comment.create();
await user.addComment(comment);
// ๋˜๋Š”
await user.addComment(comment.id);

// โœ… ์—ฌ๋Ÿฌ๊ฐœ ์ถ”๊ฐ€์‹œ ๋ฐฐ์—ด๋กœ ๊ฐ€๋Šฅ

const user = await User.findOne({});
const comment1 = await Comment.create();
const comment2 = await Comment.create();
await user.addComment([comment1, comment2]);

 

๊ด€๊ณ„ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ์ธ์ˆ˜๋กœ ์ถ”๊ฐ€ํ•  ๋Œ“๊ธ€ ๋ชจ๋ธ์„ ๋„ฃ๊ฑฐ๋‚˜ ๋Œ“๊ธ€์˜ ์•„์ด๋””๋ฅผ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค. ์ˆ˜์ • ์‚ญ์ œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€

 

SQL ์ฟผ๋ฆฌํ•˜๊ธฐ

 

๋งŒ์•ฝ ์‹œํ€„๋ผ์ด์ฆˆ ์‚ฌ์šฉํ•˜๊ธฐ ์‹ซ๊ฑฐ๋‚˜ ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ์ง€ ๋ชจ๋ฅด๋‹ค๋ฉด ์ง์ ‘ SQL ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.

const [result, metadata] = await sequelize.query('SELECT * from commnets');
console.log(result);

์›ฌ๋งŒํ•˜๋ฉด ์‹œํ€„๋ผ์ด์ฆˆ์˜ ์ฟผ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฑฐ ์ถ”์ฒœํ•˜์ง€๋งŒ ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ๋กœ ํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ์—๋Š” ์œ„์™€ ๊ฐ™์ด ํ•˜๋ฉด ๋œ๋‹ค.

 

# ์ฟผ๋ฆฌ ์ˆ˜ํ–‰ํ•˜๊ธฐ

๋ชจ๋ธ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ ํŽ˜์ด์ง€๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ JOSN ํ˜•์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ•๊ณผ JSON ํ˜•์‹์œผ๋กœ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž.

 

 

๊ฐ„๋‹จํ•˜๊ฒŒ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋“ฑ๋กํ•˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ๋“ฑ๋กํ•œ ๋Œ“๊ธ€์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•

// ์‚ฌ์šฉ์ž ์ด๋ฆ„ ๋ˆŒ๋ €์„ ๋•Œ ๋Œ“๊ธ€ ๋กœ๋”ฉ
document.querySelectorAll('#user-list tr').forEach((el) => {
  el.addEventListener('click', function () {
    const id = el.querySelector('td').textContent;
    getComment(id);
  });
});
// ์‚ฌ์šฉ์ž ๋กœ๋”ฉ
async function getUser() {
  try {
    const res = await axios.get('/users');
    const users = res.data;
    console.log(users);
    const tbody = document.querySelector('#user-list tbody');
    tbody.innerHTML = '';
    users.map(function (user) {
      const row = document.createElement('tr');
      row.addEventListener('click', () => {
        getComment(user.id);
      });
      // ๋กœ์šฐ ์…€ ์ถ”๊ฐ€
      let td = document.createElement('td');
      td.textContent = user.id;
      row.appendChild(td);
      td = document.createElement('td');
      td.textContent = user.name;
      row.appendChild(td);
      td = document.createElement('td');
      td.textContent = user.age;
      row.appendChild(td);
      td = document.createElement('td');
      td.textContent = user.married ? '๊ธฐํ˜ผ' : '๋ฏธํ˜ผ';
      row.appendChild(td);
      tbody.appendChild(row);
    });
  } catch (err) {
    console.error(err);
  }
}
// ๋Œ“๊ธ€ ๋กœ๋”ฉ
async function getComment(id) {
  try {
    const res = await axios.get(`/users/${id}/comments`);
    const comments = res.data;
    const tbody = document.querySelector('#comment-list tbody');
    tbody.innerHTML = '';
    comments.map(function (comment) {
      // ๋กœ์šฐ ์…€ ์ถ”๊ฐ€
      const row = document.createElement('tr');
      let td = document.createElement('td');
      td.textContent = comment.id;
      row.appendChild(td);
      td = document.createElement('td');
      td.textContent = comment.User.name;
      row.appendChild(td);
      td = document.createElement('td');
      td.textContent = comment.comment;
      row.appendChild(td);
      const edit = document.createElement('button');
      edit.textContent = '์ˆ˜์ •';
      edit.addEventListener('click', async () => { // ์ˆ˜์ • ํด๋ฆญ ์‹œ
        const newComment = prompt('๋ฐ”๊ฟ€ ๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”');
        if (!newComment) {
          return alert('๋‚ด์šฉ์„ ๋ฐ˜๋“œ์‹œ ์ž…๋ ฅํ•˜์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค');
        }
        try {
          await axios.patch(`/comments/${comment.id}`, { comment: newComment });
          getComment(id);
        } catch (err) {
          console.error(err);
        }
      });
      const remove = document.createElement('button');
      remove.textContent = '์‚ญ์ œ';
      remove.addEventListener('click', async () => { // ์‚ญ์ œ ํด๋ฆญ ์‹œ
        try {
          await axios.delete(`/comments/${comment.id}`);
          getComment(id);
        } catch (err) {
          console.error(err);
        }
      });
      // ๋ฒ„ํŠผ ์ถ”๊ฐ€
      td = document.createElement('td');
      td.appendChild(edit);
      row.appendChild(td);
      td = document.createElement('td');
      td.appendChild(remove);
      row.appendChild(td);
      tbody.appendChild(row);
    });
  } catch (err) {
    console.error(err);
  }
}
// ์‚ฌ์šฉ์ž ๋“ฑ๋ก ์‹œ
document.getElementById('user-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  const name = e.target.username.value;
  const age = e.target.age.value;
  const married = e.target.married.checked;
  if (!name) {
    return alert('์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”');
  }
  if (!age) {
    return alert('๋‚˜์ด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”');
  }
  try {
    await axios.post('/users', { name, age, married });
    getUser();
  } catch (err) {
    console.error(err);
  }
  e.target.username.value = '';
  e.target.age.value = '';
  e.target.married.checked = false;
});
// ๋Œ“๊ธ€ ๋“ฑ๋ก ์‹œ
document.getElementById('comment-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  const id = e.target.userid.value;
  const comment = e.target.comment.value;
  if (!id) {
    return alert('์•„์ด๋””๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”');
  }
  if (!comment) {
    return alert('๋Œ“๊ธ€์„ ์ž…๋ ฅํ•˜์„ธ์š”');
  }
  try {
    await axios.post('/comments', { id, comment });
    getComment(id);
  } catch (err) {
    console.error(err);
  }
  e.target.userid.value = '';
  e.target.comment.value = '';
});

 

์™€ ์–ด๋ ต๋‹ค! ์ผ๋‹จ ํ›… ์ฝ๊ณ  ๋„˜์–ด๊ฐ€๋ณด์ž

 

 

๋ผ์šฐํ„ฐ๋“ค์„ ๋ฏธ๋ฆฌ app.js์— ์—ฐ๊ฒฐํ•ด๋ณด์ž.

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

const { sequelize } = require('./models');
const indexRouter = require('./routes');
const usersRouter = require('./routes/users');
const commentsRouter = require('./routes/comments');

const app = express();
app.set('port', process.env.PORT || 3001);
app.set('view engine', 'html');
nunjucks.configure('views', {
  express: app,
  watch: true,
});
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());
app.use(express.urlencoded({ extended: false }));

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/comments', commentsRouter);

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 : {};
  res.status(err.status || 500);
  res.render('error');
});

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

 

 

๋ผ์šฐํ„ฐ์˜ ๋‚ด์šฉ์€ sequlize.js์— ๋‚˜์˜ค๋Š” GET, POST, PUT, DELETE ์š”์ฒญ์— ํ•ด๋‹นํ•˜๋Š” ๋ผ์šฐํ„ฐ๋ฅผ ๋งŒ๋“ ๋‹ค. routes ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ ์•ˆ์— index.js๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ๋œ๋‹ค.

 

// routes/index.js
const express = require('express');
const User = require('../models/user');

const router = express.Router();

router.get('/', async (req, res, next) => {
  try {
    const users = await User.findAll();
    res.render('sequelize', { users });
  } catch (err) {
    console.error(err);
    next(err);
  }
});

module.exports = router;

๋จผ์ € GET /๋กœ ์ ‘์†ํ–ˆ์„ ๋•Œ์˜ ๋ผ์šฐํ„ฐ์ž…๋‹ˆ๋‹ค. User.findAll ๋ฉ”์„œ๋“œ๋กœ ๋ชจ๋“  ์‚ฌ์šฉ์ž๋ฅผ ์ฐพ์€ ํ›„ sequelize.html์„ ๋ Œ๋”๋งํ•  ๋•Œ ๊ฒฐ๊ณผ๊ฐ’์ธ uses๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค. ์‹œํ€„๋ผ์ด์ฆˆ๋Š” ํ”„๋กœ๋ฏธ์Šค๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›ํ•˜๋ฏ€๋กœ async/await๊ณผ try/catch๋ฌธ์„ ์‚ฌ์šฉํ•ด์„œ ๊ฐ๊ฐ ์กฐํšŒ ์„ฑ๊ณต์‹œ์™€ ์‹คํŒจ์‹œ์˜ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค

 

๋‹ค์Œ์€ user.js๋‹ค. router.route('/')๋กœ ๊ฐ™์€ ๊ฒฝ๋กœ๋Š” ํ•˜๋‚˜๋กœ ๋ฌถ์—ˆ๋‹ค.

const express = require('express');
const User = require('../models/user');
const Comment = require('../models/comment');

const router = express.Router();

router.route('/')
  .get(async (req, res, next) => {
    try {
      const users = await User.findAll();
      res.json(users);
    } catch (err) {
      console.error(err);
      next(err);
    }
  })
  .post(async (req, res, next) => {
    try {
      const user = await User.create({
        name: req.body.name,
        age: req.body.age,
        married: req.body.married,
      });
      console.log(user);
      res.status(201).json(user);
    } catch (err) {
      console.error(err);
      next(err);
    }
  });

router.get('/:id/comments', async (req, res, next) => {
  try {
    const comments = await Comment.findAll({
      include: {
        model: User,
        where: { id: req.params.id },
      },
    });
    console.log(comments);
    res.json(comments);
  } catch (err) {
    console.error(err);
    next(err);
  }
});

module.exports = router;

GET /users์™€ POST /users ์ฃผ์†Œ๋กœ ์š”์ฒญ์ด ๋“ค์–ด์˜ฌ ๋•Œ์˜ ๋ผ์šฐํ„ฐ์ด๋‹ค.

๊ฐ๊ฐ ์‚ฌ์šฉ์ž๋ฅผ ์กฐํšŒํ•˜๋Š” ์š”์ฒญ๊ณผ ๋“ฑ๋กํ•˜๋Š” ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. GET /์—์„œ๋„ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ–ˆ์ง€๋งŒ, GET /users์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ JSON ํ˜•์‹์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋Š” ๊ฒƒ์— ์ฐจ์ด๊ฐ€ ์žˆ๋‹ค.

 

GET /users/id:/comments ๋ผ์šฐํ„ฐ์—๋Š” findAll ๋ฉ”์„œ๋“œ์— ์ถ”๊ฐ€๋˜์–ด ์žˆ๋‹ค.

include ์˜ต์…˜์—์„œ๋Š” model ์†์„ฑ์—๋Š” User ๋ชจ๋ธ์„, where ์†์„ฑ์—๋Š” :id๋กœ ๋ฐ›์€ ์•„์ด๋”” ๊ฐ’์„ ๋„ฃ๋Š”๋‹ค.

:id๋Š” ๋ผ์šฐํŠธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” req.params.id๋กœ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

GET /users/1/comments๋ผ๋ฉด ์‚ฌ์šฉ์ž id๊ฐ€ 1์ธ ๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค์ž. ์กฐํšŒ๋œ ๋Œ“๊ธ€ ๊ฐ์ฒด์—๋Š” include๋กœ ๋„ฃ์–ด์ค€ ์‚ฌ์šฉ์ž ์ •๋ณด๋„ ๋“ค์–ด์žˆ์–ด์„œ ์ž‘์„ฑ์ž์˜ ์ด๋ฆ„์ด๋‚˜ ๋‚˜์ด ๋“ฑ๋„ ์กฐํšŒ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

๋‹ค์Œ์€ comment.js

const express = require('express');
const { Comment } = require('../models');

const router = express.Router();

router.post('/', async (req, res, next) => {
  try {
    const comment = await Comment.create({
      commenter: req.body.id,
      comment: req.body.comment,
    });
    console.log(comment);
    res.status(201).json(comment);
  } catch (err) {
    console.error(err);
    next(err);
  }
});

router.route('/:id')
  .patch(async (req, res, next) => {
    try {
      const result = await Comment.update({
        comment: req.body.comment,
      }, {
        where: { id: req.params.id },
      });
      res.json(result);
    } catch (err) {
      console.error(err);
      next(err);
    }
  })
  .delete(async (req, res, next) => {
    try {
      const result = await Comment.destroy({ where: { id: req.params.id } });
      res.json(result);
    } catch (err) {
      console.error(err);
      next(err);
    }
  });

module.exports = router;

 

๋Œ“๊ธ€์— CRUD ์ž‘์—…์„ ํ•˜๋Š” ๋ผ์šฐํ„ฐ๋‹ค. POST /comments, PATCH /comments/:id, DELETE/comments/:id๋ฅผ ๋“ฑ๋ก

 

์ด์ œ npm start๋กœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์‹œํ€„๋ผ์ด์ฆˆ๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” SQL๋ฌธ์ด ๋‚˜์˜ค๋ฏ€๋กœ ์–ด๋–ค ๋™์ž‘์„ ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

SQL ๊ตฌ๋ฌธ์ด ๋ณด๊ณ ์‹ถ์ง€ ์•Š๋‹ค๋ฉด config/config.json์˜ dialect ์†์„ฑ ใ…ฃใ…Œ์— "logging": false๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

 

์ ‘์† ์‹œ GET / ๋ผ์šฐํ„ฐ์—์„œ User.findAll ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฏ€๋กœ ๊ทธ์— ๋”ฐ๋ฅธ SQL๋ฌธ์ด ์‹คํ–‰๋˜๋Š” ๋ชจ์Šต์ด๋‹ค.

 

 

 

 

 

 

Comments