데브코스

[6주차 - DAY2] 유효성 검사

미안하다 강림이 좀 늦었다 2024. 4. 2. 15:33

 

 

유효성 검사

사용자가 입력한 값이 유효한 값인지 확인하는 일이다.

예를 들자면 비밀번호가 몇 자리 이상, 연락처에는 문자가 입력될 수 없다 등의 검사를 하는 것이다.

 

Express 유효성 검사 모듈 설치

우리가 유효성 검사를 하는 모듈을 만들어서 쓸 수도 있지만 Express의 유효성 검사를 해주는 외부 모듈이 있기 때문에 이것을 사용해 보자.

 

터미널에서 아래 명령어를 입력하여 유효성 검사를 위한 외부 모듈을 설치해 준다.

npm i express-validator

 

모듈 불러오기

body와 param은 각각 request의 body, param을 의미하고, validationResult는 오류를 받아주는 기능을 한다.

const { body, param, validationResult } = require('express-validator');

 

채널 전체 조회

http 메서드(get, post, put, delete 등)의 첫 번째 파라미터는 미들웨어이다. 콜백 함수를 실행하기 전에 이 미들웨어부터 실행한다.

사용자가 소유하고 있는 모든 채널을 조회하는 기능이므로 사용자 아이디가 요청으로 들어와야 한다. 따라서 사용자 아이디가 비어있지는 않은지, 정수인지에 대해 유효성 검사를 한다. 두 조건 중 하나라도 만족시키지 않으면 withMessage의 파라미터로 전달되는 문자열이 오류 메시지로 설정된다. 유효성 검사에 대한 결과는 콜백 함수에서 확인한다. 에러가 발생했다면 상황에 맞는 응답을 반환한다.

그리고 SQL문 실행 결과 에러가 발생한 경우에도 상태 코드 400을 응답으로 반환한다.

router.route('/')
    .get(body('userId').notEmpty().isInt().withMessage('숫자 입력 필요'),
        (req, res) => { 
            const err = validationResult(req);

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

            const { userId } = req.body;

            const sql = `SELECT * FROM channels WHERE user_id = ?`;
            conn.query(sql, userId,
                function (err, results, fields) {
                    if (err) {
                        console.log(err);
                        return res.status(400).end();
                    }

                    if (results.length) {
                        res.status(200).json(results);
                    } else {
                        notFoundChannel(res);
                    }
                }
            );
        })

조회 성공
userId를 보내지 않은 경우
userId를 문자로 보낸 경우

 

채널 생성

2개 이상의 항목에 대해 유효성 검사를 해야 할 때는 배열 안에 작성한다.

사용자 테이블에 존재하지 않는 사용자 아이디에 대해 삽입을 시도하면 외래키 제약 조건에 위배되기 때문에 SQL문 실행 결과에 에러가 발생한다.

router.route('/')
    .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;

            const sql = `INSERT INTO channels(name, user_id) VALUES(?, ?)`;
            const 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);
                }
            );
        })

채널 생성 성공 - response
채널 생성 성공 - workbench
외래키 제약 조건에 의한 삽입 실패
에러 메시지

 

채널 개별 조회

url로 전달되는 채널 아이디는 비어있으면 안 되며 숫자여야 한다. 비어있거나 문자인 경우 유효성 검사를 통해 걸러내서 상태 코드 400을 응답으로 보낸다.

router.route('/:id')
    .get(param('id').notEmpty().isInt().withMessage('채널 아이디 숫자 필요'),
        (req, res) => {
            const err = validationResult(req);

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

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

            const sql = `SELECT * FROM channels WHERE id = ?`;
            conn.query(sql, id,
                function (err, results, fields) {
                    if (err) {
                        console.log(err);
                        return res.status(400).end();
                    }

                    if (results.length) {
                        res.status(200).json(results);
                    } else {
                        notFoundChannel(res);
                    }
                }
            );
        })

조회 성공
id를 문자로 전달할 경우
id를 전달하지 않을 경우

 

채널 개별 수정

수정하고자 하는 채널의 id와 바꿀 새로운 채널명가 request에 적혀있어야 한다. 채널의 id는 숫자여야 하고, 채널명은 문자열이어야 한다.

수정 SQL문 실행 결과, 영향을 받은 행이 없으면 요청으로 받은 id를 가지는 채널이 없다는 것이므로 채널 정보를 찾지 못했다는 메시지를 보낸다.

router.route('/:id')
    .put(
        [param('id').notEmpty().isInt().withMessage('채널 아이디 숫자 필요'),
        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);
            const { name } = req.body;

            const sql = `UPDATE channels SET name = ? WHERE id = ?`;
            const values = [name, id];
            conn.query(sql, values,
                function (err, results, fields) {
                    if (err) {
                        console.log(err);
                        return res.status(400).end();
                    }

                    if (results.affectedRows) {
                        res.status(200).json(results);
                    } else {
                        notFoundChannel(res);
                    }
                }
            );
        })

수정 성공 - response
수정 성공 - workbench

 

채널 개별 삭제

url로 전달되는 채널의 id는 숫자여야 하며, 비어있으면 안 된다.

삭제 SQL문 실행 결과, 영향을 받은 행이 없으면 요청으로 받은 id를 가지는 채널이 없다는 것이므로 채널 정보를 찾지 못했다는 메시지를 보낸다.

router.route('/:id')
    .delete(param('id').notEmpty().isInt().withMessage('채널 아이디 숫자 필요'),
        (req, res) => {
            const err = validationResult(req);

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

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

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

                    if (results.affectedRows) {
                        res.status(200).json(results);
                    } else {
                        notFoundChannel(res);
                    }
                }
            );
        })

채널 삭제 성공
채널 아이디를 문자로 입력한 경우

 

 

 

검사 미들웨어 분리

모든 라우터에서 유효성 검사에서 에러가 발생했는지 확인하고 있기 때문에 중복되는 코드가 많다. 그리고 유효성 검사에서 에러가 발생했는지 확인하는 부분은 콜백 함수를 실행하기 전에 항상 확인해야 한다. 따라서 공통된 코드를 뽑아내 함수로 만들어준다. 이 항수를 미들웨어로 넣어주는 건 내일 할 예정이다.

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

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