파이프(Pipe)

2025. 5. 2. 17:06·NestJS/NestJS 문서화

파이프의 역할과 특징

파이프는 클라이언트에서 전달된 요청 데이터를 검증 혹은 변환하는 역할을 합니다.

파이프의 특징

라우팅 핸들러(컨트롤러)가 호출되기 전에 작동합니다.

파이프 내부에서 예외 처리를 할 수 있습니다.
비동기식일 수 있습니다.

@Injectoble() 데코레이터가 있는 클래스입니다.

파이프는 보통 직접 만들지 않고, NestJS 기본 제공 파이프 혹은 class-transformer, class-validator 패키지를 사용합니다.

class-transformer, class-validator를 사용한 데이터 검증 및 변환

class-transformer, class-validator를 사용해 데이터 검증 및 변환을 하기 위해서는 다음 과정을 거쳐야 합니다.

  1. main.ts 파일에 전역으로 파이프를 사용하도록 설정(옵션 객체를 전달하지 않으면 필요 없는 과정입니다)
  2. DTO(Data Transfer Object) 클래스에 데이터 검증 및 변환 규칙 정의
  3. 라우터에 DTO 적용

main.ts 파일에 globalPipe 적용

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

DTO 클래스에 데이터 검증 및 변환 규칙 정의

DTO에 규칙을 적용하기 전에 class-validator, class-transformer 패키지를 추가해야 합니다.

npm install class-validator class-transformer

DTO 파일에 class-validator의 데코레이터 적용

import { IsNotEmpty } from 'class-validator';

export class CreateTaskDto {
  @IsNotEmpty()
  title: string;

  @IsNotEmpty()
  description: string;
}

라우터에 DTO 적용

@Post()
async createTask(@Body() createTaskDto: CreateTaskDto) {
  return await this.taskService.createTask(createTaskDto);
}

class-validator와 class-transformer가 작동하는 방식(파이프를 전역에 적용해도 괜찮은 이유)

데이터 검증 시 class-validator와 class-transformer가 생성해주는 메타데이터를 이용해 데이터를 검증하기 때문에 파이프를 전역에서 사용하도록 설정해도 문제가 발생하지 않습니다.

 

DTO에 class-validator와 class-transformer 데코레이터를 적용하지 않으면 런타임에 실행되는 자바스크립트 클래스 파일에는 아무런 메타데이터도 갖고 있지 않습니다.

 

create-task.dto.ts 파일

export class CreateTaskDto {
  title: string;
  description: string;
}

/dist 폴더에 생성되는 create-task.dto.js 파일

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CreateTaskDto = void 0;
class CreateTaskDto {
}
exports.CreateTaskDto = CreateTaskDto;
//# sourceMappingURL=create-task.dto.js.map

 

DTO 파일에 데코레이터를 적용하면 자바스크립트 클래스 파일에 메타데이터가 추가됩니다.

create-task.dto.ts 파일

import { Transform } from 'class-transformer';
import { IsNotEmpty, IsString } from 'class-validator';

export class CreateTaskDto {
  @Transform(({ value }) => String(value))
  @IsNotEmpty()
  @IsString()
  title: string;

  @Transform(({ value }) => String(value))
  @IsNotEmpty()
  @IsString()
  description: string;
}

/dist 폴더에 생성되는 create-task.dto.js 파일

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CreateTaskDto = void 0;
const class_transformer_1 = require("class-transformer");
const class_validator_1 = require("class-validator");
class CreateTaskDto {
}
exports.CreateTaskDto = CreateTaskDto;
__decorate([
    (0, class_transformer_1.Transform)(({ value }) => String(value)),
    (0, class_validator_1.IsNotEmpty)(),
    (0, class_validator_1.IsString)(),
    __metadata("design:type", String)
], CreateTaskDto.prototype, "title", void 0);
__decorate([
    (0, class_transformer_1.Transform)(({ value }) => String(value)),
    (0, class_validator_1.IsNotEmpty)(),
    (0, class_validator_1.IsString)(),
    __metadata("design:type", String)
], CreateTaskDto.prototype, "description", void 0);
//# sourceMappingURL=create-task.dto.js.map

