pipe는 @Injectable()
데코레이터가 적용되어 있는 클래스다.
NestJS에서 Pipe는 들어오는 요청 데이터를 변환(transformation)하거나 유효성 검사(validation) 등의 데이터 변환 및 검증 작업을 수행하는 데 사용된다.
데이터 변환 :
input 데이터를 원하는 형태로 변환한다.
만약, string 형태여야하는데, number 형식으로 데이터가 들어온다면,
pipe에서 자동으로 string 형태로 바꿔준다.
유효성 검사 :
input 데이터를 검증한 뒤 유효하지 않을 경우 예외를 던져 에러가 발생한다.
유효한 경우에는, 해당 상태 그대로 전달하여 handler에 도착하게 된다.
만약, name.length < 5 여야하는데, 5자 이상이 오면 에러가 발생.
Pipe는 controller route handler의 매개변수에 대해 동작한다.
Controller 메소드가 호출되기 직전에 pipe를 삽입한 뒤,
데이터를 수신 받아서, 데이터 변화와 유효성 검사 체크 뒤 결과에 따라 작동된다.
- Handler-level Pipes
- Parameter-level Pipes
- Global-level Piple
특정 메서드(핸들러)에 파이프를 적용하는 방식으로 해당 메서드만 특정 파이프를 적용하고, 다른 메서드에는 적용하지 않을 수 있다.
@Post()
@UsePipes(pipe)
createBoard(
@Body('title') title,
@Body('content') content,
){}
@UsePipes()
데코레이터를 사용한다.createBoard()
라우트 핸들러 하나에만 pipe를 작동하게 되는 것이다.@Post()
createBoard(
@Body('title', ParameterPipe) title: string
@Body('content') content: string
){}
title
파라미터에만 파이프 적용이 되는 상태다.async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPiples(GlobalPipes);
await app.listen(3000);
}
bootstrap();
main.ts
에 작성하면 된다.NestJs에서 기본적으로 만들어 놓은 6개의 파이프
ValidationPipe
: 데이터 유효성 검사를 수행하는 파이프.
ParseIntPipe
: string을 number로 바꿔주는 파이프.
주로 URL 경로나 숫자 값을 받을 때 유용하다.
@Get(':id')
getUserById(@Param('id', ParseIntPipe) id: number){}
위의 코드를 보면, id
값이 string에서 number로 변환된 후 사용된다.
만약, 아래와 같이 파라미터의 값으로 number가 아닌 string으로 요청을 보내게 된다면,
localhost:3000/users/hello
400 Bad Request Error가 발생하게 된다.
ParseBoolPipe
: string을 boolean 값으로 변환된다.
ParseArrayPipe
: string을 array 형태로 변환되어 사용된다.
ParseUUIDPipe
: string에서 UUID로 변환한다.
UUID값의 유효성을 확인하면서 처리할 수 있다.
DefaultValuePipe
: default 값을 설정
@Get()
getUsers(@Query('page', new DefaultValuePipe(3)) page: number) {}
Post에 적용해보겠다!
예를 들어, 내용이 작성되지 않았을 때 유효성 검사를해서 에러를 발생시킬 수 있다.
class-transformer
, class-validator
npm i class-transformer class-validator --save
import {IsNotEmpty} from 'class-validator';
export class CreateBoardDto{
@IsNotEmpty()
title: string;
@IsNotEmpty()
content: string;
}
@Post()
@UsePipes(ValidationPipe)
createBoard(@Body() createBoardDto: CreateBoardDto): Board{}
UsePipes(ValidationPipe)
파이프를 만들어 사용한다.ValidationPipe
라고 작성했다.비어있는 상태에서 보내주면 아래와 같이 에러가 발생한다.
{
"message": [
"title should not be empty",
"content should not be empty"
],
"error": "Bad Request",
"statusCode": 400
}
custom pipe는 데이터의 유효성 검사, 데이터 변환 등등 다양한 용도로 사용될 수 있다.
먼저 PipeTransform
인터페이스를 구현하는 클래스를 작성해야한다.
따라서, custom pipe를 만들 경우 항상 implements PipeTransform
을 작성해야한다.
PipeTransform
인터페이스는 transform()
메소드를 정의하게 된다.
transform()
메소드파이브가 변환 혹은 검증할 값을 입력으로 받고, 변환된 값을 Route 핸들러로 전달된다.
만약, 예외가 생긴다면, 클라이언트에게 바로 에러가 전달된다.
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}로 변경할 수 없음.`)
}
return value;
}
private isStatusValid(status: any){
const index = this.StatusOptions.indexOf(status);
return index !== -1;
}
}
statusOption
를 정의할 때, 값이 변경되면 안되기 때문에 readonly
로 사용한다.
readonly를 사용하면, 클래스 외부에서 사용할 수 있지만, 값을 변경할 수 없다.
status에 값을 넣어줄 때, 어떠한 문자열이 들어온다고 하더라도 대문자로 변환시킨다.
status를 지정할 때 PRIVATE
, PUBLIC
모두 대문자로 표기했기 때문.
isStatusValid
함수를 만들어서 StatusOptions
안에 들어있는 값인지 확인한다.
indexOf(status) status가 들어있는 index 값을 찾는다.
StatusOptions이 현재 배열로 되어있기 때문에 해당 status 값을 찾으면 0 혹은 1이 나올 것이다.
배열 내에 값이 존재하지 않으면 -1을 반환한다.
let numsIdx = [1, 3, 5, 7];
console.log(numsIdx.indexOf(1)); // 0
console.log(numsIdx.indexOf(7)); // 3
console.log(numsIdx.indexOf(9)); // -1
따라서, StatusOptions 안에 들어있지 않는 값이 들어올 경우, -1이 반환될테고, -1 !== -1은 false
이기 때문에,
if(!this.isStatusValid(value)){}
가 실행되어 에러 상태러를 확인할 수 있다.