class-validator를 사용한 타입스크립트(Typescript) Validation

Typescript Data Validation

2019/12/17

참고자료 : typestack/class-validator

Express를 사용하는 Typescript 환경에서 class-validator를 사용하여 Request DataValidation하는 과정을 다루고 있습니다.


1. Data Validation

Data Validation(데이터 유효성 검사)이란,
다루는 데이터가 올바른 Format을 가지는지 확인하는 과정입니다.

2. class-validator

typestack/class-validator
데코레이터 및 비 데코레이터 기반 검증을 사용할 수 있습니다.
내부적으로 validator.js를 사용하여 유효성 검사를 수행합니다.
class-validator는 브라우저 및 node.js 플랫폼 모두에서 작동합니다.

npm install class-validator --save

3. Format

정상적인 데이터는 아래의 Format을 충족해야 한다고 설정해봅시다.

title      : string (minLength=10, maxLength=20)
text       : string (hello 를 포함해야함)
rating     : number (minValue=0, maxValue=10)
email      : string (Email)
site       : string (FQDN)
userId     : string (UUID ver.4)
createDate : date

4. Sample Data

정상적인 Request Data Sample은 아래와 같습니다.

{
  "title": "My Best Title",
  "text": "this is a great post about hello world!!",
  "rating": 8,
  "email": "siner308@gmail.com",
  "site": "siner308.github.io",
  "userId": "1fb0e256-ca80-4e2d-9528-ac63bedaede8",
  "createDate": "2019-12-17",
}

5. Validator Class

이제 class-validator에서 사용할 Class를 작성합니다.
도입 초기에 Typeorm의 Entity와 헷갈리는 바람에 고생을 했습니다...

import { IsUUID, Contains, IsInt, Length, IsEmail, IsFQDN,
  IsDate, Min, Max } from "class-validator";

export class Post {

  @Length(10, 20)
  public title: string;

  @Contains('hello')
  public text: string;

  @IsInt()
  @Min(0)
  @Max(10)
  public rating: number;

  @IsEmail()
  public email: string;

  @IsFQDN()
  public site: string;

  @IsUUID('4')
  public userId: string;

  @IsDate()
  public createDate: Date;

}

6. Handling

데이터 처리 로직은 아래처럼 구현합니다.

// postController.ts

import { validate } from 'class-validator';
import { Request, Response, NextFunction, Router } from 'express';
import { PostService, IPost } from '../services/PostService';

const postRouter: Router = Router();

postRouter.post('/post', async (req: Request, res: Response, next: NextFunction) => {
  const title: string = req.body.title;
  const text: string = req.body.text;
  const rating: number = req.body.rating;
  const email: string = req.body.email;
  const site: string = req.body.site;
  const userId: string = req.body.userId;
  const createDate: Date = new Date(req.body.createDate);

  // validation
  const post: Post = new Post();
  post.title = title;
  post.text = text;
  post.rating = rating;
  post.email = email;
  post.site = site;
  post.userId = userId;
  post.createDate = createDate;

  const errors: ValidationError[] = await validate(post); // errors is an array of validation errors
  if (errors.length > 0) {
    console.log('validation failed. errors: ', errors);
    return next(errors);
  } else {
    console.log('validation succeed');
  }
    
  // continuous logic
  const postService: PostService = new PostService();
  try {
    const createdPost: IPost = await postService.createPost(title, text, rating, email, site, userId, createdDate);
    res.status(200).json({
      msg: 'created',
      alertPanel: createdPost,
    });
  } catch (err) {
      res.status(400).json({
        msg: err.message,
      });
  }
})

export default postRouter;

7. Valid Case

image

validation succeed

정상 데이터의 경우 아래와 같이 성공 로그가 출력됩니다.

7. Invalid Case

image

image

image

유효하지 않은 데이터가 들어왔을 경우, 콘솔에서 Validation에 통과되지 않은 원인을 확인할 수 있습니다.