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
κ²μκΈ μμ