2023.12.22(금)
사용자가 입력한 데이터가 특정 규칙에 맞게 입력되었는지 그 유효성, 타당성을 검증하는 것
express-validator 설치 : npm install express-validator
다음의 HTTP request object들에 적용 가능🔗
req.body
: the body of the HTTP request. Can be any value, however objects, arrays and other JavaScript primitives work better.req.cookies
: the [Cookie
header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie) parsed as an object from cookie name to its value.req.headers
: the headers sent along with the HTTP request.req.params
: an object from name to value. In express.js, this is parsed from the request path and matched with route definition path, but it can really be anything meaningful coming from the HTTP request.req.query
: the portion after the ?
in the HTTP request's path, parsed as an object from query parameter name to value.어제 코드에 유효성 검사 추가 & 리팩토링
.
│ app.js
│ mariadb.js
│ package-lock.json
│ package.json
│
├─node_modules
│
└─routes
channels.js
users.js
// get the client
const mysql = require('mysql2')
// create the connection to database
const connection = mysql.createConnection({
// host: 'localhost',
// port: 3306,
user: 'root',
password: 'root',
// timezone: 'Asia/Seoul',
database: 'Youtube',
dateStrings: true
})
module.exports = connection;
const express = require('express') // npm install express
const app = express()
const port = 8888
app.listen(port, () => console.log(`> Server is running on http://localhost:${port}/`))
const userRouter = require('./routes/users')
const channelRouter = require('./routes/channels')
app.use(express.json())
app.use('/', userRouter)
app.use('/channels', channelRouter)
const express = require('express')
const router = express.Router()
const conn = require('../mariadb')
const { body, param, validationResult } = require('express-validator')
const validate = (req, res, next) => {
const err = validationResult(req)
if (err.isEmpty()) {
next()
} else {
return res.status(400).json(err.array())
}
}
// 로그인
router.post(
'/login',
[
body('email').notEmpty().isEmail().withMessage('이메일 확인 필요'),
body('password').notEmpty().isString().withMessage('비밀번호 확인 필요'),
validate
],
(req, res) => {
const { email, password } = req.body
let sql = 'SELECT * FROM `users` WHERE email = ?'
conn.query(
sql, email,
function (err, results) {
if (err) {
return res.status(400).json(err);
}
const loginUser = results[0]
if (loginUser && loginUser.password == password) {
res.status(200).json({ message: `${loginUser.name}님 환영합니다.` })
} else {
res.status(403).json({ message: "이메일 또는 비밀번호가 틀렸습니다." })
}
}
)
})
// 회원가입
router.post(
'/join',
[
body('email').notEmpty().isEmail().withMessage('이메일 확인 필요'),
body('password').notEmpty().isString().withMessage('비밀번호 확인 필요'),
body('name').notEmpty().isString().withMessage('이름 확인 필요'),
body('contact').notEmpty().isString().withMessage('연락처 확인 필요'),
validate
],
(req, res) => {
const { email, name, password, contact } = req.body
let sql = 'INSERT INTO `users` (email, name, password, contact) VALUES (?, ?, ?, ?)'
let values = [email, name, password, contact]
conn.query(
sql, values,
function (err, results) {
if (err) {
return res.status(400).json(err);
}
res.status(201).json({ message: `${name}님 환영합니다.`, results })
}
)
})
router.route('/users')
.get( // 회원 개별 조회
[
body('email').notEmpty().isEmail().withMessage('이메일 확인 필요'),
validate
],
(req, res) => {
let { email } = req.body
let sql = 'SELECT * FROM `users` WHERE email = ?'
conn.query(
sql, email,
function (err, results) {
if (err) {
return res.status(400).json(err);
}
if (results.length) {
res.status(200).json(results)
} else {
res.status(404).json({ message: "존재하지 않는 회원입니다." })
}
}
)
})
.delete( // 회원 개별 탈퇴
[
body('email').notEmpty().isEmail().withMessage('이메일 확인 필요'),
validate
],
(req, res) => {
let { email } = req.body
let sql = 'DELETE FROM `users` WHERE email = ?'
conn.query(
sql, email,
function (err, results) {
if (err) {
return res.status(400).json(err);
}
if (results.affectedRows != 0) {
res.status(200).json({ message: `탈퇴되었습니다.`, results })
} else {
res.status(404).json({ message: "존재하지 않는 회원입니다." })
}
}
)
})
module.exports = router
const express = require('express')
const router = express.Router()
const conn = require('../mariadb')
const { body, param, validationResult } = require('express-validator')
const channelNotFound = (res) => res.status(404).json({ message: "채널이 존재하지 않습니다." })
const validate = (req, res, next) => {
const err = validationResult(req)
if (err.isEmpty()) {
next()
} else {
return res.status(400).json(err.array())
}
}
router.route('/')
.get( // 채널 전체 조회
[
body('userId').notEmpty().isInt().withMessage('숫자를 입력해주세요.'),
validate
],
(req, res) => {
const { userId } = req.body
let sql = 'SELECT * FROM `channels` WHERE user_id = ?'
conn.query(
sql, userId,
function (err, results) {
if (err) {
return res.status(400).json(err);
}
if (results.length) {
res.status(200).json(results)
} else {
channelNotFound(res)
}
}
)
})
.post( // 채널 개별 생성
[
body('userId').notEmpty().isInt().withMessage('숫자를 입력해주세요.'),
body('name').notEmpty().isString().withMessage('문자를 입력해주세요.'),
validate
],
(req, res) => {
const { name, userId } = req.body
let sql = 'INSERT INTO `channels` (name, user_id) VALUES (?, ?)'
let values = [name, userId]
conn.query(
sql, values,
function (err, results) {
if (err) {
return res.status(400).json(err);
}
res.status(201).json({ message: `${name} 채널을 응원합니다.`, results })
}
)
})
router.route('/:id')
.get( // 채널 개별 조회
[
param('id').notEmpty().isInt().withMessage('숫자를 입력해주세요.'),
validate
],
(req, res) => {
let { id } = req.params
id = parseInt(id)
let sql = 'SELECT * FROM `channels` WHERE id = ?'
conn.query(
sql, id,
function (err, results) {
if (err) {
return res.status(400).json(err);
}
if (results.length) {
res.status(200).json(results)
} else {
channelNotFound(res)
}
}
)
})
.put( // 채널 개별 수정
[
param('id').notEmpty().isInt().withMessage('숫자를 입력해주세요.'),
body('name').notEmpty().isString().withMessage('채널명 오류'),
validate
],
(req, res) => {
let { id } = req.params
id = parseInt(id)
let { name } = req.body
let sql = 'UPDATE `channels` SET name = ? WHERE id = ?'
let values = [name, id]
conn.query(
sql, values,
function (err, results) {
if (err) {
return res.status(400).json(err);
}
if (results.affectedRows != 0) {
res.status(200).json({ message: `채널명이 ${name}(으)로 성공적으로 수정되었습니다.`, results })
} else {
channelNotFound(res)
}
}
)
})
.delete( // 채널 개별 삭제
[
param('id').notEmpty().isInt().withMessage('숫자를 입력해주세요.'),
validate
],
(req, res) => {
let { id } = req.params
id = parseInt(id)
let sql = 'DELETE FROM `channels` WHERE id = ?'
conn.query(
sql, id,
function (err, results) {
if (err) {
return res.status(400).json(err);
}
if (results.affectedRows != 0) {
res.status(200).json({ message: `채널이 삭제되었습니다.`, results })
} else {
channelNotFound(res)
}
}
)
})
module.exports = router
❓ middleware?
- 요청과 응답 중간(사이)에 위치하여 middleware라고 불림
- router와 error handler 또한 middleware의 일종이며, 요청과 응답 중간에서 특정 작업을 수행하기 위해 사용됨
next()
함수next()
를 호출해야하며 그렇지 않으면 request가 hanging됨설명 | 요약 | |
---|---|---|
next('route') | 호출 시 router middleware stack에서 나머지 middleware 함수들을 skip ※ app.METHOD() 또는 router.METHOD() 함수를 사용하여 load된 middleware 함수에서만 작동! | middleware sub-stack 밖으로 탈출 = 다음 middleware가 아니라 router로 이동 |
next('router') | 호출 시 router의 나머지 middleware 함수들을 skip하고 router 인스턴스 밖으로 control 전달 | express.Router()로 만든 router 인스턴스 밖으로 탈출 |
next(value)
를 호출함'route'
나 'router'
를 제외한 아무 값을 next()
함수에 넘기면 Express는 현재 request를 error로 간주하고 나머지 non-error handling routing과 middleware 함수들을 생략함! ⇒ 바로 가장 가까운 error handling middleware로 넘어감매 request마다 myLogger에 의해 console에 “LOGGED”가 출력되고 next()
로 다음 middleware인 app.get을 실행시킴
const express = require('express')
const app = express()
const myLogger = function (req, res, next) {
console.log('LOGGED')
next()
}
app.use(myLogger)
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(3000)
매 request마다 req 객체에 timestamp 값을 가진 requestTime property를 만들고 next()
로 다음 middleware인 app.get을 실행시킴
app.get에서는 requestTime middleware에서 만든 req.requestTime
property에서 timestamp를 가져와 출력함
객체에 property를 추가해주면 이후 다른 middleware에서 사용 가능
const express = require('express')
const app = express()
const requestTime = function (req, res, next) {
req.requestTime = Date.now()
next()
}
app.use(requestTime)
app.get('/', (req, res) => {
let responseText = 'Hello World!<br>'
responseText += `<small>Requested at: ${req.requestTime}</small>`
res.send(responseText)
})
app.listen(3000)
cookie-parser middleware를 사용해 req 객체에서 들어오는 cookie를 parse하여 cookieValidator 함수에 전달
validateCookies middlware는 Promise를 반환
next()
로 다음 middleware 실행 (코드에는 없기 때문에 request hanging됨)next()
로 rejected value나 thrown error 값을 넘김 → error handler middleware 실행async function cookieValidator (cookies) {
try {
await externallyValidateCookie(cookies.testCookie)
} catch {
throw new Error('Invalid cookies')
}
}
const express = require('express')
const cookieParser = require('cookie-parser')
const cookieValidator = require('./cookieValidator')
const app = express()
async function validateCookies (req, res, next) { // return Promise
await cookieValidator(req.cookies)
next()
}
app.use(cookieParser())
app.use(validateCookies)
// error handler
app.use((err, req, res, next) => {
res.status(400).send(err.message)
})
app.listen(3000)
options 객체나 다른 매개변수들을 허용하는 함수를 만들어야 하는 경우 다음과 같이 함수를 반환하는 함수 작성! (함수형 프로그래밍, 고차 함수)
// my-middleware.js 파일
module.exports = function (options) {
return function (req, res, next) {
// Implement the middleware function based on the options object
next()
}
}
// 또는
module.exports = (options) => (req, res, next) => {
// Implement the middleware function based on the options object
next()
}
const mw = require('./my-middleware.js')
app.use(mw({ option1: '1', option2: '2' }))
app.use()
와 app.METHOD()
함수를 사용해서 middleware를 app 객체 인스턴스에 바인딩
사용 방법 | 설명 |
---|---|
app.use(middleware) | 모든 요청에서 해당 middleware를 사용 |
app.use('/path', middleware) | 특정 경로로 접근할 때 middleware를 사용 |
app.METHOD('/path', middleware) | 특정 HTTP 메소드에 특정 경로로 접근할 때 middleware를 사용 |
express.Router()
로 만든 라우터 인스턴스에 바인딩router.use()
와 router.METHOD()
)으로 middleware를 등록하고 사용 가능app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
express.json()
(버전 4.16.0+)express.raw()
(버전 4.17.0+)express.Router()
express.static()
express.text()
(버전 4.17.0+)express.urlencoded()
(버전 4.16.0+)다음 주에 배울 할 JWT 실습이 기대된다.