Javascript/NestJS

NestJS Pipe에 대해 알아보기

kyoulho 2023. 3. 14. 08:21

Pipe


클라이언트가 보내는 HTTP 요청으로부터 데이터를 Parsing 하고 Validation 하여 컨트롤러 메서드의 파라미터로 전달하는 중간 레이어이다. 스프링 MVC의 ArgumentResolver와 Bean Validation이라고 생각하면 된다. 스프링의 BeanValidator와 비슷한 구조를 하고 있지만 예외처리를 각각의 Pipe에서 하기 때문에 오히려 코드가 깔끔해질것 같다는 생각이 든다.

NestJS 기본 내장 파이프

  • ValidationPipe: DTO에 대한 유효성 검사를 수행합니다.
  • ParseArrayPipe: 문자열을 배열로 변환합니다.
  • ParseBoolPipe: 문자열을 부울 값으로 변환합니다.
  • ParseEnumPipe: 문자열을 enum 값으로 변환합니다.
  • ParseIntPipe: 문자열을 정수로 변환합니다.
  • ParseFloatPipe: 문자열을 부동 소수점으로 변환합니다.
  • DefaultValuePipe: 값이 제공되지 않을 때 기본값을 제공합니다.
  • ParseUUIDPipe: 문자열을 UUID로 변환합니다.

 

Binding Pipes


파이프를 사용하는 방법에는 세가지가 있다.

Handler-level Pipe

@UsePipes데코레이터를 사용하며 핸들러 레벨에서 파이프를 사용하는 것이다.

보통 요청 데이터 유효성 검사, 데이터 변환, 로깅 등과 같은 기능을 수행하며, 모든 파라미터에 적용된다.

  @Post()
  @UsePipes(ValidationPipe)
  createBoard(@Body() createBoardDto: CreateBoardDto): Board {
    return this.boardsService.createBoard(createBoardDto);
  }

Parameter-level Pipe

핸들러의 파라미터에만 적용되는 파라미터 레벨의 파이프 사용법이다.

@Query, @Param , @Body 에서 모두 사용할 수 있으며 두 번째 인자로 파이프를 전달하면 된다.

  @Get(':id')
  getBoardById(@Param('id', ParseIntPipe) id: number): Board {
    return this.boardsService.getBoardById(id);
  }

Global Pipe

애플리케이션 전체에서 사용되는 파이프 사용법이다.

모든 요청에 대해 적용되며 전역 파이프를 사용하면 모든 컨트롤러의 요청을 미리 처리할 수 있다.

// main.ts

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
	app.useGlobalPipes(ValidationPipe);
  await app.listen(3000);
}
bootstrap();

 

Pipe 사용


NestJS에서 기본적으로 제공하는 ValidationPipe를 사용해서 DTO에 대한 유효성 검사하는 예제이다.

Validation 데코레이터를 담고 있는 class-validator 모듈과 데이터 변환에 class-transformer 모듈이  필요하다.

[Validation 데코레이터 문서]

npm install --save class-validator

 

// createBoardDto.js
import { IsNotEmpty } from 'class-validator';

export class CreateBoardDto {
  @IsNotEmpty()
  title: string;
  @IsNotEmpty()
  description: string;
}

// boardController.js
@Post()
@UsePipes(ValidationPipe)
createBoard(@Body() createBoardDto: CreateBoardDto): Board {
  return this.boardsService.createBoard(createBoardDto);
}

 

 

커스텀 파이프 구현


PipeTransform 인터페이스를 구현하여 커스텀 파이프를 구현할 수 있다.

PipeTransform

PipeTransform은 transform() 메소드를 구현해야 하며, 이 메서드에서 입력 값을 처리하고 결과 값을 핸들러로 반환한다. 만약 예외가 발생하면 클라이언트에 바로 전해진다. 이 메소드의 첫 번째 파라미터 value는 입력 값이다.

AgrumentMetadata

 

// upperCasePipe.js
import { PipeTransform, Injectable } from '@nestjs/common';

@Injectable()
export class UpperCasePipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata): string {
		console.log('value', value);         // value jhkyun
		console.log('metadata', metadata);   // metadata { metatype: [Function: String], type: 'param', data: 'username' }
    return value.toUpperCase();
  }
}

// userController.js
import { Controller, Get, Param, UsePipes } from '@nestjs/common';
import { UpperCasePipe } from './uppercase.pipe';

@Controller('users')
export class UsersController {

  @Get(':username')
  @UsePipes(new UpperCasePipe())
  getUser(@Param('username') username: string) {
    return { username };
  }
}