<데브코스> 2026-02-04 TIL 유효성 검사(express-validator), SQL오류 처리, next

유효성 검사 validation 

사용자가 입력한 값에 대한 유효성 (= 타당성)을 검사하는 것

 

userId : 값이 있어야, 숫자

body('userId').notEmpty().isInt().withMessage('숫자 입력하자!')

채널 name : 값이 있어야, 문자 ->

사람 name : 숫자 X, 문자열, 2자 이상 ...

 

외부 모듈 express-validator

https://express-validator.github.io/docs/category/guides

 

express-validator guides | express-validator

📄️ Customizing express-validator If the application you're building is anything but a very simple one, you'll need validators,

express-validator.github.io

npm install express-validator

 


채널 개별 생성

유효성검사 전 채널 개별 생성 

    .post((req, res)=>{
        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.status(201).json(results)
                }
            )   
        } else {
            res.status(404).json({
                message : "요청 값을 제대로 보내주세요."
            })
        }
    }) // 채널 개별 생성 = db에 저장

수정 후 

const {body, validationResult} = require('express-validator')
    .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())
            }

            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.status(201).json(results)
                }
            ) 
    }) // 채널 개별 생성 = db에 저장


SQL 에러 처리하기

없은 userId(FK)을 입력했을 경우, 오류 처리가 되지않는 문제를 해결해보자.

            conn.query(sql,values,
                function(err, results){
                    if(err){
                        console.log(err)
                        return res.status(400).end();
                    }
                    res.status(201).json(results)
                }
            )


마저 정리해보자 .. 

 

채널 전체 조회

    .get(
        [body('userId').notEmpty().isInt().withMessage('숫자 입력 필요')]
        ,(req, res)=>{
            const arr = validationResult(req)

            if(!err.isEmpty()){
                return res.status(400).json(err.array())
            }

            var {userId} = req.body

            let sql = `SELECT * FROM channels WHERE user_id = ?`
            conn.query(sql, userId,
                function(err, results){
                    if(results.length){
                        res.status(200).json(results)   
                    }
                    else {
                        notFoundChannel(res)
                    }
                }
            )   
    }) // 채널 전체 조회

results.length도 해줄 수 있지 않을까?

-> 유효성 검사는 사용자의 요청에 대한 내용만 처리! 이 부분은 if로 남기는게 맞음. 


채널 개별 조회

param 메소드 추가

const {body, param, validationResult} = require('express-validator')
router
    .route('/:id')
    .get(
        param('id').notEmpty().withMessage('채널id 필요')
        ,(req, res)=>{
            const err = validationResult(req)

            if(!err.isEmpty()){
                return res.status(400).json(err.array())
            }

            let {id} = req.params
            id = parseInt(id)

            let sql = `SELECT * FROM channels WHERE id = ?`
            conn.query(sql, id,
                function(err, results){
                    if(err){
                        console.log(err)
                        return res.status(400).end()
                    }
                    
                    if(results.length){
                        res.status(200).json(results)   
                    }
                    else {
                        notFoundChannel(res)
                    }
                }
            )
    })

 

채널 개별 수정 

    .put(
        [param('id').notEmpty().withMessage('채널id 필요'),
         body('name').notEmpty().isString().withMessage('채널명 오류')]
        ,(req, res)=>{
            const err = validationResult(req)
            
            if (!err.isEmpty()){
                return res.status(400).json(err.array())
            }

            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)
                        return res.status(400).end()
                    }

                    if(results.affectedRows == 0){
                        return res.status(400).end()
                    } else{
                        res.status(200).json(results)
                    }
                }
            )
    }) // 채널 개별 수정

 

{
    "fieldCount": 0,
    "affectedRows": 0,
    "insertId": 0,
    "info": "",
    "serverStatus": 2,
    "warningStatus": 0,
    "changedRows": 0
}

put은 성공, 혹은 실패 상관없이 무조건 results 값을 실행결과로 가져오기때문에

실행결과에서 affectedRows로 검사해줄 수 있음.

 


 

채널 개별 삭제 

    .delete(
        [param('id').notEmpty().withMessage('채널id 필요')]
        ,(req, res)=>{
            const err = validationResult(req)

            if(!err.isEmpty()){
                return res.status(400).json(err.array())
            }
            let {id} = req.params
            id = parseInt(id)

            let sql = `DELETE FROM channels WHERE id = ?`
            conn.query(sql, id, 
                function(err, results) {
                    if(err){
                        console.log(err)
                        return res.status(400).end()
                    }

                    if(results.affectedRows == 0){
                        return res.status(400).end()
                    } else {
                        res.status(200).json(results)
                    }
                    
                }
            );
    }) // 채널 개별 삭제

Delete도 PUT과 마찬가지로 성공/실패 상관없이 실행결과 코드를 가져옴 

 


계속 반복되는 코드를 하나로 뽑아내자!

검사 미들웨어 분리 

 

함수일 때에는?

선언부

