Express 를 사용하면 body data 혹은 param 에 유효하지 않은 값을 받을 것을 대비해서 일일이 유효성 검사를 해줘야 하는 불편함이 존재한다. 하지만 NestJS 는 아주 쉽게 유효성 검사를 할 수있게 도와주며 심지어 내가 원하는 데이터의 타입이 아닌 데이터가 들어오는 것도 방지할 수 있다. 간단하게 맛보기로 어떻게 사용할 수 있는지 알아보자.

 

$ npm i class-validator class-transformer

 

위의 라이브러리들을 설치 한 후 src/users/dto 경로에 create-user.dto.ts 생성.

 

// create-user.dto.ts

import { IsString } from 'class-validator';

export class CreateUserDto {
  @IsString()
  name: string;

  @IsString()
  address: string;

  @IsString({ each: true })
  hobbies: string[];
}

 

소스만 봐도 무엇을 원하는지 알 것 같다. 그리고 main.ts 에 아래와 같이 app 객체에 useGlobalPipes 를 추가. express 에서 middleware 를 사용하는 것이라고 생각하면 된다.

 

// main.ts

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(
    new ValidationPipe({
      whitelist: true, // decroator 가 없는 property 는 걸러서 받음.
      forbidNonWhitelisted: true, // whiltelist 에 해당하는 데이터를 보내면 에러를 발생시킴.
      transform: true, // param 을 내가 원하는 타입으로 변경해줌.
    }),
  );
  await app.listen(3000);
}
bootstrap();

 

그리고 Controller 와 Service 에 타입을 추가해 주자.

 

// users.controller.ts

import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  getAllUser() {
    return this.usersService.getAllUser();
  }

  @Get(':id')
  getUserById(@Param('id') userId: number) {
    return this.usersService.getUserById(userId);
  }

  @Delete(':id')
  deleteByUserId(@Param('id') userId: number) {
    return this.usersService.deleteByUserId(userId);
  }

  // userInfo 타입을 DTO 로 지정!
  @Post()
  createUser(@Body() userInfo: CreateUserDto) {
    console.log('userInfo :', userInfo);
    return this.usersService.createUser(userInfo);
  }
}
// users.service.ts

import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { User } from './entity/users.interface';

@Injectable()
export class UsersService {
  private users: User[] = [];

  getAllUser(): User[] {
    return this.users;
  }

  getUserById(userId: number): User {
    return this.users.find((user) => user.id === userId);
  }

  deleteByUserId(userId: number): boolean {
    this.users = this.users.filter((user) => user.id !== userId);
    return true;
  }

  // userInfo 에 DTO 타입 지정!
  createUser(userInfo: CreateUserDto) {
    const newUser = {
      id: this.users.length + 1,
      ...userInfo,
    };
    this.users = [...this.users, newUser];
    return '새로운 유저 생성!';
  }
}

 

변경 된 코드는 새로운 User 를 생성하는 부분에 타입을 지정해 준 것이 전부.

 

DTO 에 정의 되지않은 데이터를 보냈을 때

테스트 결과는 위와 같다. 참고로 forbidNonWhitelisted 를 사용하지 않고 whitelist 만 사용할 경우 위와 같이 데이터를 보내면 올바르게 전송되는데 대신 DTO 에 정의 되지 않은 nao 는 제외되어 들어오게 된다. forbidNonWhitelisted 는 애초에 DTO 에 정의 되지 않은 속성값이 존재 할 경우 바로 에러를 발생시킨다.

 

그리고 localhost:3000/users/1 로 GET 요청을 보내면 Param 의 id 의 타입은 String 으로 들어오게 된다. 그러나 아래와 같이 Param 의 타입을 number 로 변경해주면 number 로 들어오게 된다. 이 기능을 사용하기 위해서는 validationPipe 속성에 trasnform 을 true 로 설정해 주어야 한다. express 를 사용할 때면 param 의 값을 일일이 형변환을 해주었어야 했는데 NestJS 를 사용하면 그럴 필요도 없고 아주 간편하다. 

...
getUserById(@Param('id') userId: number) {
    console.log(typeof userId); // number
    return this.usersService.getUserById(userId);
  }
...

 

 

참고

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기