NestJS에서 Pipe란?

Olivia·2023년 8월 10일
0

[NestJS]

목록 보기
10/11
post-thumbnail

NestJS에서 Pipe란?

pipe는 @Injectable() 데코레이터가 적용되어 있는 클래스다.
NestJS에서 Pipe는 들어오는 요청 데이터를 변환(transformation)하거나 유효성 검사(validation) 등의 데이터 변환 검증 작업을 수행하는 데 사용된다.

  • 데이터 변환 :
    input 데이터를 원하는 형태로 변환한다.
    만약, string 형태여야하는데, number 형식으로 데이터가 들어온다면,
    pipe에서 자동으로 string 형태로 바꿔준다.

  • 유효성 검사 :
    input 데이터를 검증한 뒤 유효하지 않을 경우 예외를 던져 에러가 발생한다.
    유효한 경우에는, 해당 상태 그대로 전달하여 handler에 도착하게 된다.
    만약, name.length < 5 여야하는데, 5자 이상이 오면 에러가 발생.

Pipe는 controller route handler의 매개변수에 대해 동작한다.
Controller 메소드가 호출되기 직전에 pipe를 삽입한 뒤,
데이터를 수신 받아서, 데이터 변화와 유효성 검사 체크 뒤 결과에 따라 작동된다.


Pipe 사용 방법

  • Handler-level Pipes
  • Parameter-level Pipes
  • Global-level Piple

특정 메서드(핸들러)에 파이프를 적용하는 방식으로 해당 메서드만 특정 파이프를 적용하고, 다른 메서드에는 적용하지 않을 수 있다.

Handler-level Pipes

@Post()
@UsePipes(pipe)
createBoard(
	@Body('title') title,
    @Body('content') content,
){}
  • Handler-level Pipes는 @UsePipes() 데코레이터를 사용한다.
  • createBoard() 라우트 핸들러 하나에만 pipe를 작동하게 되는 것이다.
  • 파라미터 모두 적용되기 때문에 Parameter-level Pipes 보다 넓게 적용 된다.

Parameter-level Pipes

@Post()
createBoard(
	@Body('title', ParameterPipe) title: string
  	@Body('content') content: string
){}
  • Parameter-level Pipes는 특정 매개변수에만 파이프를 적용한다.
  • 특정 파라미터에만 파이프를 적용할 수 있기 때문에 특정 파라미터의 데이터를 다르게 처리 할 수 있다.
  • 위의 코드를 보면 title파라미터에만 파이프 적용이 되는 상태다.

Global-level Pipes

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPiples(GlobalPipes);
  await app.listen(3000);
}
bootstrap();
  • Global-level Pipe는 어플리케이션 전체에 적용되는 파이프라 가장 넓은 범위에 적용되는 파이프다.
  • 클라이언트에서 들어오는 모든 요청에 대해 동일한 파이프가 자동으로 적용된다.
  • Global-level Pipe를 적용하기 위해서는 가장 상단인 main.ts에 작성하면 된다.

Built-in Pipes

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) {}
  • page쿼리 파라미터가 number로 변환된 후, default 값으로 3이 설정된다.

built-in Pipe 사용 유효성 검사를 해보잣✨

Post에 적용해보겠다!
예를 들어, 내용이 작성되지 않았을 때 유효성 검사를해서 에러를 발생시킬 수 있다.

Pipe를 사용하기위해 필요한 모듈 및 설치 방법

  • 필요한 모듈
    : class-transformer, class-validator
  • 설치 방법
    npm i class-transformer class-validator --save
  • validation관련 참고

pipe 생성하기

DTO

import {IsNotEmpty} from 'class-validator';

export class CreateBoardDto{
	@IsNotEmpty()
  	title: string;
  	@IsNotEmpty()
  	content: string;
}
  • 위의 코드를 보면 알 수 있듯이, DTO 내용 안에 각각의 부분에 설정해주면,
    title과 content가 비어있을 때 에러를 보낸다.

Controller

@Post()
@UsePipes(ValidationPipe)
createBoard(@Body() createBoardDto: CreateBoardDto): Board{}
  • Controller에서 요청이 들어올 때,
    Handler-level에 UsePipes(ValidationPipe) 파이프를 만들어 사용한다.
    유효성 검사를 하기 위해서 ValidationPipe라고 작성했다.
  • 이렇게 설정하면, DTO에 적용한 것들이 자동으로 유효성 체크를 해주게 된다.

비어있는 상태에서 보내주면 아래와 같이 에러가 발생한다.

{
    "message": [
        "title should not be empty",
        "content should not be empty"
    ],
    "error": "Bad Request",
    "statusCode": 400
}

custome pipe 사용 유효성 검사를 해보잣✨

custom pipe는 데이터의 유효성 검사, 데이터 변환 등등 다양한 용도로 사용될 수 있다.

custome pipe 구현 방법

먼저 PipeTransform 인터페이스를 구현하는 클래스를 작성해야한다.
따라서, custom pipe를 만들 경우 항상 implements PipeTransform을 작성해야한다.
PipeTransform 인터페이스는 transform()메소드를 정의하게 된다.

transform()메소드

  • 첫 번째 파라미터 : 처리된 인자의 값
  • 두 번째 파라미터 : 인자에 대한 Metadata를 포함한 객체

파이브가 변환 혹은 검증할 값을 입력으로 받고, 변환된 값을 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)){}가 실행되어 에러 상태러를 확인할 수 있다.

결과


참고
공식문서
따라하며 배우는 NestJS

profile
👩🏻‍💻

0개의 댓글