1️⃣ 유효성 검사(valid ation)는 무엇인가?
if문을 수시로 사용하게 되면 코드가 더러워짐
"사용자가 입력한 값(사용자의 요청)"에 대한 유효성(타당성)을 확인하는 것
지금까지는 이것을 if문을 사용했지만, 외부모듈 express-validater을 사용하면 코드가 깔끔해질 수 있다.
설치 후 [사용 가이드]를 참고해서 코드에 적용해보자
1️⃣ userId에 대한 유효성 검사를 해보자
const {body, validationResult} = require('express-validator') // body 메소드를 불러와 벨리데이터 모듈을 넣어준다, 추가적으로 오류를 받아주는 리절트 변수도 넣어준다
router
.route('/:id')
.post(
body('userId').notEmpty().isInt().withMessage('숫자를 입력하자') // 바디 메소드에 검사할 데이터를 넣고 조건들을 부여함
, (req, res) => {
const err = validationResult(req) // 만약 에러가 있다면
if (!err.isEmpty()){ // 에러를 반환하도록
console.log(err.array())
}
const {name, userId} = req.body
if (name){ // 유효성 검사를 위에서 하니 userId는 빼줘도 됨
let sql = `INSERT INTO channels (name, user_id) VALUES (?, ?)`
let values = [name, userId]
conn.query(sql, values,
function(err, results) {
res.json(results); // 쿼리 결과를 클라이언트에 응답
}
)
} else {
res.status(400).json({
message : '요청 값을 제대로 보내주세요'
})
}
})
1️⃣ name의 유효성 검사 추가
[배열]로 묶어주면 된다. return을 이용해 곧바로 원하는 결과로 코드를 종료할 수 있다. .post(
[body('userId').notEmpty().isInt().withMessage('숫자입력 필요'), // 바디 메소드에 검사할 데이터를 넣고 조건들을 부여함
body('name').notEmpty().isString().withMessage('문자입력 필요')] // 배열을 이용하면 여러 유효성 검사 가능
, (req, res) => {
const err = validationResult(req) // 만약 에러가 있다면
if (!err.isEmpty()){ // 에러를 반환하도록 하고
console.log(err.array()) // 에러의 배열을 꺼내보자
return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
}
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) {
res.json(results); // 쿼리 결과를 클라이언트에 응답
}
)
})

1️⃣ 없는 유저id로 채널을 생성하면 데이터베이스에 저장이 안된다
없는 유저 id로 채널을 생성하면 에러가 발생하지 않는다.

다시 제대로 채널을 만들고 확인해보니 채널 테이블에는 저장이 안 되어있고 아이디는 붕 떠있는 것을 확인할 수 있다. 에러는 발생하지 않았지만, 테이블 저장은 안되는 것이었다.


.post(
[body('userId').notEmpty().isInt().withMessage('숫자입력 필요'), // 바디 메소드에 검사할 데이터를 넣고 조건들을 부여함
body('name').notEmpty().isString().withMessage('문자입력 필요')]
, (req, res) => {
const err = validationResult(req) // 만약 에러가 있다면
if (!err.isEmpty()){ // 에러를 반환하도록
console.log(err.array())
return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
}
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) {
console.log(err)
return res.status(400).end(); // 에러가 걸리면 끝내도록 설정
}
res.status(200).json(results) // 쿼리 결과를 클라이언트에 응답
}
)
})


