[개발일지] 22년 36주차 - validationPipe

FeRo 페로·2022년 9월 11일
0

validationPipe

지난 주에 이어서 nomad nestJS 강의를 봤다. 이번에는 validationPipe라는 걸 배웠다. pipe라고 하면 관이라고 생각을 하겠지만, 맞다. 관인데, 유효성을 검사할 수 있는 관이다. express에서 미들웨어라고 생각을 하면 된다.

만약 우리가 dto에 없는 데이터를 백엔드로 넘기려고 한다면 에러가 떠야 한다. 하지만 유효성 검사를 하지 않는다면 에러 처리가 되지 않고 바로 DB에 저장한다. 이걸 걸러주는 pipe를 설정하는 것을 배웠다. 오늘은 이 부분에 대해서 공부한 내용을 정리하려고 한다.

파이프 사용법

일단 파이프 처리를 하지 않으면 아래처럼 된다.

// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

// 루트모듈(AppModule)에서 앱을 생성한다.
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

// 바디로 아래와 같이 http://localhost:3000/movies 로 POST 요청을 보내면 그냥 DB에 저장된다!!!
{
    "hacked": "yeah!"
}

일단 두 가지 패키지, class-validator, class-transformer를 설치해야 한다. 이건 nestJS에서도 설치하라고 되어 있는 것이기 때문에 맘 놓고 설치하면 된다.

그리고 app에 파이프를 설정해 주면 기본적인 세팅은 끝이다.

// main.ts
import { ValidationPipe } from '@nestjs/common'; // ValidationPipe import
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

// 루트모듈(AppModule)에서 앱을 생성한다.
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe()) // app에 validationPipe 설정
  await app.listen(3000);
}
bootstrap();

이렇게만 해서는 아무런 효과도 볼 수 없다. 왜냐하면 dto는 그대로이기 때문이다. 이렇게 app에 validationPipe 사용을 위한 세팅을 해주고 나면 dto에 데코레이터를 이용해서 유효성 검사를 해줘야 한다.

// movies/dto/create-movie.dto.ts
import { IsNumber, IsOptional, IsString } from "class-validator";

export class CreateMovieDto{
    @IsString()
    readonly title: string;

    @IsNumber()
    readonly year: number;

    @IsOptional()
    @IsString({each: true})
    readonly genres: string[];
}

이렇게 하면 유효성 검사를 위한 최소한의 세팅은 끝낸 상태이다. 일단 아까와 같은 데이터를 보내보도록 하자.

{
    "hacked": "yeah!"
}

// 이젠 아래와 같은 에러가 출력이 된다.
{
    "statusCode": 400,
    "message": [
        "title must be a string",
        "year must be a number conforming to the specified constraints",
        "each value in genres must be a string"
    ],
    "error": "Bad Request"
}

이건 반쪽 짜리에 불과하다

여기서 몇 가지를 기능을 옵션으로 추가할 수 있다. 예를 들면 저렇게 이상한 걸 보냈을 때 request 자체를 안가도록 막을 수 있다. forbidNonWhiteListed라는 옵션이다. 그 외에도 몇 가지 옵션이 있는데 아래의 코드에서 살펴보도록 하자.

import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    // option을 지정해 줄 수 있다
    new ValidationPipe({
      whitelist: true, 
      forbidNonWhitelisted: true,
      transform: true,
    }),
  );
  await app.listen(3000);
}
bootstrap();

whitelist : 데코레이터(@isString 등)가 없는 필드는 아예 Validator에 도착하지 않는다. dto에 해당 필드가 있어도 데코레이터가 없으면 안된다.
forbidNonWhitelisted : 데코레이터가 없는 필드는 "요청 조차" 가지 않는다. 단, whitelist 옵션이 true여야 사용이 가능하다.
transform : 원래는 유저가 주는 모든 필드는 String으로 도착하게 된다. 그래서 url에서 params를 number 타입으로 사용하기 위해서는 형변환을 해야 했다. 하지만 transform 옵션을 사용하면 자동으로 형변환까지 해준다.

이런 옵션을 하면 에러 메시지에도 몇 가지가 더 추가된다.

{
    "statusCode": 400,
    "message": [
        "property hacked should not exist", // <= 요걸 준다!!
        "title must be a string",
        "year must be a number conforming to the specified constraints",
        "each value in genres must be a string"
    ],
    "error": "Bad Request"
}

일관성 있고 생산성 높은 유효성 검사

귀여워
이때까지 nestJS를 배우면서 가장 마음에 드는 부분이었다. 니꼬 선생님은 nestJS를 '엔터프라이즈급 프레임워크'라고 소개했는데, 그 소개에 걸맞는 기능이었다. 매우 쉽게 일괄적으로 유효성 검사가 가능했기 때문이다. 또한 매우 다양한 유효성 검사를 제공하고 있다. 더 다양한 부분은 nestJS validation options에서 확인할 수 있다.

profile
주먹펴고 일어서서 코딩해

0개의 댓글