Nodejs – 유효성 검사

Validation 이란?

클라이언트가 서버에게 특정한 요청이나 리소스를 만들기 위해서 body로 보내는 데이터들을 유효한지 / 정확한 데이터를 보내고 있는지 확인하는 것을 말한다.

스키마가 확정된 데이터베이스에서는 어떤 특정한 필드에 어떤 데이터 타입인지 다 정의되어있기때문에, validation 체크를 데이터베이스에서 해줄 수 있다. 하지만 데이터베이스에서 유효성 검사를 할때까지 아무런 유효성 검사를 하지 않는 것은 최악이므로, 서버에서 유효성 검사를 한다.

함수를 만들때도 함수에서 필요한 인자가 적절한지 적절하지 않은지 확인해서 early return 하는 것처럼, 서버에서 동작하는 모든 동작들은 비용이 발생하기 때문에 서버에서도 유효성 검사를 빨리 하면 할수록 좋다.

또한, 서버에서 유효성 검사를 한다고해도 클라이언트가 요청 > 서버가 응답을 해야하는 리소스 낭비가 있으므로, 클라이언트측에서도 가능한 유효성 검사를 하는 것이 좋다.


Validation 라이브러리

위와 같은 POST json 으로 User 를 새로 생성한다고 가정해보자. 요청을 받는 서버는 저 request 에 대해 모든 유효성 검사를 수동으로 해줘야한다. (email 형식이 맞는지 / 특정한 필드는 필수 필드인데도 불구하고 없는 경우 등..)

그럴때 유용하게 사용할 수 있는 라이브러리가 ‘express-validator’ 이다.


How to use ?

요청 핸들러는 배열 형태로 등록이 가능하다.그래서 다음과 같이 처리해줄 수 있다.

import express from 'express';
import {body, validationResult} from 'express-validator';

const app = express();
app.use(express.json());

app.post('/users', body('name').isLength({min:2, max:10}), (req,res,next) => {
    const errors = validationResult(req); //req 를 전달하여 등록한 'body('name').isLength({min:2, max:10})' 유효성 검사에 에러가 있는지 확인.
    if(!errors.isEmpty()){
        res.status(400).send({message: errors.array() });
    }
    console.log(req.body);
    res.sendStatus(201);
});

app.listen(8080);

핸들러 body(‘name’).isLength({min:2, max:10}) 를 등록하고, 콜백함수에서 들어온 request를 전달하여 유효성 검사에 에러가 있는지 확인하면 된다. (name의 길이는 2~10자)

다음과 같이 유효성 검사가 잘 동작하는 것을 알 수 있다.

여기서 message 내의 기본 값인 ‘Invalid value’ 가아닌 조금 더 의미있는 내용을 전달하고 싶다면, 핸들러 추가시 .withMessage()를 추가해주면 된다.

app.post(
  '/users',
  body('name')
    .isLength({ min: 2, max: 10 })
    .withMessage('2~10 글자 사이로 입력해주세요!'),
  (req, res, next) => {
    const errors = validationResult(req); //req 를 전달하여 등록한 'body('name').isLength({min:2, max:10})' 유효성 검사에 에러가 있는지 확인.
    if (!errors.isEmpty()) {
      res.status(400).send({ message: errors.array() });
    }
    res.sendStatus(201);
  },
);

바뀐 메세지도 잘 동작하는 것을 알 수 있다.


단순히 문자열 길이만 검사하는게 아니라, validator 라이브러리 내에 많은 유용한 api 가 있고, 그 api들을 체이닝하는 것도 가능하다 👍

app.post(
  '/users',
  body('name')
    .notEmpty() // 비어있다면,
    .withMessage('이름을 입력해주세요! ')
    .isLength({ min: 2, max: 10 })
    .withMessage('2~10 글자 사이로 입력해주세요!'),
  body('age').notEmpty().isInt().withMessage('숫자를 입력해주세요!'), // age 가 비어있거나, 숫자가 아니거나
  (req, res, next) => {
    const errors = validationResult(req); //req 를 전달하여 등록한 'body('name').isLength({min:2, max:10})' 유효성 검사에 에러가 있는지 확인.
    if (!errors.isEmpty()) {
      res.status(400).send({ message: errors.array() });
    }
    console.log(req.body);
    res.sendStatus(201);
  },
);

param으로 넘어온 값에 대해서도 유효성 검사가 가능하다.

import express from 'express';
import { body, param, validationResult } from 'express-validator';

const app = express();

app.use(express.json());

app.post(
  '/users',
  [
    body('name')
      .isLength({ min: 2, max: 10 })
      .withMessage('2~10 글자 사이로 입력해주세요!'),
    body('age').notEmpty().isInt().withMessage('숫자를 입력해주세요!'),
    body('email').isEmail().withMessage('유효한 이메일 형식을 입력하세요!'),
    body('job.name').notEmpty(),
  ],
  (req, res, next) => {
    const errors = validationResult(req); //req 를 전달하여 등록한 'body('name').isLength({min:2, max:10})' 유효성 검사에 에러가 있는지 확인.
    if (!errors.isEmpty()) {
      res.status(400).send({ message: errors.array() });
    }
    console.log(req.body);
    res.sendStatus(201);
  },
);

app.get(
  '/:email',
  param('email')
    .isEmail() // 이메일 형식이 유효하지 않다면,
    .withMessage('유효한 이메일 형식을 입력하세요!'),
  (req, res, next) => {
    const errors = validationResult(req); //req 를 전달하여 등록한 'body('name').isLength({min:2, max:10})' 유효성 검사에 에러가 있는지 확인.
    if (!errors.isEmpty()) {
      res.status(400).send({ message: errors.array() });
    }
    res.send('Email');
  },
);

app.listen(8080);

Validation 코드 최종 리팩토링

import express from 'express';
import { body, param, validationResult } from 'express-validator';

const app = express();

app.use(express.json());

const validate = (req, res, next) => {
  const errors = validationResult(req);
  if (errors.isEmpty()) {
    return next();
  }
  res.status(400).send({ message: errors.array()[0].msg });
}; // validate 는 계속 사용하므로, 따로 정의

app.post(
  '/users',
  [
    body('name')
      .isLength({ min: 2, max: 10 })
      .withMessage('2~10 글자 사이로 입력해주세요!'),
    body('age').notEmpty().isInt().withMessage('숫자를 입력해주세요!'),
    body('email').isEmail().withMessage('유효한 이메일 형식을 입력하세요!'),
    body('job.name').notEmpty(),
    validate, // 배열로 전달
  ],
  (req, res, next) => {
    console.log(req.body);
    res.sendStatus(201);
  },
);

app.get(
  '/:email',
  [
    param('email')
      .isEmail() // 이메일 형식이 유효하지 않다면,
      .withMessage('유효한 이메일 형식을 입력하세요!'),
    validate, // 배열로 전달
  ],
  (req, res, next) => {
    res.send('Email Ok :) ');
  },
);

app.listen(8080);

추가 더 자세한 정보는 아래 공식 사이트를 참고하자.

  • https://express-validator.github.io/docs/