๐Ÿ”ŽMySQL

์„œ๊ฐ€ํฌยท2021๋…„ 11์›” 6์ผ
0

Node.js

๋ชฉ๋ก ๋ณด๊ธฐ
6/15
post-thumbnail

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ž€

์ง€๊ธˆ๊นŒ์ง€๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ–ˆ์Œ

  • ์„œ๋ฒ„๋ฅผ ์žฌ์‹œ์ž‘ํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋„ ์‚ฌ๋ผ์ ธ๋ฒ„๋ฆผ -> ์˜๊ตฌ์ ์œผ๋กœ ์ €์žฅํ•  ๊ณต๊ฐ„ ํ•„์š”
    MySQL ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์šฉ
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค: ๊ด€๋ จ์„ฑ์„ ๊ฐ€์ง€๋ฉฐ ์ค‘๋ณต์ด ์—†๋Š” ๋ฐ์ดํ„ฐ๋“ค์˜ ์ง‘ํ•ฉ
  • DBMS: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์‹œ์Šคํ…œ
  • RDBMS: ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์‹œ์Šคํ…œ
  • ์„œ๋ฒ„์˜ ํ•˜๋“œ ๋””์Šคํฌ๋‚˜ SSD ๋“ฑ์˜ ์ €์žฅ ๋งค์ฒด์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ
  • ์„œ๋ฒ„ ์ข…๋ฃŒ ์—ฌ๋ถ€์™€ ์ƒ๊ด€ ์—†์ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
  • ์—ฌ๋Ÿฌ ์‚ฌ๋žŒ์ด ๋™์‹œ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ณ , ๊ถŒํ•œ์„ ๋”ฐ๋กœ ์ค„ ์ˆ˜ ์žˆ์Œ

7. MySQL ์ ‘์†ํ•ด๋ณด๊ธฐ

์ฝ˜์†”(CMD)์—์„œ MySQL์ด ์„ค์น˜๋œ ๊ฒฝ๋กœ๋กœ ์ด๋™

  • ๊ธฐ๋ณธ ๊ฒฝ๋กœ๋Š” C:\Program Files\MySQL\MySQL Server 8.0\bin
  • -h๋Š” ํ˜ธ์ŠคํŠธ, -u๋Š” ์‚ฌ์šฉ์ž โ€“p๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ ์˜๋ฏธ
  • ํ”„๋กฌํ”„ํŠธ๊ฐ€ mysql>๋กœ ๋ฐ”๋€๋‹ค๋ฉด ์„ฑ๊ณต
  • ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ข…๋ฃŒํ•˜๋ ค๋ฉด exit ์ž…๋ ฅ

12. MySQL ์„œ๋ฒ„ ์ฃผ์†Œ ์ž…๋ ฅ

Connection Name์— localhost ์ ๊ณ  ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ

13. localhost ์„œ๋ฒ„ ์ ‘์†ํ•˜๊ธฐ

์•„๊นŒ ์„ค์ •ํ–ˆ๋˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์ ‘์†

์ ‘์†์ด ๋˜์—ˆ๋‹ค๋ฉด ์„ฑ๊ณต

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ํ…Œ์ด๋ธ” ์ƒ์„ฑํ•˜๊ธฐ

1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ์„ฑํ•˜๊ธฐ

์ฝ˜์†”์—์„œ MySQL ํ”„๋กฌํ”„ํŠธ์— ์ ‘์†

  • CREATE SCHEMA nodejs;๋กœ nodejs ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ƒ์„ฑ
  • use nodejs;๋กœ ์ƒ์„ฑํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ ํƒ

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

MySQL ํ”„๋กฌํ”„ํŠธ์—์„œ ํ…Œ์ด๋ธ” ์ƒ์„ฑ

  • CREATE TABLE [๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ช….ํ…Œ์ด๋ธ”๋ช…]์œผ๋กœ ํ…Œ์ด๋ธ” ์ƒ์„ฑ
  • ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ํ…Œ์ด๋ธ”

3. ์ปฌ๋Ÿผ๊ณผ ๋กœ์šฐ