class-validator

class-validator는 데이터의 유효성을 검증할 때 사용하는 라이브러리입니다.

자주 사용할 것 같은 데코레이터만 정리했습니다.

데코레이터 설명
@IsNotEmpty() null, '', undefined가 아닌지 확인
@IsIn(값 배열) 배열의 값 중 하나와 일치하는지 확인
@IsNotIn(값 배열) 배열의 값 모두와 일치하지 않는지 확인
@IsBoolean() 논리형인지 확인
@IsDate() Date() 타입인지 확인
@IsISO8601() 연월일 시분초 형식의 Date() 타입인지 확인
@IsNumber(옵션 객체) 숫자형인지 확인
옵션 속성
{
  allowNaN : boolean, // NaN 허용여부
  allowInfinity: boolean, // Infinity 허용여부
  maxDecimalPlaces: number, // 최대 소수 자리수
}
@IsInt() 정수인지 확인
@IsFloat() 실수인지 확인
@IsArray() 배열인지 확인
@Min() 최소값 설정
@Max() 최대값 설정
@Contains(문자열) 문자열이 포함되었는지 확인
@NotContains(문자열) 문자열이 포함되지 않았는지 확인
@IsAlpha() 문자열이 a-zA-Z인지 확인
@IsAlphanumeric() 문자열이 a-zA-Z0-9인지 확인
@IsEmail(옵션 객체) 문자열이 이메일인지 확인
옵션이 있는데 너무 많아서 생략(문서에도 안 나와있어서 열 받음)
@IsFQND(옵션 객체) 문자열이 도메인 주소인지 확인
옵션이 있는데 쓸 일이 없을 듯?
@IsIP(버전옵션) 문자열이 IP 주소인지 확인
버전 옵션: '4' 혹은 '6'. IP의 버전이 4인지 6인지 명시
@IsJSON() 문자열이 유효한 JSON인지 확인
@Length(최소 길이, 최대 길이) 문자열의 최소 길이, 최대 길이 지정
최소 길이는 필수, 최대 길이는 옵션
@MinLength(최소 길이) 문자열의 최소 길이 지정
@MaxLength(최대 길이) 문자열의 최대 길이 지정
@Matches(정규식패턴, 수정자?)
@Matches(정규식패턴, 옵션객체)
정규식과 일치하는지 확인.
수정자가 왜 있는지도 모르겠고, 도대체 어떻게 사용하는지도 모르겠습니다.
옵션 객체에는 message를 전달해 오류 메세지를 전달할 수 있습니다.
{ message: `오류입니다`}
@ArrayNotEmpty() 비어있지 않은 배열인지 확인
@ArrayUnique() 배열의 모든 값이 유니크 값인지 확인
@IsArray()
@IsString({ each: true })
@MinLength(3, { each: true })
배열 각각의 요소가 string인지 확인
배열 각각의 요소의 길이가 3이상인지 확인

옵션

class-validator의 데코레이터 사용 시 객체를 전달해 옵션을 사용할 수 있습니다.

@IsInt({ message: '정수형만 사용 가능합니다' })
id: number;

@MaxLength(20, {
  each: true,
})
tags: string[];
옵션 설명
message 오류 발생 시 전달할 메세지
each 배열, Set, Map의 경우 배열 요소 각각에 옵션을 검사할지 여부

class-transformer

class-transformer는 데이터를 변환할 때 사용하는 라이브러리입니다.

@Transform() 데코레이터

데코레이터에 콜백 함수를 전달해 사용합니다.

입력된 데이터를 콜백 함수의 반환값으로 수정합니다.

 

콜백 함수의 파라미터 선언부에 객체를 전달한 이유는 콜백 함수에 실제로 객체가 전달되기 때문입니다.