1️⃣ SQL을 먹이고 나머지 코드도 DB와 연동해보자
const express = require('express')
// 모듈화를 위해 서버를 불러오는 건 app.js로 넘김
const router = express.Router() // express에 Router로 사용할 수 있도록 만들어 줌
// app이라는 서버에 직접 연결 해주던 것을 app,js에 넘기게 되어
// app이 사용된 모든 곳을 router로 변경 해줌
const conn = require('../mariadb'); // Promise 기반의 mariadb 연결 객체 가져옴
const {body, param, validationResult} = require('express-validator') // body 메소드를 불러와 벨리데이터 모듈을 넣어준다, 추가적으로 오류를 받아주는 리절트 변수도 넣어준다
router.use(express.json()) //http 외 모듈 사용 'json 모듈'
let db = new Map()
let id = 1
router
.route('/') // route로 URL 묶어주기
// 채널 개별 생성
.post(
[body('userId').notEmpty().isInt().withMessage('숫자입력 필요'), // 바디 메소드에 검사할 데이터를 넣고 조건들을 부여함
body('name').notEmpty().isString().withMessage('문자입력 필요')]
, (req, res) => {
const err = validationResult(req) // 만약 에러가 있다면
if (!err.isEmpty()){ // 에러를 반환하도록
return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
}
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)
{ console.log(err)
return res.status(400).end()
} // 에러가 걸리면 끝내도록 설정
res.status(200).json(results) // 쿼리 결과를 클라이언트에 응답
}
)
})
// 채널 전체 조회
.get(
body('userId').notEmpty().isInt().withMessage('숫자 입력 필요') // 유효성 검사는 사용자의 요청에 대해서 구분이 가능
, (req, res) => {
const err = validationResult(req) // 만약 에러가 있다면
if (!err.isEmpty()){ // 에러를 반환하도록
return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
}
let {userId} = req.body
let sql = 'SELECT * FROM channels WHERE user_id = ?'
conn.query(sql, userId,
function(err, results) {
if (err) {
console.log(err)
res.status(400).end()
}
if (results.length) {
res.status(200).json(results) // 쿼리 결과를 클라이언트에 응답
} else {
notFoundChannel(res)
}
}
)
})
router
.route('/:id')
// 채널 개별 조회
.get(
param('id').notEmpty().withMessage('채널id 필요') // param은 url에서 꺼내쓰는 것이기 때문에 body가 아닌 pram으로 지정
,(req, res) => {
const err = validationResult(req) // 만약 에러가 있다면
if (!err.isEmpty()){ // 에러를 반환하도록
return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
}
let {id} = req.params // req.params 객체에서 URL 경로에 포함된 id 값을 추출하여 id 변수에 저장
id = parseInt(id) // URL에서 문자열로 추출된 id를 정수로 변환
let sql = 'SELECT * FROM channels WHERE id = ?'
conn.query(sql, id, // email 값을 바인딩
function(err, results) {
if (err) {
console.log(err)
res.status(400).end()
}
if (results.length) {
res.status(200).json(results); // 쿼리 결과를 클라이언트에 응답
} else {
notFoundChannel(res)
}
}
)
})
1️⃣ update
.put(
[param('id').notEmpty().withMessage('채널id 필요'), // param은 url에서 꺼내쓰는 것이기 때문에 body가 아닌 pram으로 지정
body('name').notEmpty().isString().withMessage('채널명 오류')
],(req, res) => {
const err = validationResult(req) // 만약 에러가 있다면
if (!err.isEmpty()){ // 에러를 반환하도록
return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
}
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) {
console.log(err)
res.status(400).end()
}
if (results.affectedRows == 0) {
return res.status(400).end() // 쿼리가 실행되었지만 변경된 행이 없을 경우 400 에러 응답
} else {
res.status(200).json(results) // 쿼리 실행으로 행이 변경된 경우 성공적으로 응답
}
}
)
})
2️⃣ delete
.delete(
param('id').notEmpty().withMessage('채널id 필요') // param은 url에서 꺼내쓰는 것이기 때문에 body가 아닌 pram으로 지정
,(req, res) => {
const err = validationResult(req) // 만약 에러가 있다면
if (!err.isEmpty()){ // 에러를 반환하도록
return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
}
let {id} = req.params
id = parseInt(id)
let sql = 'DELETE FROM channels WHERE id = ?'
conn.query(sql, id, // email 값을 바인딩
function(err, results) {
if (err) {
console.log(err)
res.status(400).end()
}
res.json(results); // 쿼리 결과를 클라이언트에 응답
}
)
})
채널이 정상적으로 삭제되는 것을 볼 수 있음.


없는 채널을 삭제해도 정상작동을 함. 여기도 affectedRows가 없다면 에러메시지 추가

.delete(
param('id').notEmpty().withMessage('채널id 필요') // param은 url에서 꺼내쓰는 것이기 때문에 body가 아닌 pram으로 지정
,(req, res) => {
const err = validationResult(req) // 만약 에러가 있다면
if (!err.isEmpty()){ // 에러를 반환하도록
return res.status(400).json(err.array()) // return을 써주면 함수가 곧바로 종료됨
}
let {id} = req.params
id = parseInt(id)
let sql = 'DELETE FROM channels WHERE id = ?'
conn.query(sql, id, // email 값을 바인딩
function(err, results) {
if (err) {
console.log(err)
res.status(400).end()
}
if (results.affectedRows == 0) {
return res.status(400).end() // 쿼리가 실행되었지만 변경된 행이 없을 경우 400 에러 응답
} else {
res.status(200).json(results) // 쿼리 실행으로 행이 변경된 경우 성공적으로 응답
}
}
)
})