๋‚˜์ด, ๊ฒฐํ˜ผ ์—ฌ๋ถ€, ์„ฑ๋ณ„๊ฐ™์€ ์ •๋ณด๊ฐ€ ์ปฌ๋Ÿผ
์‹ค์ œ๋กœ ๋“ค์–ด๊ฐ€๋Š” ๋ฐ์ดํ„ฐ๋Š” ๋กœ์šฐ

4. ์ปฌ๋Ÿผ ์˜ต์…˜๋“ค

id INT NOT NULL AUTO_INCREMENT

  • ์ปฌ๋Ÿผ๋ช… ์˜†์˜ ๊ฒƒ๋“ค์€ ์ปฌ๋Ÿผ์— ๋Œ€ํ•œ ์˜ต์…˜๋“ค
  • INT: ์ •์ˆ˜ ์ž๋ฃŒํ˜•(FLOAT, DOUBLE์€ ์‹ค์ˆ˜)
  • VARCHAR: ๋ฌธ์ž์—ด ์ž๋ฃŒํ˜•, ๊ฐ€๋ณ€ ๊ธธ์ด(CHAR์€ ๊ณ ์ • ๊ธธ์ด)
  • TEXT: ๊ธด ๋ฌธ์ž์—ด์€ TEXT๋กœ ๋ณ„๋„ ์ €์žฅ
  • DATETIME: ๋‚ ์งœ ์ž๋ฃŒํ˜• ์ €์žฅ
  • TINYINT: -128์—์„œ 127๊นŒ์ง€ ์ €์žฅํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ๋Š” 1 ๋˜๋Š” 0๋งŒ ์ €์žฅํ•ด ๋ถˆ ๊ฐ’ ํ‘œํ˜„
  • NOT NULL: ๋นˆ ๊ฐ’์€ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค๋Š” ๋œป(NULL์€ ๋นˆ ๊ฐ’ ํ—ˆ์šฉ)
  • AUTO_INCREMENT: ์ˆซ์ž ์ž๋ฃŒํ˜•์ธ ๊ฒฝ์šฐ ๋‹ค์Œ ๋กœ์šฐ๊ฐ€ ์ €์žฅ๋  ๋•Œ ์ž๋™์œผ๋กœ 1 ์ฆ๊ฐ€
  • UNSIGNED: 0๊ณผ ์–‘์ˆ˜๋งŒ ํ—ˆ์šฉ
  • ZEROFILL: ์ˆซ์ž์˜ ์ž๋ฆฌ ์ˆ˜๊ฐ€ ๊ณ ์ •๋œ ๊ฒฝ์šฐ ๋นˆ ์ž๋ฆฌ์— 0์„ ๋„ฃ์Œ
  • DEFAULT now(): ๋‚ ์งœ ์ปฌ๋Ÿผ์˜ ๊ธฐ๋ณธ๊ฐ’์„ ํ˜„์žฌ ์‹œ๊ฐ„์œผ๋กœ

5. Primary Key, Unique Index

PRIMARY KEY(id)

  • id๊ฐ€ ํ…Œ์ด๋ธ”์—์„œ ๋กœ์šฐ๋ฅผ ํŠน์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ณ ์œ ํ•œ ๊ฐ’์ž„์„ ์˜๋ฏธ
  • ํ•™๋ฒˆ, ์ฃผ๋ฏผ๋“ฑ๋ก๋ฒˆํ˜ธ๊ฐ™์€ ๊ฐœ๋…
    UNIQUE INDEX name_UNIQUE (name ASC)
  • ํ•ด๋‹น ์ปฌ๋Ÿผ(name)์ด ๊ณ ์œ ํ•ด์•ผ ํ•จ์„ ๋‚˜ํƒ€๋‚ด๋Š” ์˜ต์…˜
  • name_UNIQUE๋Š” ์ด ์˜ต์…˜์˜ ์ด๋ฆ„(์•„๋ฌด๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ๊ฑธ๋กœ ์ง€์–ด๋„ ๋จ)
  • ASC๋Š” ์ธ๋ฑ์Šค๋ฅผ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์ €์žฅํ•จ์˜ ์˜๋ฏธ(๋‚ด๋ฆผ์ฐจ์ˆœ์€ DESC)![]

6. ํ…Œ์ด๋ธ” ์˜ต์…˜

