๐7์ฅ ํค์๋๐
MySQL
์ํฌ๋ฒค์น
CRUD
์ํ๋ผ์ด์ฆ
์๋ฒ๋ฅผ ์ข ๋ฃํด๋ ๋ฐ์ดํฐ๊ฐ ์ ์ฅ๋ ์ ์๋๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ํ์ฉํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๐ ๋ฐ์ดํฐ๋ฒ ์ด์ค
https://dev.mysql.com/downloads/installer/
[Node.js ๊ต๊ณผ์] ์ฐธ๊ณ
https://dev.mysql.com/downloads/workbench/
[Node.js ๊ต๊ณผ์] ์ฐธ๊ณ
๐ MySQL ์๋ฃํ
์๋ฃํ | ์๋ฏธ | ๋น๊ณ |
INT | ์ ์ | ์์๊น์ง ์ ์ฅ : FLOAT, DOUBLE |
VARCHAR | ์๋ฆฟ์ | ๊ฐ๋ณ๊ธธ์ด |
CHAR | ์๋ฆฟ์ | ๊ณ ์ ๊ธธ์ด, ๋ถ์กฑํ ์๋ฆฟ์๋ ์คํ์ด์ค๋ก ์ฑ์ด๋ค. |
TEXT | ๊ธด ๊ธ | ์๋ฐฑ ์ ์ด์ |
TINYINT | -128~127๊น์ง์ ์ ์ | Boolean ๋์ ์ฌ์ฉ |
DATETIME, DATE, TIME | ๋ ์ง์ ์๊ฐ |
๐ ์ปฌ๋ผ ์ต์
์ต์ | ๋น๊ณ |
PRIMARY KEY (PR) | ๊ธฐ๋ณธํค |
NULL, NOT NULL (NN) | NULL ํ์ฉ ์ฌ๋ถ |
UNIQUE INDEX (UQ) | ๊ณ ์ ๊ฐ |
BINARY (B) | ๋ฐ์ดํฐ๋ฅผ ์ด์ง ๋ฌธ์์ด๋ก ์ ์ฅ |
UNSIGNED (UN) | ์์๋ฅผ ๋ฌด์ํ๊ณ ์ซ์ ์ ์ฅ, FLOAT์ DOUBLE์๋ ์ ์ฉ ๋ถ๊ฐ๋ฅ |
ZEROFILL (ZF) | ์ซ์์ ์๋ฆฟ์๋ฅผ ๊ณ ์ INT(4)์ 1 ์ ์ฅ → 0001 |
AUTO_INCREMENT (AI) | ์ซ์๋ฅผ ์๋์ผ๋ก ์ฆ๊ฐ |
GENERATED COLUMN (G) | ๋ค๋ฅธ ์ด์ ๊ธฐ๋ฐ์ผ๋ก ํ ์์์ผ๋ก ์์ฑ๋ ๊ฐ |
๐ ํ ์ด๋ธ ์์ฑ
๐ ์ํ๋ผ์ด์ฆ
๐ ORM
๐ ํจํค์ง ์ค์น ๋ฐ ํธ์ถ
npm i express morgan nunjucks sequelize sequelize-cli mysql2
npm i -D nodemon
npx sequelize init
โ app.js๋ฅผ ํตํด ์ต์คํ๋ ์ค์ ์ํ๋ผ์ด์ฆ ์ฐ๊ฒฐ ์ฝ๋๋ฅผ ์์ฑ
1๏ธโฃ models/index.js ํ์ธ
const Sequelize = require('sequelize');
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;
module.exports = db;
2๏ธโฃ config/config.json ํ์ธ
{
"development": {
"username": "์ฌ์ฉ์ ์ด๋ฆ",
"password": "๋น๋ฐ๋ฒํธ",
"database": "๋ฐ์ดํฐ๋ฒ ์ด์ค๋ช
",
"host": "127.0.0.1",
"dialect": "mysql"
},
...
}
3๏ธโฃ app.js ์์ฑ
const express = require('express');
const morgan = require('morgan');
const path = require('path');
const nunjucks = require('nunjucks');
const { sequelize } = require('./models');
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((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'), '๋ฒ ํฌํธ์์ ๋๊ธฐ ์ค');
});
โ MySQL์์ ์ ์ํ ํ ์ด๋ธ์ ์ํ๋ผ์ด์ฆ์์๋ ์ ์ํด์ผ ํฉ๋๋ค.
โ MySQL์ ํ ์ด๋ธ์ ์ํ๋ผ์ด์ฆ์ ๋ชจ๋ธ๊ณผ ๋์๋ฉ๋๋ค.
๐user.js, comment.js ์์ฑ
๐๋ชจ๋ธ ์์ฑ๋ฒ
๐ํ ์ด๋ธ ์ต์
MySQL | ์ํ๋ผ์ด์ฆ |
VARCHAR(100) | STRING(100) |
INT | INTEGER |
TINYINT | BOOLEAN |
DATETIME | DATE |
NOT NULL | allowNull: false |
UNIQUE | unique: true |
DEFAULT now() | defaultValue: Sequelize.NOW |
๐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.User = User;
db.Comment = Comment;
User.initiate(sequelize); // ๊ฐ๊ฐ์ ๋ชจ๋ธ์ static initaiate ๋ฉ์๋ ํธ์ถ
Comment.initiate(sequelize);
User.associate(db);
Comment.associate(db);
module.exports = db;
โถ๏ธ db ๊ฐ์ฒด๋ฅผ ์ด์ฉํด์ User์ Comment ๋ชจ๋ธ์ ์ ๊ทผ
โ user์ comments ํ ์ด๋ธ ๊ฐ์ ๊ด๊ณ ์ ์
โ ๋ชจ๋ธ ๊ฐ๊ฐ์ static associate์ ์ถ๊ฐ
โ ์ํ์ฐธ์กฐ๋ฅผ ํผํ๊ธฐ ์ํด์ db ๋งค๊ฐ๋ณ์ ์ฌ์ฉ
๐hasMany์ belongsTo
๐hasOne๊ณผ belongsTo
๐belongsToMany์ belongsToMany
โ ๊ฐ๋จํ๊ฒ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ฑ๋กํ๊ณ ์ฌ์ฉ์๊ฐ ๋ฑ๋กํ ๋๊ธ์ ๊ฐ์ ธ์ค๋ ์๋ฒ๋ฅผ ์ ์ํด ๋ณด๊ฒ ์ต๋๋ค.
1๏ธโฃ sequelize.js ํ์ผ ์์ฑ
// ์ฌ์ฉ์ ์ด๋ฆ ๋๋ ์ ๋ ๋๊ธ ๋ก๋ฉ
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 = '';
});
๐ script ํ๊ทธ์๋ ๋ฒํผ๋ค์ ๋๋ ์ ๋ ์๋ฒ์ ๋ผ์ฐํฐ๋ก AJAX ์์ฒญ์ ๋ณด๋ด๋ ์ฝ๋๊ฐ ๋ค์ด์์ต๋๋ค.
2๏ธโฃ ๋ผ์ฐํฐ ์์ฑ
โ sequelize.js์ ๋์ค๋ GET, POST, PUT, DELETE ์์ฒญ์ ํด๋นํ๋ ๋ผ์ฐํฐ๋ฅผ ๋ง๋ญ๋๋ค.
โ routes ํด๋๋ฅผ ๋ง๋ค๊ณ ๊ทธ ์์ index.js, users.js, comments.js๋ฅผ ์์ฑํฉ๋๋ค.
๐ 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;
๐ 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;
๐ comments.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;
3๏ธโฃ app.js์ ๋ผ์ฐํฐ๋ฅผ ์ฐ๊ฒฐํฉ๋๋ค.
...
const { sequelize } = require('./models');
const indexRouter = require('./routes');
const usersRouter = require('./routes/users');
const commentsRouter = require('./routes/comments');
const app = express();
...
app.use(express.urlencoded({ extended: false }));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/comments', commentsRouter);
๋น์นธ ์ฑ์ฐ๊ธฐ ๋ฌธ์ (๋น์นธ์ ๋๋๊ทธํด์ ์ ๋ต์ ๋งํ ๋ณด์ธ์!)
1. ๊ด๋ จ์ฑ์ ๊ฐ์ง๋ฉฐ ์ค๋ณต์ด ์๋ ๋ฐ์ดํฐ๋ค์ ์งํฉ์ (๋ฐ์ดํฐ๋ฒ ์ด์ค)๋ผ๊ณ ํ๋ค.
2. MySQL์ ์๋ฃํ์ ์ฑ์ฐ์์ค.
์๋ฃํ | ์๋ฏธ | ๋น๊ณ |
INT | ์ ์ | ์์๊น์ง ์ ์ฅ : FLOAT, DOUBLE |
VARCHAR | ์๋ฆฟ์ | ๊ฐ๋ณ๊ธธ์ด |
CHAR | ์๋ฆฟ์ | ๊ณ ์ ๊ธธ์ด, ๋ถ์กฑํ ์๋ฆฟ์๋ ์คํ์ด์ค๋ก ์ฑ์ด๋ค. |
TEXT | ๊ธด ๊ธ | ์๋ฐฑ ์ ์ด์ |
TINYINT | -128~127๊น์ง์ ์ ์ | Boolean ๋์ ์ฌ์ฉ |
DATETIME, DATE, TIME | ๋ ์ง์ ์๊ฐ |
3. MySQL์ ์ปฌ๋ผ ์ต์ ์ ์ฑ์ฐ์์ค.
์ต์ | ๋น๊ณ |
PRIMARY KEY (PR) | ๊ธฐ๋ณธํค |
NULL, NOT NULL (NN) | NULL ํ์ฉ ์ฌ๋ถ |
UNIQUE INDEX (UQ) | ๊ณ ์ ๊ฐ |
BINARY (B) | ๋ฐ์ดํฐ๋ฅผ ์ด์ง ๋ฌธ์์ด๋ก ์ ์ฅ |
UNSIGNED (UN) | ์์๋ฅผ ๋ฌด์ํ๊ณ ์ซ์ ์ ์ฅ, FLOAT์ DOUBLE์๋ ์ ์ฉ ๋ถ๊ฐ๋ฅ |
ZEROFILL (ZF) | ์ซ์์ ์๋ฆฟ์๋ฅผ ๊ณ ์ INT(4)์ 1 ์ ์ฅ → 0001 |
AUTO_INCREMENT (AI) | ์ซ์๋ฅผ ์๋์ผ๋ก ์ฆ๊ฐ |
GENERATED COLUMN (G) | ๋ค๋ฅธ ์ด์ ๊ธฐ๋ฐ์ผ๋ก ํ ์์์ผ๋ก ์์ฑ๋ ๊ฐ |
4. ํ ์ด๋ธ์ ๊ด๊ณ๊ฐ 1:N์ผ ๋, 1์ธ ๋ชจ๋ธ์ (hasMany) ๋ฉ์๋๋ก, N์ธ ๋ชจ๋ธ์ (belongsTo) ๋ฉ์๋๋ก ๊ด๊ณ๋ฅผ ํํํ๋ค.
5. ๋ ธ๋์์ MySQL ์์ ์ ์ฝ๊ฒ ํ ์ ์๋๋ก ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ (์ํ๋ผ์ด์ )๋ผ๊ณ ํ๋ค.
6. ๋ชจ๋ธ์ ์ ์ํ ๋, ํ ์ด๋ธ์ ๋ํ ์ค์ ์ (static initiate) ๋ฉ์๋๋ฅผ, ๋ค๋ฅธ ๋ชจ๋ธ๊ณผ์ ๊ด๊ณ ์์ฑ์ (static associate) ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค.
์ฝ๋ ๋ฌธ์
1. ๋น์นธ์ ๋ค์ด๊ฐ ์ฝ๋๋ฅผ ์์ฑํด ๋ณด์ธ์.
const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env];
const db = {};
const sequelize = new Sequelize(config.{1๋ฒ ๋น์นธ}, config.{2๋ฒ ๋น์นธ}, config.{3๋ฒ ๋น์นธ}, config);
db.sequelize = sequelize;
module.exports = db;
์ ๋ต: database, username, password
2. ๋น์นธ์ ๋ค์ด๊ฐ ์ฝ๋๋ฅผ ์์ฑํด ๋ณด์ธ์.
const Sequelize = require('sequelize');
class Comment extends Sequelize.Model {
static {1๋ฒ ๋น์นธ}(sequelize) {
Comment.init({
comment: {
type: Sequelize.STRING(100),
allowNull: false,
},
created_at: {
type: Sequelize.DATE,
allowNull: true,
defaultValue: Sequelize.NOW,
},
}, {
...
}
static {2๋ฒ ๋น์นธ} (db) {
db.Comment.{3๋ฒ ๋น์นธ}(db.User, { foreignKey: 'commenter', targetKey: 'id' });
}
};
module.exports = Comment;
์ ๋ต: initiate, associate, belongsTo
[๋ ธ๋ 2] 9์ฅ. ์ต์คํ๋ ์ค๋ก SNS ์๋น์ค ๋ง๋ค๊ธฐ (1) | 2023.12.01 |
---|---|
[๋ ธ๋ 2] 8์ฅ. MongoDB (0) | 2023.11.24 |
[๋ ธ๋ 2] 6์ฅ. ์ต์คํ๋ ์ค ์น ์๋ฒ ๋ง๋ค๊ธฐ (0) | 2023.11.10 |
[๋ ธ๋ 2] 5์ฅ. ํจํค์ง ๋งค๋์ (0) | 2023.11.03 |
[๋ ธ๋ 2] 4์ฅ. http ๋ชจ๋๋ก ์๋ฒ ๋ง๋ค๊ธฐ (0) | 2023.11.03 |