[Nest JS] Pipes

minidoo·2021년 10월 5일
0

NestJS

목록 보기
4/8
post-thumbnail

파이프는 Injectable() 데코레이터로 주석이 달린 클래스이며, PipeTransform 인터페이스를 구현해야 한다.

파이프는 입력 데이터를 1. 원하는 형식으로 변환(transformation) 2. 유효성 검사(validation) 으로 사용된다.

두 경우 모두 컨트롤러 라우트 핸들러가 처리하는 인수 에서 작동한다. 메소드가 호출되기 직전에 파이프를 삽입하고 파이프는 메소드로 향하는 인수를 수신하여 이에 대해 작동한다.

Nest에는 즉시 사용할 수 있는 6개의 파이프가 제공되며, 유효성 검사의 사용 사례인 ValidationPipe 에 대해 알아본다. 파이프는 @nestjs/common 패키지에서 내보내진다.

Custom Pipes

커스텀 버전을 처음부터 만들어 커스텀 파이프가 어떻게 구성되는지 살펴본다.

import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    return value;
  }
}

모든 파이프는 PipeTransform 를 implements 하기 위해 transform() 메서드를 구현해야 한다. value 매개변수는 현재 처리된 메서드 인수, metadata는 현재 처리된 메서드 인수의 메타데이터이다.

Schema Based Validation

라우트 핸들러에서 파이프가 사용되는 방법에 대해 알아본다.

// order.controller.ts

@Post()
async createOrder(@Body() orderDataDTO: OrderDataDTO) {
	this.orderService.createOrder(orderDataDTO);
}
// orderDataDTO.dto.ts

export class OrderDataDTO {
	barcode: number;
	user: string;
}

createOrder 메서드로 들어오는 모든 요청에 유효한 본문이 포함되어 있는지 확인하고 싶다. 따라서 OrderDataDTO 객체의 두 멤버를 검증해야 한다.

  1. 라우트 핸들러 메소드 내에서 수행
  2. 유효성 검사기 클래스
  3. 유효성 검사 미들웨어
  4. 검증 파이프

1번 방법은 단일 책임 규칙(SRP, 클래스는 단 한 개의 책임을 가져야 한다)을 위반하므로 이상적이지 않다. 2번 방법은 각 메서드 시작부분에서 유효성 검사기를 호출해야 한다는 단점이 있다. 전체 애플리케이션의 모든 컨텍스트에서 사용할 수 있는 일반 미들웨어를 만드는 것이 불가능하여 3번 방법도 어렵다.

class-validation 라이브러리를 사용하여 검증 파이프를 만들어본다.

Class Validation

이 강력한 라이브러리를 사용하여 데코레이터 기반 유효성 검사를 할 수 있다. 데코레이터 기반 유효성 검사는 처리된 속성의 metadata에 액세스할 수 있어 Nest의 파이프 기능과 결합할 때 매우 강력하다.

$ npm i --save class-validator class-transformer

orderDataDTO 클래스에 타입 검사를 할 수 있는 데코레이터를 추가한다.

// orderDataDTO.dto.ts

import { IsString, IsInt } from 'class-validator';

export class OrderDataDTO {
	@IsInt()
	barcode: number;
	
	@IsString()
	user: string;
}

ValidationPipe 클래스에 라이브러리를 사용한 코드를 작성한다.

// validation.pipe.ts

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';

@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    const object = plainToClass(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      throw new BadRequestException('Validation failed');
    }
    return value;
  }

  private toValidate(metatype: Function): boolean {
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}

transform() 메서드는 async 로 작성한다.
Nest는 동기 및 비동기 파이프를 모두 지원한다. 일부 class-validation 유효성 검사가 비동기화 될 수도 있기 때문에 메서드 자체를 async로 만든다.

헬퍼 함수 toValidate() 를 확인한다.
처리중인 현재 인수가 네이티브 자바스트립트 타입인 경우 유효성 검사 단계를 건너 뛴다.

클래스 변환기 함수 plainToClass() 를 사용하여 일반 자바스크립트 인수 객체를 타입이 지정된 객체로 변환한다.
네트워크 요청에서 역직렬화될 때 들어오는 포스트(post) 본문 객체가 아무 타입 정보도 가지고 있지 않기 때문에 장식된 형태로의 변환이 수행되어야 한다.

// order.controller.ts

@Post()
async createOrder(@Body(new ValidationPipe()) orderDataDTO: OrderDataDTO) {
	this.orderService.createOrder(orderDataDTO);
}

마지막 단계는 ValidationPipe 를 바인딩하는 것이다. 파이프는 매개변수 범위, 메서드 범위, 컨트롤러 범위 또는 전역 범위일 수 있다. 위의 코드는 매개변수 범위에서 작동된 파이프 예제이다.

Global Scoped Pipes

ValidationPipe 는 전체 애플리케이션의 모든 라우트 핸들러에 적용되도록 전역 범위(global-scoped) 파이프로 설정할 수 있다.

// main.ts

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

위의 방법은 전역 파이프의 바인딩이 모듈 컨텍스트 외부에서 수행되었으므로 종속성을 주입할 수 없다. 따라서 모든 모듈에서 직접 전역 파이프를 설정한다.

// app.module.ts

import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_PIPE,
      useClass: ValidationPipe,
    },
  ],
})
export class AppModule {}

Exception Filter 가 모든 throw 에러를 잡아냈다면, Pipes는 메소드가 호출되기 전에 DTO를 검사하는 역할을 한다.

참고 사이트

Pipes | 네스트 JS 한국어 매뉴얼
Nest.js and the Custom Validation Pipe

0개의 댓글