COMMENT: ํ…Œ์ด๋ธ”์— ๋Œ€ํ•œ ๋ณด์ถฉ ์„ค๋ช…(ํ•„์ˆ˜ ์•„๋‹˜)
DEFAULT CHARSET: utf8๋กœ ์„ค์ •ํ•ด์•ผ ํ•œ๊ธ€์ด ์ž…๋ ฅ๋จ(utf8mb4 ํ•˜๋ฉด ์ด๋ชจํ‹ฐ์ฝ˜ ๊ฐ€๋Šฅ)
ENGINE: InnoDB ์‚ฌ์šฉ(์ด์™ธ์— MyISAM์ด ์žˆ์Œ, ์—”์ง„๋ณ„๋กœ ๊ธฐ๋Šฅ ์ฐจ์ด ์กด์žฌ)

7. ํ…Œ์ด๋ธ” ์ƒ์„ฑ๋˜์—ˆ๋‚˜ ํ™•์ธํ•˜๊ธฐ

DESC ํ…Œ์ด๋ธ”๋ช…

ํ…Œ์ด๋ธ” ์‚ญ์ œํ•˜๊ธฐ: DROP TABLE ํ…Œ์ด๋ธ”๋ช…

8. ๋Œ“๊ธ€ ํ…Œ์ด๋ธ” ์ €์žฅํ•˜๊ธฐ

comments ํ…Œ์ด๋ธ” ์ƒ์„ฑ

9. ์™ธ๋ž˜ํ‚ค(foreign key)

๋Œ“๊ธ€ ํ…Œ์ด๋ธ”์€ ์‚ฌ์šฉ์ž ํ…Œ์ด๋ธ”๊ณผ ๊ด€๊ณ„๊ฐ€ ์žˆ์Œ(์‚ฌ์šฉ์ž๊ฐ€ ๋Œ“๊ธ€์„ ๋‹ฌ๊ธฐ ๋•Œ๋ฌธ)

  • ์™ธ๋ž˜ํ‚ค๋ฅผ ๋‘์–ด ๋‘ ํ…Œ์ด๋ธ”์ด ๊ด€๊ณ„๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ํ‘œ์‹œ

  • FOREIGN KEY (์ปฌ๋Ÿผ๋ช…) REFERENCES ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค.ํ…Œ์ด๋ธ”๋ช… (์ปฌ๋Ÿผ)

  • FOREIGN KEY (commenter) REFERENCES nodejs.users (id)

  • ๋Œ“๊ธ€ ํ…Œ์ด๋ธ”์—๋Š” commenter ์ปฌ๋Ÿผ์ด ์ƒ๊ธฐ๊ณ  ์‚ฌ์šฉ์ž ํ…Œ์ด๋ธ”์˜ id๊ฐ’์ด ์ €์žฅ๋จ

  • ON DELETE CASCADE, ON UPDATE CASCADE

  • ์‚ฌ์šฉ์ž ํ…Œ์ด๋ธ”์˜ ๋กœ์šฐ๊ฐ€ ์ง€์›Œ์ง€๊ณ  ์ˆ˜์ •๋  ๋•Œ ๋Œ“๊ธ€ ํ…Œ์ด๋ธ”์˜ ์—ฐ๊ด€๋œ ๋กœ์šฐ๋“ค๋„ ๊ฐ™์ด ์ง€์›Œ์ง€๊ณ  ์ˆ˜์ •๋จ

  • ๋ฐ์ดํ„ฐ๋ฅผ ์ผ์น˜์‹œํ‚ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ์˜ต์…˜(CASCADE ๋Œ€์‹  SET NULL๊ณผ NO ACTION๋„ ์žˆ์Œ)

10. ํ…Œ์ด๋ธ” ๋ชฉ๋ก ๋ณด๊ธฐ

SHOW TABLES

CRUD ์ž‘์—…ํ•˜๊ธฐ

1. CRUD

Create, Read, Update, Delete์˜ ๋‘๋ฌธ์ž์–ด

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋งŽ์ด ํ•˜๋Š” ์ž‘์—… 4๊ฐ€์ง€

2. Create

INSERT INTO ํ…Œ์ด๋ธ” (์ปฌ๋Ÿผ๋ช…๋“ค) VALUES (๊ฐ’๋“ค)

3. Read

