Express ๊ฒ์ํ ํ๋ก์ ํธ
1.
ํ๋ก์ ํธ ํด๋ ์์ฑ - board
2.
๋ชจ๋ ์ค์น
3.
config.json (๋ฐ์ดํฐ ์์ค ์ค์ )
4.
model ์ค์
5.
๋ผ์ฐํฐ ์์ฑ
6.
๋ทฐ ์์ฑ
7.
app ์ค์
ํ๋ก์ ํธ ํด๋ ์์ฑ - board
package.json ์์ฑ
npm init
Shell
๋ณต์ฌ
๋ชจ๋ ์ค์น
# npm ์ด๊ธฐํ
npm init
# ๋ชจ๋ ์ค์น
npm install express morgan pug sequelize sequelize-cli mysql2 dotenv nodemon cookie-parser express-session
# ๊ฐ๋ณ ์ค์น
npm install express
npm install morgan
npm install pug
npm install sequelize
npm install sequelize-cli
npm install mysql2
npm install dotenv
npm install nodemon
npm install cookie-parser
npm install express-session
---
# sequelize ์ด๊ธฐํ (sequelize-cli ๋ชจ๋ ์ด์ฉ)
npx sequelize init
### sequelize ์ค์ ํ์ผ๋ค์ด ์์ฑ๋จ
### ์๋ ์์ฑ๋ models/index.js ์ ์๋ฌ ๋ฐ์์ด์ ๋ฑ์ผ๋ก ์ฝ๋ ์์
```
const Sequelize = require('sequelize');
// โ
๋ชจ๋ธ import
const Board = require('./board');
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.Board = Board;
// ๋ชจ๋ธ ์ด๊ธฐํ ๋ฉ์๋ ํธ์ถ
Board.initiate(sequelize);
// ์ฐ๊ด ๊ด๊ณ ์ค์ ๋ฉ์๋ ํธ์ถ (ํ์์ ๋ฐ๋ผ ์ฌ์ฉ)
Board.associate(db);
module.exports = db;
```
Markdown
๋ณต์ฌ
๋ชจ๋ ์ค์น
npm install express morgan pug sequelize sequelize-cli mysql2 dotenv nodemon cookie-parser express-session
Shell
๋ณต์ฌ
์ํ๋ผ์ด์ฆ ์ด๊ธฐํ
npx sequelize init
Shell
๋ณต์ฌ
config.json (๋ฐ์ดํฐ ์์ค ์ค์ )
{
"development": {
"username": "joeun",
"password": "123456",
"database": "test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "joeun",
"password": "123456",
"database": "test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "joeun",
"password": "123456",
"database": "test",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
JSON
๋ณต์ฌ
model ์ค์
index.js
const Sequelize = require('sequelize');
// โ
๋ชจ๋ธ import
const Board = require('./board');
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.Board = Board;
// ๋ชจ๋ธ ์ด๊ธฐํ ๋ฉ์๋ ํธ์ถ
Board.initiate(sequelize);
// ์ฐ๊ด ๊ด๊ณ ์ค์ ๋ฉ์๋ ํธ์ถ (ํ์์ ๋ฐ๋ผ ์ฌ์ฉ)
Board.associate(db);
module.exports = db;
JavaScript
๋ณต์ฌ
board.js
const { DataTypes, Model, Sequelize } = require('sequelize');
class Board extends Model {
static initiate(sequelize) {
Board.init(
{
board_no: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
title: {
type: DataTypes.STRING,
allowNull: false,
},
writer: {
type: DataTypes.STRING,
allowNull: false,
},
content: {
type: DataTypes.TEXT,
allowNull: false,
},
reg_date: {
type: 'TIMESTAMP',
// defaultValue: 'now()',
},
upd_date: {
type: 'TIMESTAMP',
// defaultValue: 'now()',
},
},
{
sequelize,
modelName: 'Board',
tableName: 'board',
timestamps: false, // true โก createdAt, updatedAt ์ปฌ๋ผ ์๋์์ฑ
}
);
}
static associate(db) {
// Add associations if needed
}
}
module.exports = Board;
JavaScript
๋ณต์ฌ
๋ผ์ฐํฐ ์์ฑ
board.js
const express = require('express')
const Sequelize = require('sequelize'); // โ
Sequelize ์ถ๊ฐ
const Board = require('../models/board') // โ
Board ๋ชจ๋ธ import
const router = express.Router()
// ๐ฉโ๐ป ๊ฒ์๊ธ ๋ชฉ๋ก
router.get('/', async (req, res) => {
console.log('๊ฒ์๊ธ ๋ชฉ๋ก...');
let boardList = []
try {
boardList = await Board.findAll() // โ
์ ์ฒด ๋ฐ์ดํฐ ์กฐํ
} catch (error) {
console.log(error);
}
// console.log(boardList);
res.render('board/list', {boardList} )
})
// ๐ฉโ๐ป ๊ฒ์๊ธ ๋ฑ๋ก
router.get('/insert', (req, res) => {
console.log('๊ฒ์๊ธ ๋ฑ๋ก ํ๋ฉด...');
res.render('board/insert')
})
// ๐ฉโ๐ป ๊ฒ์๊ธ ๋ฑ๋ก
router.post('/', async (req, res) => {
console.log('๊ฒ์๊ธ ๋ฑ๋ก...');
// ๊ตฌ์กฐ๋ถํดํ ๋น
const { title, writer, content } = req.body;
const newBoard = { title, writer, content };
let result = 0
try {
result = await Board.create(newBoard) // โ
๋ฐ์ดํฐ ๋ฑ๋ก
} catch (error) {
console.log(error);
}
console.log(`๋ฑ๋ก result : ${result}`);
res.redirect('/board');
});
// ๐ฉโ๐ป ๊ฒ์๊ธ ์์ ํ์ด์ง
router.get('/update/:id', async (req, res) => {
console.log('๊ฒ์๊ธ ์์ ํ๋ฉด...');
console.log(`id : ${req.params.id}`);
let id = req.params.id
let board = await Board.findByPk(id)
res.render('board/update', { board, id });
});
// ๐ฉโ๐ป ๊ฒ์๊ธ ์์
router.post('/update', async (req, res) => {
console.log('๊ฒ์๊ธ ์์ ...');
const { id, title, writer, content } = req.body;
let result = 0
try {
result = await Board.update({
title: title,
writer: writer,
content: content,
upd_date: Sequelize.literal('now()')
}, {
where: {board_no: id}
})
} catch (error) {
console.log(error);
}
console.log(`์์ result : ${result}`);
res.redirect(`/board/${id}`);
});
// ๐ฉโ๐ป ๊ฒ์๊ธ ์ญ์
router.post('/delete', async (req, res) => {
console.log('๊ฒ์๊ธ ์ญ์ ...');
const id = req.body.id;
let result = 0
try {
result = await Board.destroy({
where: { board_no : id }
})
} catch (error) {
console.log(error);
}
console.log(`์ญ์ result : ${result}`);
res.redirect('/board');
});
// ๐ฉโ๐ป ๊ฒ์๊ธ ์ฝ๊ธฐ
// ์์ฒญ ๊ฒฝ๋ก์ ํ๋ผ๋ฏธํฐ ๋งคํ ๋ฐฉ๋ฒ โก '/:ํ๋ผ๋ฏธํฐ๋ช
'
router.get('/:id', async (req, res) => {
console.log('๊ฒ์๊ธ ์ฝ๊ธฐ ํ๋ฉด...');
console.log(`id : ${req.params.id}`);
let id = req.params.id
let board = await Board.findByPk(id)
console.log(board);
res.render('board/read', {board, id})
})
module.exports = router;
JavaScript
๋ณต์ฌ
index.js
const express = require('express');
const router = express.Router();
// GET / ๋ผ์ฐํฐ
router.get('/', (req, res) => {
res.render('index', { title: 'Main' });
});
// ๋ก๊ทธ์ธ
router.get('/login', (req, res) => {
res.render('login', { title: 'Login' });
});
// ํ์๊ฐ์
router.get('/join', (req, res) => {
res.render('join', { title: 'Join' });
});
module.exports = router;
JavaScript
๋ณต์ฌ
๋ทฐ ์์ฑ
1.
./fragment/header.pug
2.
./fragment/footer.pug
3.
error.pug
4.
layout.pug
5.
index.pug
6.
login.pug
7.
join.pug
8.
./board/list.pug
9.
./board/insert.pug
10.
./board/read.pug
11.
./board/update.pug
./fragment/header.pug
header
h1 Pug
nav
ul
li
a(href="/") Home
li
a(href="/login") Login
li
a(href="/board") Board
HTML
๋ณต์ฌ
./fragment/footer.pug
footer
p Copyright ยฉ 2023 Pug
HTML
๋ณต์ฌ
error.pug
extends layout
block content
h1= message
h2= error.status
pre #{error.stack}
HTML
๋ณต์ฌ
layout.pug
doctype html
html
head
title= title
link(rel='stylesheet', href='/css/style.css')
body
include fragment/header.pug
block content
include fragment/footer.pug
HTML
๋ณต์ฌ
index.pug
extends layout
block content
div.container-lg
h1= title
h3 #{title} ํ๋ฉด์
๋๋ค
HTML
๋ณต์ฌ
login.pug
extends layout
block content
div.container
h2 Login
form
div.form-group
label(for="username") Username:
input(type="text", id="username", name="username", required)
div.form-group
label(for="password") Password:
input(type="password", id="password", name="password", required)
div.form-group
button.btn(type="submit") Login
a.btn(href="/join") Sign Up
HTML
๋ณต์ฌ
join.pug
extends layout
block content
div.container
h2 Sign Up
form
div.form-group
label(for="username") Username:
input(type="text", id="username", name="username", required)
div.form-group
label(for="email") Email:
input(type="email", id="email", name="email", required)
div.form-group
label(for="password") Password:
input(type="password", id="password", name="password", required)
div.form-group
button.btn(type="submit") Sign Up
HTML
๋ณต์ฌ
./board/list.pug
extends ../layout
block content
div.container-lg
div.btn-box
h1 ๊ฒ์๊ธ ๋ชฉ๋ก
a.btn(href="/board/insert") ๊ธ์ฐ๊ธฐ
table.table(border=1)
thead
tr
th No
th Title
th Writer
tbody
each board, index in boardList
tr
td(width=80)= board.board_no
td
a(href=`/board/${board.board_no}`) #{board.title}
td(width=200)= board.writer
HTML
๋ณต์ฌ
./board/insert.pug
extends ../layout
block content
div.container-lg
h1 ๊ฒ์๊ธ ๋ฑ๋ก
form(action="/board", method="post")
table.table.form-group(border=1)
tr
th(width="200") Title
td
input(type="text", name="title")
tr
th(width="200") Writer
td
input(type="text", name="writer")
tr
th(width="200") Content
td
input(type="text", name="content")
div.btn-box
a.btn(href="/board") ๋ชฉ๋ก
button.btn(type="submit") ๋ฑ๋ก
HTML
๋ณต์ฌ
./board/read.pug
extends ../layout
block content
div.container-lg
h1 #{board.title}
table.table(border=1)
tr
th(width="200") No
td #{id}
tr
th(width="200") Writer
td #{board.writer}
tr
th(width="200") Content
td #{board.content}
div.btn-box
a.btn(href="/board") ๋ชฉ๋ก
a.btn(href=`/board/update/${id}`) ์์
HTML
๋ณต์ฌ
./board/update.pug
extends ../layout
block content
div.container-lg
h1 ๊ฒ์๊ธ ์์
form(action="/board/update", method="post" id="form")
input(type="hidden", name="id" value=`${id}`)
table.table.form-group(border=1)
tr
th(width="200") No
td #{id}
tr
th(width="200") Title
td
input(type="text", name="title", value=`${board.title}`)
tr
th(width="200") Writer
td
input(type="text", name="writer", value=`${board.writer}`)
tr
th(width="200") Content
td
input(type="text", name="content", value=`${board.content}`)
div.btn-box
div.item
a.btn(href="/board") ๋ชฉ๋ก
div.item
button.btn(type="submit") ์์
a.btn(href="javascript:;" id="btn-delete") ์ญ์
script.
document.getElementById('btn-delete').addEventListener('click', function() {
let form = document.getElementById('form')
form.action = '/board/delete';
form.submit();
});
HTML
๋ณต์ฌ
app ์ค์
.env
# ๋ชจ๋ (development, test, production)
NODE_ENV=development
# PORT ์ค์
PORT=3000
# ์ฟ ํค
COOKIE_SECRET=cookiesecret
Plain Text
๋ณต์ฌ
app.js
const express = require('express');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const dotenv = require('dotenv');
const path = require('path');
const { sequelize } = require('./models'); // โ
์ํ๋ผ์ด์ฆ
// dotenv ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ํ๊ฒฝ ๋ณ์๋ฅผ ๋ก๋ํ๋ ๋ถ๋ถ
// ์ด ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด ํ๋ก์ ํธ ๋ฃจํธ์ ์์นํ .env ํ์ผ์ ํ๊ฒฝ ๋ณ์๊ฐ process.env ๊ฐ์ฒด์ ์ถ๊ฐ๋ฉ๋๋ค.
dotenv.config();
// ๐ฉโ๐ป ๋ผ์ฐํฐ ๋ชจ๋ import
const indexRouter = require('./routes/index');
const boardRouter = require('./routes/board');
const app = express();
// ํฌํธ ์ค์ : ํ๊ฒฝ ๋ณ์์์ PORT๋ฅผ ๊ฐ์ ธ์ค๊ณ , ์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ผ๋ก 3000 ์ฌ์ฉ
app.set('port', process.env.PORT || 3000);
// โ
์ํ๋ผ์ด์ฆ ์ฑํฌ
// - ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ
์ด๋ธ ์์ฑ ๋๋ ๋๊ธฐํ
sequelize.sync({ force: false })
.then(() => {
console.log('๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ฑ๊ณต');
})
.catch((err) => {
console.error(err);
});
// ๋ทฐ ์์ง ์ค์ : Pug๋ฅผ ์ฌ์ฉํ๋๋ก ์ค์
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// ๋ก๊น
๋ฏธ๋ค์จ์ด ์ค์ : ๊ฐ๋ฐ ํ๊ฒฝ์์๋ dev ๋ชจ๋๋ก ๋ก๊ทธ๋ฅผ ์ถ๋ ฅ
app.use(morgan('dev'));
// ์ ์ ํ์ผ ์ ๊ณต ๋ฏธ๋ค์จ์ด ์ค์ : public ํด๋๋ฅผ ์ ์ ํ์ผ ์ ๊ณต ๋๋ ํ ๋ฆฌ๋ก ์ค์
app.use('/', express.static(path.join(__dirname, 'public')));
// JSON ํ์ฑ ๋ฏธ๋ค์จ์ด ์ค์
// - JSON ํ์์ ์์ฒญ ๋ณธ๋ฌธ์ ํ์ฑ
// - URL ์ธ์ฝ๋ฉ๋ ์์ฒญ ๋ณธ๋ฌธ์ ํ์ฑ
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// cookie-parser ๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ์ฌ ์ฟ ํค๋ฅผ ํ์ฑํ๋ ๋ถ๋ถ
// process.env.COOKIE_SECRET๋ .env ํ์ผ์ด๋ ํ๊ฒฝ ๋ณ์์์ ์ค์ ํ ๋น๋ฐ ํค๋ฅผ ์ฌ์ฉ
app.use(cookieParser(process.env.COOKIE_SECRET));
// Express ์ ํ๋ฆฌ์ผ์ด์
์ ์ธ์
๋ฏธ๋ค์จ์ด ์ค์
app.use(session({
resave: false, // ์ธ์
๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋์ง ์์ผ๋ฉด ์๋ฒ์ ๋ค์ ์ ์ฅํ์ง ์์
saveUninitialized: false, // ์ด๊ธฐํ๋์ง ์์ ์ธ์
์ ์ ์ฅ์์ ์ ์ฅํ์ง ์์
secret: process.env.COOKIE_SECRET, // ์ธ์
์๋ณ์ ์ํ ๋น๋ฐ ํค
// ์ธ์
์ฟ ํค ์ค์
cookie: {
httpOnly: true, // ๋ธ๋ผ์ฐ์ ์์ ์ฟ ํค์ ์ ๊ทผํ ๋๋ง ๊ฐ๋ฅํ๋๋ก httpOnly ์์ฑ ์ฌ์ฉ
secure: false, // HTTPS๊ฐ ์๋ ํ๊ฒฝ์์๋ ์ฟ ํค ์ ์ก ํ์ฉ
},
name: 'session-cookie', // ์ธ์
์ฟ ํค์ ์ด๋ฆ ์ค์
}));
// ๐ฉโ๐ป ๋ผ์ฐํฐ ์ค์
app.use('/', indexRouter);
app.use('/board', boardRouter);
// 404 ์ค๋ฅ ์ฒ๋ฆฌ ๋ฏธ๋ค์จ์ด
app.use((req, res, next) => {
// ์์ฒญ๋ ๊ฒฝ๋ก๊ฐ ์กด์ฌํ์ง ์์ ๋ 404 ์ํ ์ฝ๋์ 'Not Found' ๋ฉ์์ง๋ฅผ ์๋ต์ผ๋ก ์ ์ก
res.status(404).send('Not Found');
});
// ์๋ฌ ์ฒ๋ฆฌ ๋ฏธ๋ค์จ์ด
app.use((err, req, res, next) => {
// ์๋ฌ๊ฐ ๋ฐ์ํ์ ๋, ์๋ฌ ๋ฉ์์ง๋ฅผ ์ฝ์์ ์ถ๋ ฅํ๊ณ 500 ์ํ ์ฝ๋์ ์๋ฌ ๋ฉ์์ง๋ฅผ ์๋ต์ผ๋ก ์ ์ก
console.error(err);
res.status(500).send(err.message);
});
// Express ์ ํ๋ฆฌ์ผ์ด์
์ ํน์ ํฌํธ์์ ์คํํ๋ ๋ถ๋ถ
app.listen(app.get('port'), () => {
// ์ ํ๋ฆฌ์ผ์ด์
์ด ์คํ๋๋ฉด ์ฝ์์ ํด๋น ํฌํธ์์ ๋๊ธฐ ์ค์์ ์ถ๋ ฅ
console.log(app.get('port'), '๋ฒ ํฌํธ์์ ๋๊ธฐ ์ค');
});
JavaScript
๋ณต์ฌ
์คํ ํ๋ฉด
/
๋ฉ์ธํ๋ฉด
/login
๋ก๊ทธ์ธ ํ๋ฉด
/join
ํ์๊ฐ์
ํ๋ฉด
/board
๊ฒ์๊ธ ๋ชฉ๋ก
/board/insert
๊ฒ์๊ธ ๋ฑ๋ก
/board/2
๊ฒ์๊ธ ์ฝ๊ธฐ
/board/update/2
๊ฒ์๊ธ ์์