Search

Express κ²Œμ‹œνŒ ν”„λ‘œμ νŠΈ

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

κ²Œμ‹œκΈ€ μˆ˜μ •

Github (μ†ŒμŠ€μ½”λ“œ)