SELECT ์ปฌ๋Ÿผ FROM ํ…Œ์ด๋ธ”๋ช…
SELECT * ์€ ๋ชจ๋“  ์ปฌ๋Ÿผ์„ ์„ ํƒํ•œ๋‹ค๋Š” ์˜๋ฏธ

  • ์ปฌ๋Ÿผ๋งŒ ๋”ฐ๋กœ ์ถ”๋ฆฌ๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅ

4. Read ์˜ต์…˜๋“ค

WHERE๋กœ ์กฐ๊ฑด์„ ์ฃผ์–ด ์„ ํƒ ๊ฐ€๋Šฅ

  • AND๋กœ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์กฐ๊ฑด์„ ๋™์‹œ์— ๋งŒ์กฑํ•˜๋Š” ๊ฒƒ์„ ์ฐพ์Œ

  • OR๋กœ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์กฐ๊ฑด ์ค‘ ํ•˜๋‚˜ ์ด์ƒ์„ ๋งŒ์กฑํ•˜๋Š” ๊ฒƒ์„ ์ฐพ์Œ

5. ์ •๋ ฌํ•ด์„œ ์ฐพ๊ธฐ

ORDER BY๋กœ ํŠน์ • ์ปฌ๋Ÿผ ๊ฐ’ ์ˆœ์„œ๋Œ€๋กœ ์ •๋ ฌ ๊ฐ€๋Šฅ

  • DESC๋Š” ๋‚ด๋ฆผ์ฐจ์ˆœ, ASC ์˜ค๋ฆ„์ฐจ์ˆœ

6. LIMIT, OFFSET

LIMIT์œผ๋กœ ์กฐํšŒํ•  ๊ฐœ์ˆ˜ ์ œํ•œ

OFFSET์œผ๋กœ ์•ž์˜ ๋กœ์šฐ๋“ค ์Šคํ‚ต ๊ฐ€๋Šฅ(OFFSET 2๋ฉด ์„ธ ๋ฒˆ์งธ ๊ฒƒ๋ถ€ํ„ฐ ์ฐพ์Œ)

7. Update

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ์ž‘์—…

  • UPDATE ํ…Œ์ด๋ธ”๋ช… SET ์ปฌ๋Ÿผ=์ƒˆ๊ฐ’ WHERE ์กฐ๊ฑด

8. Delete

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๋Š” ์ž‘์—…

  • DELETE FROM ํ…Œ์ด๋ธ”๋ช… WHERE ์กฐ๊ฑด

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

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

SQL ์ž‘์—…์„ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

  • ORM: Object Relational Mapping: ๊ฐ์ฒด์™€ ๋ฐ์ดํ„ฐ๋ฅผ ๋งคํ•‘(1๋Œ€1 ์ง์ง€์Œ)
  • MySQL ์™ธ์—๋„ ๋‹ค๋ฅธ RDB(Maria, Postgre, SQLite, MSSQL)์™€๋„ ํ˜ธํ™˜๋จ
  • ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฌธ๋ฒ•์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐ์ž‘ ๊ฐ€๋Šฅ

๐Ÿ‘‰์ฐธ๊ณ 
https://github.com/ZeroCho/nodejs-book/tree/master/ch6/6.5.2/learn-express

  • ํ”„๋กœ์ ํŠธ ์„ธํŒ… ํ›„, ์ฝ˜์†”์„ ํ†ตํ•ด ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•œ ํ›„ package.json ์ž‘์„ฑ
    ๐Ÿ”ปpacket.json
{
  "name": "npmtest",
  "version": "1.0.0",
  "description": "์ €๋ฅผ ์ฝ์–ด์ฃผ์„ธ์š”.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "seokahi",
  "license": "MIT"
}

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

์‹œํ€„๋ผ์ด์ฆˆ ๋ช…๋ น์–ด ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด sequelize-cli ์„ค์น˜

  • mysql2๋Š” MySQL DB๊ฐ€ ์•„๋‹Œ ๋“œ๋ผ์ด๋ฒ„(Node.js์™€ MySQL์„ ์ด์–ด์ฃผ๋Š” ์—ญํ• )

    npx sequelize init์œผ๋กœ ์‹œํ€„๋ผ์ด์ฆˆ ๊ตฌ์กฐ ์ƒ์„ฑ