value 말고는 사용할 것 같지는 않지만 정리해둡니다.

속성 설명
value 값
key 키
obj 값이 담긴 객체 전체
type 변형 유형
options 콜백 함수에서 사용할 수 있는 옵션 객체
import { Transform } from 'class-transformer';

export class Post {
  id: number;
  
  @Transform(({ value }) => trim(value) )
  title: string;
  
  @Transform(({ value }) => trim(value) )
  content: string;
}

쓸지도 모를 데코레이터들

@Transform() 데코레이터 말고는 쓸 것 같지 않지만 혹시 몰라 정리합니다.

데코레이터 설명
@Type( () => 자료형 ) 원래 자료형을 익명함수에서 반환한 자료형으로 변경.
타입스크립트가 아니라 자바스크립트의 자료형을 사용해야 합니다.
보통 첫 글자가 소문자면 타입스크립트의 자료형이고,
첫 글자가 대문자면 자바스크립트의 자료형입니다.
@Exclude( { 옵션객체 } ) 하나의 DTO를 request, response에서 사용할 때 요청 객체와 응답 객체에 필요한 데이터가 다를 때 데이터를 제외하기 위해 사용합니다.

두 가지 옵션이 있습니다.

toPlainOnly: 클래스를 객체로 변환 시. reponse에 사용
toClassOnly: 객체를 클래스로 변환 시. request에 사용
@Expose( {옵션객체} ) 클래스의 프로퍼티가 아니라 getter나 메서드가 반환하는 값을 전달합니다.
프로퍼티에도 사용할 수 있습니다.

옵션객체에 name: string을 전달해 프로퍼티명이 아니라 다른 이름으로 객체를 전달 할 수 있습니다.

특정 프로퍼티만 전달하기

class에 @Exclude() 데코레이터를 사용하고, 전달하고 싶은 프로퍼티에만 @Expose() 데코레이터를 사용하면 @Expose() 데코레이터를 사용한 프로퍼티만 전달할 수 있습니다.

 

아래 예시는 password 프로퍼티를 전달하지 않습니다.

import { Exclude, Expose } from 'class-transformer';

@Exclude()
export class User {
  @Expose()
  id: number;

  @Expose()
  email: string;

  password: string;
}

ValidationPipe 옵션

ValidationPipe() 사용 시 옵션 객체를 전달할 수 있습니다.

ValidationPipe()를 사용하지 않아도class-validation, class-transformer 라이브러리가 작동하기 때문에 옵션 객체를 사용하지 않으면 코드에 추가할 필요는 없습니다.

 

옵션 객체의 속성은 문서에서 확인할 수 있습니다.

옵션 설명
whitelist 기본값 false.
true 설정 시 DTO 속성에 class-validator 데코레이터가 존재하지 않으면 request 객체에서 제거.
forbidNonWhitelisted 기본값 false.
true 설정 시 DTO 속성에 class-validator 데코레이터가 존재하지 않는 속성이 request 객체에 있을 때 오류 반환
Get 이외의 메서드에서는 사용할 지도 모르겠네요.

NestJS 기본 제공 파이프

@nestjs/common 모듈에 9개의 기본 파이프가 제공됩니다.

 

class-transformer, class-validator 대신 기본 제공 파이프를 사용할 일은 거의 없습니다.

기본 제공 파이프를 사용하는 것은 단일 책임 원칙에 위배되기 때문에 사용하지 않는 것을 권장합니다.

 

request 객체는 모두 string으로 전달됩니다.

Parse[자료형]Pipe들은 string으로 전달되는 request 객체를 해당 자료형으로 변경합니다. 변경이 불가능하면 클라이언트에 오류를 반환합니다.

