개요
NestJS에서 Pipe는 들어오는 데이터의 변환과 유효성 검사를 처리하는 강력한 도구이다.
파이프는 주로 요청 데이터가 적절한 형식인지 검사(validation)하거나 필요한 경우 해당 데이터를 다른 형식으로 변환하는데 사용된다.
파이프는 데이터가 핸들러로 전달되기 전에 실행되므로, 요청에 들어온 데이터가 검증되거나 변환된 상태로 컨트롤러로 전달된다.
pipe를 사용하는 목적은 딱 두가지다.
1.
transformation으로 값을 바꾸는 목적
@Get(':id')
async findOne(@Param('id', new ParseIntPipe()) id: number) {
return this.userService.findOne(id);
}
// 문자열로 전달된 id값을 정수값으로 변환해주는 파이프이다.
TypeScript
복사
2.
validation으로 값을 검사하는 목적
export class CreateProductDto {
@IsString()
@IsNotEmpty()
readonly productName: string;
@IsNumber()
@IsNotEmpty()
readonly price: string;
}
// VaildationPipe 사용
@Post()
async createProduct(
@Body((new ValidationPipe()) createProductDto: CreateProductDto,
): Promise<string> {
return this.productService.create(createProductDto);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------
main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// ValidationPipe를 전역적으로 설정
app.useGlobalPipes(new ValidationPipe());
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();
컨트롤러 매 함수마다 new ValidationPipe()를 해줄 필요 없고
main.ts 파일에 app.useGlobalPipes(new ValidationPipe()); 를 명시해주면 된다.
TypeScript
복사
dto 클래스에 @IsString 같이 데코레이터를 적용해도 main.ts 파일에 pipe에 대한 설정이 없으면 동작하지 않는다.
꼭 pipe를 전역으로 설정해주고 dto에 데코레이터로 validation을 해야 한다.
pipe의 종류
Nestjs의 pipe는 built-in pipe와 custom pipe가 있다.
built-in pipe는 Nestjs에서 제공하는 기본적인 pipe이고 custom pipe는 사용자가 직접 만드는 pipe이다.
1. Built-in pipe
1.
ValidationPipe
2.
ParseIntPipe
3.
ParseFloatPipe
4.
ParseBoolPipe
5.
ParseArrayPipe
6.
ParseUUIDPipe
7.
ParseEnumPipe
8.
DefaultValuePipe
정말 단순하게 타입만 바꾼다면 커스텀 pipe까지 만들 필요가 없이 빌트인 파이프를 사용하는게 더 효율적이다.
2. Custom pipe
NestJs에서 커스텀 파이프를 만드는 이유는 특정 로직을 적용해 들어오는 데이터를 변환하거나 유효성 검사를 추가로 처리하기 위함이 대부분이다.
다음은 커스텀 파이프를 만들어본 예시다. 특정 문자열을 소문자로 변환하는 커스텀 파이프로 이 파이프는 사용자가 입력한 문자열을 모두 소문자로 변환해 컨트롤러에 전달한다.
1. 커스텀 파이프 생성
먼저 PipeTransform 인터페이스를 구현하여 커스텀 파이프를 만듭니다. 이 파이프는 사용자가 입력한 문자열을 소문자로 변환하는 역할을 합니다.
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ToLowerCasePipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
if (typeof value !== 'string') {
throw new BadRequestException('Validation failed: String expected');
}
// 문자열을 소문자로 변환
return value.toLowerCase();
}
}
TypeScript
복사
2. 파이프 설명
•
PipeTransform 인터페이스: NestJS의 모든 파이프는 이 인터페이스를 구현해야 합니다. transform 메서드는 요청 데이터가 파이프를 통과할 때 실행됩니다.
•
value: any: 파이프를 통해 전달된 데이터. 이 파라미터가 클라이언트로부터 들어오는 값입니다.
•
metadata: ArgumentMetadata: 요청 데이터에 대한 메타데이터. 어떤 파라미터에서 왔는지 등 추가적인 정보를 제공합니다.
•
BadRequestException: 만약 값이 문자열이 아니라면, 잘못된 입력이라는 의미에서 예외를 던집니다.
3. 커스텀 파이프 사용
import { Controller, Post, Body } from '@nestjs/common';
import { ToLowerCasePipe } from './to-lower-case.pipe';
@Controller('users')
export class UserController {
@Post()
createUser(@Body('username', new ToLowerCasePipe()) username: string) {
// username이 소문자로 변환된 상태로 들어옴
return `Username is: ${username}`;
}
}
TypeScript
복사
설명
•
@Body('username', new ToLowerCasePipe()): Body 데코레이터에 커스텀 파이프를 사용했습니다. 이 파이프는 요청에서 전달된 username 필드에 대해 적용되며, 해당 값을 소문자로 변환합니다.
4. 결과
이제 클라이언트가 /users 엔드포인트로 요청을 보낼 때, 다음과 같은 JSON을 보낸다고 가정합니다.
{
"username": "JohnDoe"
}
JSON
복사
ToLowerCasePipe가 적용되어 컨트롤러에서 처리할 때는 "johndoe"로 변환된 값이 들어오게 됩니다.
POST /users
Body: { "username": "JohnDoe" }
Response: Username is: johndoe
Shell
복사
5. 추가 예시: 숫자를 범위 내로 제한하는 커스텀 파이프
다른 예로, 숫자가 특정 범위 내에 있는지 확인하는 커스텀 파이프도 만들 수 있다.. 예를 들어, 나이가 18~60 사이의 값이어야 한다는 검증을 하는 파이프를 만들 수 있다.
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class AgeRangePipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
const age = parseInt(value, 10);
if (isNaN(age) || age < 18 || age > 60) {
throw new BadRequestException('Age must be between 18 and 60');
}
return age;
}
}
TypeScript
복사
@Controller('users')
export class UserController {
@Post()
createUser(@Body('age', new AgeRangePipe()) age: number) {
return `User's age is: ${age}`;
}
}
TypeScript
복사
커스텀 파이프를 통해 애플리케이션의 로직을 유연하게 확장하고, 데이터 처리 및 검증을 더 세밀하게 할 수 있다.