3. models/index.js ์ˆ˜์ •

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •
require(../config/config) ์„ค์ • ๋กœ๋”ฉ
new Sequelize(์˜ต์…˜๋“คโ€ฆ)๋กœ DB์™€ ์—ฐ๊ฒฐ ๊ฐ€๋Šฅ

๐Ÿ”ปmodels/index.js

const Sequelize = require('sequelize');
const User = require('./user');
const Comment = require('./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;

4. MySQL ์—ฐ๊ฒฐํ•˜๊ธฐ

app.js ์ž‘์„ฑ
sequelize.sync๋กœ ์—ฐ๊ฒฐ

๐Ÿ”ป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'), '๋ฒˆ ํฌํŠธ์—์„œ ๋Œ€๊ธฐ ์ค‘');
});

5. config.json ์„ค์ •ํ•˜๊ธฐ

DB ์—ฐ๊ฒฐ ์ •๋ณด๋ฅผ ๋„ฃ๊ธฐ
๐Ÿ”ปconfig/config.json

{
  "development": {
    "username": "root",
    "password": "nodejsbook",
    "database": "nodejs",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": null,
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}

6. ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธํ•˜๊ธฐ

npm start๋กœ ์‹คํ–‰ํ•ด์„œ SELECT 1+1 AS RESULT๊ฐ€ ๋‚˜์˜ค๋ฉด ์—ฐ๊ฒฐ ์„ฑ๊ณต

7. ๋ชจ๋ธ ์ƒ์„ฑํ•˜๊ธฐ

ํ…Œ์ด๋ธ”์— ๋Œ€์‘๋˜๋Š” ์‹œํ€„๋ผ์ด์ฆˆ ๋ชจ๋ธ ์ƒ์„ฑ
๐Ÿ”ปmodels/user.js

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,
      timestamps: false,
      underscored: false,
      modelName: 'User',
      tableName: 'users',
      paranoid: false,
      charset: 'utf8',
      collate: 'utf8_general_ci',
    });
  }

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

8. ๋ชจ๋ธ ์˜ต์…˜๋“ค

์‹œํ€„๋ผ์ด์ฆˆ ๋ชจ๋ธ์˜ ์ž๋ฃŒํ˜•์€ MySQL์˜ ์ž๋ฃŒํ˜•๊ณผ ์กฐ๊ธˆ ๋‹ค๋ฆ„
define ๋ฉ”์„œ๋“œ์˜ ์„ธ ๋ฒˆ์งธ ์ธ์ž๋Š” ํ…Œ์ด๋ธ” ์˜ต์…˜

  • timestamps: true๋ฉด createdAt(์ƒ์„ฑ ์‹œ๊ฐ„), updatedAt(์ˆ˜์ • ์‹œ๊ฐ„) ์ปฌ๋Ÿผ์„ ์ž๋™์œผ๋กœ ๋งŒ๋“ฆ
  • ์˜ˆ์ œ์—์„œ๋Š” ์ง์ ‘ created_at ์ปฌ๋Ÿผ์„ ๋งŒ๋“ค์—ˆ์œผ๋ฏ€๋กœ false๋กœ ํ•จ
  • paranoid ์˜ต์…˜์€ true๋ฉด deletedAt(์‚ญ์ œ ์‹œ๊ฐ„) ์ปฌ๋Ÿผ์„ ๋งŒ๋“ฆ, ๋กœ์šฐ ๋ณต๊ตฌ๋ฅผ ์œ„ํ•ด * ์™„์ „ํžˆ ์‚ญ์ œํ•˜์ง€ ์•Š๊ณ  deletedAt์— ํ‘œ์‹œํ•ด๋‘ 
  • underscored ์˜ต์…˜์€ ์บ๋ฉ€์ผ€์ด์Šค๋กœ ์ƒ์„ฑ๋˜๋Š” ์ปฌ๋Ÿผ์„ ์Šค๋„ค์ดํฌ์ผ€์ด์Šค๋กœ ์ƒ์„ฑ
  • modelName์€ ๋ชจ๋ธ ์ด๋ฆ„, tableName ์˜ต์…˜์€ ํ…Œ์ด๋ธ” ์ด๋ฆ„์„ ์„ค์ •
  • charset๊ณผ collate๋Š” ํ•œ๊ธ€ ์„ค์ •์„ ์œ„ํ•ด ํ•„์š”(์ด๋ชจํ‹ฐ์ฝ˜ ๋„ฃ์œผ๋ ค๋ฉด utf8mb4๋กœ)

