웹 풀 사이클 데브코스

[TIL] Day23 - 유효성 검사 express-validator, 미들웨어 배열

닿다라다나닷 2024. 5. 13. 17:00

유효성 검사 validation

사용자가 입력한 값(에 따른 결과값) 타당성,유효성을 확인하는 것

- userId : 값이 있어야 한다, 숫자

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

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

 

express-validator

일일이 검사할 수 있지만, 외부모듈을 사용해 검사한다!

 

express-validator

Express middleware for the validator module.. Latest version: 7.0.1, last published: a year ago. Start using express-validator in your project by running `npm i express-validator`. There are 10662 other projects in the npm registry using express-validator.

www.npmjs.com

 

 

 

express-validator | express-validator

Overview

express-validator.github.io

req.body, cokkies, headers, params, query의 값에 대해 서포트 할 수 있다,

 

userId 숫자 express-validator

req에서 직접 꺼내서 쓰지 말고, 콜백함수 실행 전에 모듈핸들러로 유효성 검사를 한다

 //채널 개별 생성
  .post(body("userId").notEmpty().isInt().withMessage('숫자 입력하자!'), (req, res) => {
    const err = validationResult(req);
    if (!err.isEmpty()) {
      console.log(err.array());
    }
//userId 문자열 입력시
[
  {
    type: 'field',
    value: 'abc',
    msg: '숫자 입력하자!',
    path: 'userId',
    location: 'body'
  }
]

 

name 문자

여러개 유효성 처리 - 배열에 한번에 할당한다!

//채널 개별 생성
  .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);
      });
    }
  );

if 문 없어도, 유효성 처리로 컨트롤 가능!!

 

userId 없을때 insert 에러 처리

`throw err`

sql insert 작업 시 없는 값(userId) 참조 무결성을 어길경우 404 err 발생시킨다.

conn.query(sql, values, function (err, results) {
        if (err) {
          //console.log(err);
          return res.status(404).end();
        }
        res.status(201).json(results);
      });
Error: Cannot add or update a child row: a foreign key constraint fails (`Youtube`.`channels`, CONSTRAINT `user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION)
    at Packet.asError (c:\Users\dh010\Desktop\DevCourse\스프린트2\youtube-demo\node_modules\mysql2\lib\packets\packet.js:728:17)
    at Query.execute (c:\Users\dh010\Desktop\DevCourse\스프린트2\youtube-demo\node_modules\mysql2\lib\commands\command.js:29:26)
    at Connection.handlePacket (c:\Users\dh010\Desktop\DevCourse\스프린트2\youtube-demo\node_modules\mysql2\lib\connection.js:481:34)
    at PacketParser.onPacket (c:\Users\dh010\Desktop\DevCourse\스프린트2\youtube-demo\node_modules\mysql2\lib\connection.js:97:12)
    at PacketParser.executeStart (c:\Users\dh010\Desktop\DevCourse\스프린트2\youtube-demo\node_modules\mysql2\lib\packet_parser.js:75:16)
    at Socket.<anonymous> (c:\Users\dh010\Desktop\DevCourse\스프린트2\youtube-demo\node_modules\mysql2\lib\connection.js:104:25)
    at Socket.emit (node:events:514:28)
    at addChunk (node:internal/streams/readable:545:12)
    at readableAddChunkPushByteMode (node:internal/streams/readable:495:3)
    at Readable.push (node:internal/streams/readable:375:5) {
  code: 'ER_NO_REFERENCED_ROW_2',
  errno: 1452,
  sqlState: '23000',
  sqlMessage: 'Cannot add or update a child row: a foreign key constraint fails (`Youtube`.`channels`, CONSTRAINT `user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION)',
  sql: "INSERT INTO channels (name, user_id) VALUES ('댄싱무지',100)"
}

 

channels 리팩토링

 

params에서 꺼내는 법? `const { body, param, validationResult } = require("express-validator");`

`if (!err.isEmpty()` 조건이 반복되는 것을 볼 수 있다. > 함수로 빼주기

 

UPDATE 채널 개별 수정

.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(404).end();
        }
        if (results.affectedRows == 0) return res.status(404).end();
        else res.status(200).json(results);
      });
    }
  )

404 bad req

id값 1 입력해서 제대로 실행시켰을때?

update됨!

 

id 값을 20으로 해도 sql문이 실행되는 코드 예외처리 해주어야 한다.

if (results.affectedRows == 0) return res.status(404).end();
        else res.status(200).json(results);

 

DELETE 채널 개별 삭제

.delete(
    param("id").notEmpty().isInt().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(404).end();
        }
        if (results.affectedRows == 0) return res.status(404).end();
        else res.status(200).json(results);
      });
    }
  );

지금까지 작성한 코드의 집합체! > 함수로 빼낼 수 있을듯?

 

.status 코드

설계 단계에서 명확하게 규칙을 정하고 넘어가야 하는 부분!

 

검사 미들웨어 분리

err.isEmpty()를 함수로 분리한다. > 파일 내부에서 모듈화해서 미들웨어처럼 만드는 방법 `변수에 함수 담기`

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

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

 

get()괄호 첫번째 인자는, 콜백함수를 호출하기 전에 실행해야할 코드를 모아둠

핸들러 : req 요청이 왔을때, 호출되는 메소드

router
  .route("/")
  //채널 전체 조회
  .get(
    [body("userId").notEmpty().isInt().withMessage("숫자 입력 필요"), validate],
    (req, res) => {

그런데! 미들웨어로 핸들러 인자를 넘겨주면, 뒤의 콜백함수가 실행되지 않는다.

...해결은 다음글...

 

☑️ 배운 점

if 문으로 예외처리 대신, 유효성 처리로 조건을 할당할 수 있다!

오늘 팀원들과 WIL 회의에서 if 문에 대한 생각을 공유했는데, 이번 강의에서 유효성 검사로 조건을 처리할 수 있는 방법을 배워서 좋았다. 각 요청마다 거의 같은 예외처리를 해주어야 했기에 미들웨어 모듈로 추출할 수 있었다.

body와 param 값을 핸들러 인자 위치에, 배열로 여러개의 값을 담아서, 콜백함수 실행전에, 미리 실행할 수 있다는 점.

미들웨어 배열

미들웨어 또는 핸들러 함수를 사용하기 전에 요청을 처리하고 유효성을 검사하기 위한 미들웨어 배열이다.

배열은 Express.js에서 유효성 검사를 수행하는데 사용되는 일반적인 패턴이다. 여기서 각 요소는 미들웨어 함수를 나타낸다.

유효성 검사 Express-validator에 유용하고, 다양한 유효성 검사를 몰아서 할 수 있어 미들웨어 체인을 사용한다. 또한 로직 커스터마이징이 가능하기 때문에 생산성과 가독성을 높일 수 있다.

//예시 코드
const express = require('express');
const { body, validationResult } = require('express-validator');

const app = express();

// POST 요청을 처리하는 라우트 핸들러
app.post('/user', [
    body("userId").notEmpty().isInt().withMessage("숫자 입력 필요"),
    body("name").notEmpty().isString().withMessage("문자 입력 필요"),
    validateInput,
], (req, res) => {
    // 유효성 검사가 통과되었을 때 실행되는 코드
    res.send('Validation passed');
});

// 커스텀 미들웨어 함수 - 유효성 검사 결과 처리
function validateInput(req, res, next) {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
    }
    next(); // 다음 미들웨어로 전달
}

app.listen(3000, () => console.log('Server started on port 3000'));