function validate(req, res) {
    const err = validationResult(req)

    if(!err.isEmpty()){
        return res.status(400).json(err.array())
    }
}

 

호출부 

    .get(
        [body('userId').notEmpty().isInt().withMessage('숫자 입력 필요')]
        ,(req, res)=>{

            validate(req, res)

-> req, res를 그대로 담아주는 데 손수 매개변수로 넣어줘야하는 불편함이 있음. 

 

모듈로 담아주자!

선언부

const validate = (req, res) =>  {
    const err = validationResult(req)

    if(!err.isEmpty()){
        return res.status(400).json(err.array())
    }
}

호출부

router
    .route('/')
    .get(
        [ //콜백함수가 실행되기 전에 할 일을 담아주는 곳
            body('userId').notEmpty().isInt().withMessage('숫자 입력 필요'),
            validate
        ]
        ,(req, res)=>{ ...

❗️여기서 validate는 실행되지만, if(!err.isEmpty())에서 걸리지 않을 시 .get의 이후 코드가 실행되지 않음!

 

next

const validate = (req, res, next) =>  {
    const err = validationResult(req)

    if(!err.isEmpty()){
        return res.status(400).json(err.array())
    } else {
        return next(); // 다음 할 일 (미들 웨어, 함수)
    }
}

이렇게 하면 콜백 함수로 넘어감


전체 코드 및 확인 완료

 

users.js

const express = require('express')
const router = express.Router()
const conn = require('../mariadb')
const {body, param, validationResult} = require('express-validator')


router.use(express.json())

const validate = (req, res, next) =>  {
    const err = validationResult(req)

    if(err.isEmpty()){
        return 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, result){
                if(err){
                    console.log(err)
                    return res.status(400).end()
                }

                var loginUser = result[0]; //result 없어도 오류 안남!
                    
                if(loginUser && loginUser.password == password){
                    res.status(200).json({
                        message : `${loginUser.name}님 로그인 되었습니다.`  
                    })
                }
                else {
                    res.status(404).json({
                        message : "이메일 또는 비밀번호가 틀렸습니다."
                    })   
                }
            }
        )
})

router.post(
    '/join', 
    [
        body('email').notEmpty().isEmail().withMessage('이메일 확인 필요'),
        body('name').notEmpty().isString().withMessage('이름 확인 필요'),
        body('password').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){
                    console.log(err)
                    return res.status(400).end()
                }

                res.status(201).json(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, fields) {
                        if(err){
                            console.log(err)
                            return res.status(400).end()
                        }

                        res.status(200).json(results)
                }
            );  
    }) 
    .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, fields){
                    if(err){
                        console.log(err)
                        return res.status(400).end()
                    }

                    if(results.affectedRows == 0){
                        return res.status(400).end()
                    } else {
                        res.status(200).json(results)
                    }                
                }
            ) 
    }) 


module.exports = router

 

channels.js

const express = require('express');
const router = express.Router()
const conn = require('../mariadb')
const {body, param, validationResult} = require('express-validator')

router.use(express.json()) 

const validate = (req, res, next) =>  {
    const err = validationResult(req)

    if(err.isEmpty()){
        return next(); // 다음 할 일 (미들 웨어, 함수)
    } else {
        return res.status(400).json(err.array())
    }
}

router
    .route('/')
    .get(
        [
            body('userId').notEmpty().isInt().withMessage('숫자 입력 필요'),
            validate
        ]
        ,(req, res, next)=>{
            var {userId} = req.body

            let sql = `SELECT * FROM channels WHERE user_id = ?`
            conn.query(sql, userId,
                function(err, results){
                    if(err) {
                        console.log(err)
                        return res.status(400).end()
                    }
                    if(results.length){ 
                        res.status(200).json(results)   
                    }
                    else {
                        return res.status(400).end()
                    }
                }
            )   
    }) // 채널 전체 조회

    .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){
                        console.log(err)
                        return res.status(400).end();
                    }
                    res.status(201).json(results)
                }
            ) 
    }) // 채널 개별 생성 = db에 저장

router
    .route('/:id')
    .get(
        [
            param('id').notEmpty().withMessage('채널id 필요'),
            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){
                        console.log(err)
                        return res.status(400).end()
                    }

                    if(results.length){
                        res.status(200).json(results)   
                    }
                    else {
                        return res.status(400).end()
                    }
                }
            )
    }) 
    
    .put(
        [
            param('id').notEmpty().withMessage('채널id 필요'),
            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){
                        console.log(err)
                        return res.status(400).end()
                    }

                    if(results.affectedRows == 0){
                        return res.status(400).end()
                    } else{
                        res.status(200).json(results)
                    }
                }
            )
    }) // 채널 개별 수정
    .delete(
        [
            param('id').notEmpty().withMessage('채널id 필요'),
            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){
                        console.log(err)
                        return res.status(400).end()
                    }

                    if(results.affectedRows == 0){
                        return res.status(400).end()
                    } else {
                        res.status(200).json(results)
                    }
                    
                }
            );
    }) // 채널 개별 삭제



module.exports = router