9. ๋Œ“๊ธ€ ๋ชจ๋ธ ์ƒ์„ฑํ•˜๊ธฐ

๐Ÿ”ปmodels.comment.js

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' });
  }
};

๐Ÿ”ปmodels/user.js

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,
      timestamps: false,
      underscored: false,
      modelName: 'User',
      tableName: 'users',
      paranoid: false,
      charset: 'utf8',
      collate: 'utf8_general_ci',
    });
  }

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

10. ๋Œ“๊ธ€ ๋ชจ๋ธ ํ™œ์„ฑํ™”ํ•˜๊ธฐ

index.js์— ๋ชจ๋ธ ์—ฐ๊ฒฐ

  • init์œผ๋กœ sequelize์™€ ์—ฐ๊ฒฐ
  • associate๋กœ ๊ด€๊ณ„ ์„ค์ •

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

users ๋ชจ๋ธ๊ณผ comments ๋ชจ๋ธ ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์ •์˜

  • 1:N ๊ด€๊ณ„ (์‚ฌ์šฉ์ž ํ•œ ๋ช…์ด ๋Œ“๊ธ€ ์—ฌ๋Ÿฌ ๊ฐœ ์ž‘์„ฑ)
  • ์‹œํ€„๋ผ์ด์ฆˆ์—์„œ๋Š” 1:N ๊ด€๊ณ„๋ฅผ hasMany๋กœ ํ‘œํ˜„(์‚ฌ์šฉ์ž.hasMany(๋Œ“๊ธ€))
  • ๋ฐ˜๋Œ€์˜ ์ž…์žฅ์—์„œ๋Š” belongsTo(๋Œ“๊ธ€.belongsTo(์‚ฌ์šฉ์ž))
  • belongsTo๊ฐ€ ์žˆ๋Š” ํ…Œ์ด๋ธ”์— ์ปฌ๋Ÿผ์ด ์ƒ๊น€(๋Œ“๊ธ€ ํ…Œ์ด๋ธ”์— commenter ์ปฌ๋Ÿผ)

12. 1๋Œ€1 ๊ด€๊ณ„

1๋Œ€1 ๊ด€๊ณ„

  • ์˜ˆ) ์‚ฌ์šฉ์ž ํ…Œ์ด๋ธ”๊ณผ ์‚ฌ์šฉ์ž ์ •๋ณด ํ…Œ์ด๋ธ”

13. N๋Œ€M ๊ด€๊ณ„

๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„

  • ์˜ˆ) ๊ฒŒ์‹œ๊ธ€๊ณผ ํ•ด์‹œํƒœ๊ทธ ํ…Œ์ด๋ธ”
  • ํ•˜๋‚˜์˜ ๊ฒŒ์‹œ๊ธ€์ด ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ•ด์‹œํƒœ๊ทธ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ณ  ํ•˜๋‚˜์˜ ํ•ด์‹œํƒœ๊ทธ๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ฒŒ์‹œ๊ธ€์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Œ
  • DB ํŠน์„ฑ์ƒ ๋‹ค๋Œ€๋‹ค ๊ด€๊ณ„๋Š” ์ค‘๊ฐ„ ํ…Œ์ด๋ธ”์ด ์ƒ๊น€

14. ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ ์•Œ์•„๋ณด๊ธฐ

์œ— ์ค„์ด SQL๋ฌธ, ์•„๋žซ ์ค„์€ ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ(์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ)

15. ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ ์•Œ์•„๋ณด๊ธฐ

์œ— ์ค„์ด SQL๋ฌธ, ์•„๋žซ ์ค„์€ ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ(์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ)
ํŠน์ˆ˜ํ•œ ๊ธฐ๋Šฅ๋“ค์ธ ๊ฒฝ์šฐ Sequelize.Op์˜ ์—ฐ์‚ฐ์ž ์‚ฌ์šฉ(gt, or ๋“ฑ)

1: true

16. ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ ์•Œ์•„๋ณด๊ธฐ

