본문 바로가기
타입스크립트로 함께하는 웹 풀 사이클 개발(React, Node.js)/TIL

웹 풀사이클 23일차 - 유효성검사 적용, 미들웨어 next

by 슈크림 붕어빵 2023. 12. 24.

유효성검사

valid action

사용자가 입력한 값의 유효성(=타당성)을 확인하는 것

 

ex

  • user_id: body에 값이 있어야한다, 숫자
  • name: body에 값이 있어야한다, 문자열

외부모듈 express-validator

https://www.npmjs.com/package/express-validator

 

express-validator

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

www.npmjs.com

유효성 검사 사용하기

const { body, validationResult } = require("express-validator");
  • validationResult : 문제여부
 const err = validationResult(req);
  • err는 배열 형식으로 넘어온다. err.isEmpty()가 참이라면 문제가 없는 것

 

유효성 검사 err 확인하기

1. 공통함수를 만들어서 사용해주기

function validate(req, res) {
  const err = validationResult(req);
  if (!err.isEmpty()) {
    return res.status(400).json(err.array());
  }

콜백함수의 해당 부분에서 호출해주면 잘 수행된다.

 

2. 미들웨어로 만들어주기, 

const validate = (req, res, next) => {
  const err = validationResult(req);
  if (err.isEmpty()) {
    return next();
  } else {
    return res.status(400).json(err.array());
  }
};

 

리스폰스로 해결하려나보다! 로 인식할 수 있다. 다음 콜백함수로 넘어가야한다는 것을 알려줘야한다.

next를 이용한다.

미들웨어를 콜백함수 호출 전 검사하는 부분에서 불러온다.

ex

.get(
    [
      body("user_id")
        .notEmpty()
        .isInt()
        .withMessage("user_id는 숫자여야합니다."),
      validate,
    ],
    (req, res) => {
      //회원의 채널 조회

      const err = validationResult(req);
      if (!err.isEmpty()) {
        return res.status(400).json(err.array());
      }
      let { user_id } = req.body;

      sql = `SELECT * FROM channels WHERE user_id = ?`;

      conn.query(sql, user_id, function (err, results) {
        if (err) {
          console.log(err);
          return res.status(400).send();
        }
        if (results.length) {
          res.status(200).json(results);
        } else {
          res.status(404).json({
            message: "해당 id의 회원의 채널이 없습니다.",
          });
        }
      });
    }
  )

 

참고 1 

  • .isEmail() : email인지 검사
  • .isStrongPassword()

 

참고 - 미들웨어란?

  • 미들웨어 함수는 req객체, res객체, 그리고 어플리케이션 요청-응답 사이클 도중 그 다음의 미들웨어 함수에 대한 엑세스 권한을 갖는 함수이다. 즉, 요청 전에 처리할 수 있는 어떤 것이다.
  • next를 통해 미들웨어는 순차적으로 처리된다.
  • 따라서 유효성 검사를 할 때, 미들웨어인 validate에서 next를 해주지 않으면 err.isEmpty()인 경우, 즉 유효성 검사를 통과한 경우에는 핸들러로 넘어가지 않는다.
const validate = (req, res, next) => {
  const err = validationResult(req);
  if (err.isEmpty()) {

  } else {
    return res.status(400).json(err.array());
  }
};

 

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
  .route("/users")
  .get(
    [
      body("email").notEmpty().isEmail().withMessage("user_id 확인 필요"),
      validate,
    ],
    (req, res) => {
      //회원 개별 조회
      let { email } = req.body;
      sql = `SELECT * FROM users WHERE email = ?`;

      conn.query(sql, email, function (err, results) {
        if (err) {
          console.log(err);
          return res.status(400).send();
        }
        if (results.length) {
          res.status(200).json(results);
        } else {
          //해당id의 회원이 없는 경우
          res.status(404).json(results);
        }
      });
    }
  )
  .delete(
    [
      body("email").notEmpty().isEmail().withMessage("email 확인 필요"),
      validate,
    ],
    (req, res) => {
      //회원 탈퇴
      let { email } = req.body;
      let sql = `DELETE FROM users WHERE email = ?`;
      conn.query(sql, email, function (err, results) {
        if (err) {
          console.log(err);
          return res.status(400).send();
        }
        if (results.affectedRows) {
          res.status(200).json(results);
        } else {
          //해당 id의 회원이 없는 경우
          res.status(404).json(results);
        }
      });
    }
  );
///로그인
router.post(
  "/login",
  [
    body("email").notEmpty().isEmail().withMessage("email must be a string."),
    body("pw").notEmpty().isString().withMessage("pw must be a string"),
    validate,
  ],
  (req, res) => {
    let { email, pw } = req.body;

    let sql = `SELECT * FROM users WHERE email = ? `;

    conn.query(sql, email, function (err, results) {
      if (err) {
        console.log(err);
        return res.status(400).send();
      }

      let loginUser = results[0];
      if (loginUser && loginUser.pw == pw) {
        res.status(200).json({ message: `${loginUser.name}님 환영합니다.` });
      } else {
        res.status(404).json({
          message: "id 및 pw를 확인해주세요.",
        });
      }
    });
  }
);

//회원가입
router.post(
  "/join",
  [
    body("email").notEmpty().isEmail().withMessage("email 확인 필요"),
    body("pw").notEmpty().isString().withMessage("pw확인 필요"),
    body("name").notEmpty().isString().withMessage("name확인 필요"),
    validate,
  ],
  (req, res) => {
    let { email, pw, name } = req.body;
    let sql = `INSERT INTO users (email,name,pw) VALUES (?,?,?)`;
    let values = [email, name, pw];
    conn.query(sql, values, function (err, results) {
      if (err) {
        console.log(err);
        return res.status(400).send();
      }
      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("/")
  .post(
    [
      body("user_id")
        .notEmpty()
        .isInt()
        .withMessage("user_id must be a number."),
      body("channelName")
        .notEmpty()
        .isString()
        .withMessage("name must be a letter"),
      body("description")
        .notEmpty()
        .withMessage("Channel description is required."),
      validate,
    ],
    (req, res) => {
      // 채널 생성
      const { user_id, channelName, description } = req.body; // 수정: 변수를 선언해야 합니다.

      let sql =
        "INSERT INTO channels (channelName, description, user_id) VALUES (?, ?, ?)";
      const values = [channelName, description, user_id]; // 수정: 변수명이 정확해야 합니다.

      conn.query(sql, values, function (err, results) {
        if (!err) {
          res.status(201).json(results);
        } else {
          //채널을 생성하지 못함.
          return res.status(400).send();
        }
      });
    }
  )

  .get(
    [
      body("user_id")
        .notEmpty()
        .isInt()
        .withMessage("user_id는 숫자여야합니다."),
      validate,
    ],
    (req, res) => {
      //회원의 채널 조회
      let { user_id } = req.body;
      sql = `SELECT * FROM channels WHERE user_id = ?`;

      conn.query(sql, user_id, function (err, results) {
        if (err) {
          console.log(err);
          return res.status(400).send();
        }
        if (results.length) {
          res.status(200).json(results);
        } else {
          //해당id의 회원의 채널이 없는 경우
          res.status(404).json(results);
        }
      });
    }
  );

router
  .route("/:id")
  .put(
    [
      param("id").notEmpty().withMessage("채널 id 필요"),
      body("channelName").notEmpty().isString().withMessage("채널명 필요"),
      body("description").notEmpty().isString().withMessage("채널 설명 필요"),
      validate,
    ],
    (req, res) => {
      //채널 수정

      let { id } = req.params;
      id = parseInt(id);
      let { channelName, description } = req.body;

      let sql = `UPDATE channels SET channelName=?, description=? WHERE id=?`;
      let values = [channelName, description, id];

      conn.query(sql, values, function (err, results) {
        if (err) {
          return res.status(400).json({ err });
        }
        if (results.affectedRows) {
          res.status(200).json(results);
        } else {
          //해당 id의 채널이 없는 경우
          res.status(400).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).send();
        }
        if (results.affectedRows) {
          res.status(200).json(results);
        } else {
          //해당 id의 채널이 없는 경우
          res.status(404).json(results);
        }
      });
    }
  )
  .get(
    [param("id").notEmpty().withMessage("채널 id 필요").notEmpty(), validate],
    (req, res) => {
      //개별 채널 조회
      let { id } = req.params;

      sql = `SELECT * FROM channels WHERE id = ?`;

      conn.query(sql, id, function (err, results) {
        if (err) {
          console.log(err);
          return res.status(400).send();
        }

        if (results.length) {
          res.status(200).json(results);
        } else {
          //해당 id의 채널이 없는 경우
          res.status(404).json(results);
        }
      });
    }
  );

module.exports = router;

app.js

const express = require("express");
const app = express();

app.listen(3000);

const userRouter = require("./routes/users");
const channelRouter = require("./routes/channels");

app.use("/", userRouter);
app.use("/channels", channelRouter);