Nest에서 파이프는 주로 두 가지 기능을 한다.
Nest에는 미리 정의되어 있어 사용 가능한 9개의 파이프가 있다.
- ValidationPipe
- ParseIntPipe
- ParseFloatPipe
- ParseBoolPipe
- ParseArrayPipe
- ParseUUIDPipe
- ParseEnumPipe
- DefaultValuePipe
- ParseFilePipe
이름으로 기능을 유추할 수 있듯이 유효성을 검사하는 ValidationPipe와 나머지 데이터 형에 대한 파이프들이다.
$ npm install class-validator class-transformer
위의 명령어로 두 가지 모듈을 설치한 후 사용이 가능하다.
파이프를 사용하기 위해서는 파이프를 적절한 context에 바인딩해야 한다. 크게 Handler-level Pipes, Parameter-level Pipes, Global-level Pipes 로 나눌 수 있다.
@Post()
@UsePipes(pipe)
createBoard(@Body('title') title, @Body('content') content): Board {
...
}
@UsePipe() decorator를 이용하여 해당 handler의 모든 파라미터에 파이프를 적용할 수 있다.
@Post()
createBoard(@Body('title', ParameterPipe) title,
@Body('content') content
) {
...
}
parameter 옆에 작성함으로써 특정한 파라미터에만 파이프를 적용할 수 있다.
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(GlobalPipes);
await app.listen(3000);
}
bootstrap();
이 파이프는 애플리케이션 레벨의 파이프로 클라이언트에서 들어오는 모든 요청에 적용된다. 가장 상단인 main.ts에 작성하면 된다.
데이터의 유효성을 검증하기 위해 ValidationPipe를 사용해보겠다. class-validator 모듈을 설치했다면 다양한 validation-decorators를 사용할 수 있다. class-validator GitHub을 참고하자!
// dto/createBoard.dto.ts
import { IsNotEmpty } from 'class-validator';
export class CreateBoardDto {
@IsNotEmpty()
title: string;
@IsNotEmpty()
content: string;
}
먼저 위와 같이 DTO에 원하는 validation decorator를 달아준다.
@Controller('boards')
export class BoardsController {
constructor(private boardsService: BoardsService) {}
@Post()
@UsePipes(new ValidationPipe())
createBoard(@Body() createBoardDto: CreateBoardDto): Board {
return this.boardsService.creatPost(createBoardDto);
}
}
그리고 ValidationPipe를 적용하기 위해 Handler에 @UsePipes decorator안에 ValidationPipe의 인스턴스를 인자로 넣어준다. 물론 main.ts에서 애플리케이션 레벨로 관리할 수도 있다.
이렇게 하면 title이나 content의 값이 빈 채로 들어오면 에러를 발생시킨다.
Pipe를 직접 정의하여 사용할 수 있는데 @Injectable() decorator가 달린 클래스로 정의하며, PipeTransform interface를 implements해야 한다.
// validation.pipe.ts
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
@Injectable()
export class ValidationPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
// code
return value;
}
}
모든 파이프는 PipeTransform interface대로 transform() method를 정의해야 한다. arguments로 value와 metadata가 있는데, value는 처리되는 인자의 값이고 metadata는 그 값에 대한 메타데이터이다. metadata의 ArgumentMetadata 타입은 아래와 같다.
export interface ArgumentMetadata {
type: 'body' | 'query' | 'param' | 'custom';
metatype?: Type<unknown>;
data?: string;
}
이 transform()에서 파이프에서 수행해야 할 작업을 정의할 수 있다.
export class BoardStatusValidationPipe implements PipeTransform {
readonly StatusOptions = [BoardStatus.PRIVATE, BoardStatus.PUBLIC];
transform(value: any) {
value = value.toUpperCase();
if (!this.isStatusValid(value)) {
throw new BadRequestException(`${value} isn't in the status options`);
}
return value;
}
private isStatusValid(status: any) {
const index = this.StatusOptions.indexOf(status);
return index !== -1;
}
}
transform() method에서 return된 값은 handler로 전해지고 예외가 발생하면 바로 클라이언트로 전해진다.
https://docs.nestjs.com/pipes
https://www.youtube.com/watch?v=3JminDpCJNE&t=6261s