์œ— ์ค„์ด SQL๋ฌธ, ์•„๋žซ ์ค„์€ ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ(์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ)

17. ์‹œํ€„๋ผ์ด์ฆˆ ์ฟผ๋ฆฌ ์•Œ์•„๋ณด๊ธฐ

์ˆ˜์ •

์‚ญ์ œ

18. ๊ด€๊ณ„ ์ฟผ๋ฆฌ

๊ฒฐ๊ณผ๊ฐ’์ด ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด์ž„

include๋กœ JOIN ๊ณผ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ ์ˆ˜ํ–‰ ๊ฐ€๋Šฅ(๊ด€๊ณ„ ์žˆ๋Š” ๊ฒƒ ์—ฎ์„ ์ˆ˜ ์žˆ์Œ)

๋‹ค๋Œ€๋‹ค ๋ชจ๋ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ ‘๊ทผ ๊ฐ€๋Šฅ

19. ๊ด€๊ณ„ ์ฟผ๋ฆฌ

get+๋ชจ๋ธ๋ช…์œผ๋กœ ๊ด€๊ณ„ ์žˆ๋Š” ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ๊ฐ€๋Šฅ

as๋กœ ๋ชจ๋ธ๋ช… ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ

20. ๊ด€๊ณ„ ์ฟผ๋ฆฌ

include๋‚˜ ๊ด€๊ณ„ ์ฟผ๋ฆฌ ๋ฉ”์„œ๋“œ์—๋„ where๋‚˜ attributes

๐Ÿ‘‰์ƒ์„ฑ ์ฟผ๋ฆฌ

21. ๊ด€๊ณ„ ์ฟผ๋ฆฌ

์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ๋Š” ๋ฐฐ์—ด๋กœ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ

์ˆ˜์ •์€ set+๋ชจ๋ธ๋ช…, ์‚ญ์ œ๋Š” remove+๋ชจ๋ธ๋ช…

22. raw ์ฟผ๋ฆฌ

์ง์ ‘ SQL์„ ์“ธ ์ˆ˜ ์žˆ์Œ

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

ํ”„๋ก ํŠธ ์ฝ”๋“œ๋ณด๋‹ค๋Š” ์„œ๋ฒ„ ์ฝ”๋“œ ์œ„์ฃผ๋กœ ๋ณด๊ธฐ

  • ํ”„๋ก ํŠธ ์ฝ”๋“œ๋Š” ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” AJAX ์š”์ฒญ ์œ„์ฃผ๋กœ๋งŒ ํŒŒ์•…

๐Ÿ”ป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;

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

users ๋ผ์šฐํ„ฐ

  • get, post, delete, patch ๊ฐ™์€ ์š”์ฒญ์— ๋Œ€ํ•œ ๋ผ์šฐํ„ฐ ์—ฐ๊ฒฐ
  • ๋ฐ์ดํ„ฐ๋Š” JSON ํ˜•์‹์œผ๋กœ ์‘๋‹ต
  • comments ๋ผ์šฐํ„ฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€

๐Ÿ”ปroutes/users.js

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;

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

comments ๋ผ์šฐํ„ฐ

๐Ÿ”ปroutes/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;

26. ์„œ๋ฒ„ ์ ‘์†ํ•˜๊ธฐ

npm start๋กœ ์„œ๋ฒ„ ์‹œ์ž‘

  • localhost:3000 ์œผ๋กœ ์ ‘์†ํ•˜๋ฉด ์‹œํ€„๋ผ์ด์ฆˆ๊ฐ€ ์ˆ˜ํ–‰ํ•˜๋Š” SQL๋ฌธ์ด ์ฝ˜์†”์— ์ฐํž˜

27. ์„œ๋ฒ„ ์ ‘์†ํ•˜๊ธฐ

๐Ÿ˜ƒ์ถœ์ฒ˜๐Ÿ˜ƒ
Node.js ๊ต๊ณผ์„œ - ๊ธฐ๋ณธ๋ถ€ํ„ฐ ํ”„๋กœ์ ํŠธ ์‹ค์Šต๊นŒ์ง€
https://www.inflearn.com/course/%EB%85%B8%EB%93%9C-%EA%B5%90%EA%B3%BC%EC%84%9C/dashboard

0๊ฐœ์˜ ๋Œ“๊ธ€