파이프 설명
ValidationPipe (DTO 혹은 전송된 데이터)와 가장 비슷한 클래스의 전체 속성에 대한 호환성을 검증합니다.
속성 매핑이 제대로 이루어지지 않는 경우 유효성 검사가 실패합니다.
DefaultValuePipe 파라미터에 null 혹은 undefined가 전달되면 기본값으로 변경
ParseIntPipe 파라미터를 정수형으로 변경. 변경 불가능 시 에러 처리
ParseFloatPipe 파라미터를 실수형으로 변경. 변경 불가능 시 에러 처리
ParseBoolPipe 파라미터를 논리형으로 변경. 변경 불가능 시 에러 처리
ParseArrayPipe 파라미터를 배열로 변경. 변경 불가능 시 에러 처리
ParseUUIDPipe 파라미터를 UUID로 변경. 변경 불가능 시 에러 처리
ParseEnumPipe 파라미터를 Enum으로 변경. 변경 불가능 시 에러 처리
ParseFilePipe 파라미터를 파일로 변경. 변경 불가능 시 에러 처리
ParseDatePipe 파라미터를 Date 객체로 변경. 변경 불가능 시 에러 처리

기본 제공 파이프 적용 방법

기본 제공 파이프는 매개 변수에 적용합니다.

 

파이프는 의존성 주입 대상이기 때문에 새로운 인스턴스를 만들지 않고 사용할 수 있습니다.

@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
  return this.catsService.findOne(id);
}

파이프에 오류 코드나 오류 메세지 같은 옵션을 전달하고 싶으면 새로운 인스턴스를 만들어야 합니다.

@Get(':id')
async findOne(
  @Param('id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }))
  id: number,
) {
  return this.catsService.findOne(id);
}

하나의 매개 변수에 여러 개의 파이프를 사용할 수 있습니다.

@Get()
async findAll(
  @Query('activeOnly', new DefaultValuePipe(false), ParseBoolPipe) activeOnly: boolean,
  @Query('page', new DefaultValuePipe(0), ParseIntPipe) page: number,
) {
  return this.catsService.findAll({ activeOnly, page });
}

사용자 정의 파이프

사용자 정의 파이프 구현 시 PipeTransform 인터페이스를 구현해야 합니다.

PipeTransform 인터페이스 구현 시 transform(value: any, metadata: ArgumentMetadata) 메서드를 구현해야 합니다.

transform 메서드

transform() 메서드는 두 개의 파라미터를 가집니다.

  • value: 처리 할 파라미터의 값
  • metadata(옵션): 처리할 파라미터의 메타데이터가 포함된 객체

transform() 메서드에서 반환된 결과는 라우팅 핸들러에 전달됩니다.
예외 발생 시 클라이언트에 바로 결과를 반환합니다.

구현 예시

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

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

 

저작자표시 (새창열림)

'NestJS > NestJS 문서화' 카테고리의 다른 글

인터셉터(Interceptor)  (0) 2025.05.09
가드(Guard)  (0) 2025.05.09
모델과 DTO  (0) 2025.05.02
예외 필터  (0) 2025.04.24
프로바이더와 서비스  (0) 2025.04.23
'NestJS/NestJS 문서화' 카테고리의 다른 글
  • 인터셉터(Interceptor)
  • 가드(Guard)
  • 모델과 DTO
  • 예외 필터
남느
남느
  • 남느
    남느
    남느
  • 전체
    오늘
    어제
    • 분류 전체보기 (64)
      • 프로그래밍 (15)
      • 웹 기초 지식 (2)
      • Node.js 기초 (1)
      • 알고리즘(Node.js) (1)
      • NestJS (19)
        • NestJS 문서화 (13)
        • NestJS 레시피 (2)
        • NestJS 게시판 API 프로젝트 (4)
      • TypeORM (5)
      • 우분투 적응기 (8)
      • 리눅스 답은 하모니카다 (4)
      • 자바 (1)
      • 살다보니 드는 생각들 (2)
      • 도커 (1)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백엔드
    신입
    웹
    취업
    개발자
    프로그래머
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
남느
파이프(Pipe)
상단으로

티스토리툴바