Nest.js를 배워보자/6. NestJS Middleware , Guard , Intercept

Custom Pipe 만들기

_Blue_Sky_ 2025. 12. 2. 18:40
728x90


1. 💡 Custom Pipe의 필요성

NestJS는 기본적으로 ValidationPipeParseIntPipe와 같은 내장 파이프를 제공하지만, 애플리케이션의 특정 요구사항에 맞춰 사용자 지정(Custom) 변환 또는 검증 로직이 필요할 때가 있습니다.

  • 변환: 특정 포맷의 문자열(예: YYYY-MM-DD 형식)을 특정 객체(예: CustomDateObject)로 변환해야 할 때.
  • 검증: 값이 특정 범위 내에 있거나, 데이터베이스에 존재해야 하는 등 복잡한 비즈니스 규칙을 충족하는지 검증해야 할 때.

2. 📝 Custom Pipe 구현 구조

모든 커스텀 파이프는 NestJS의 PipeTransform 인터페이스를 구현하고 @Injectable() 데코레이터를 사용해야 합니다.

728x90

2.1. PipeTransform 인터페이스

이 인터페이스는 핵심 메서드인 transform(value, metadata)를 정의합니다.

  • value: 현재 처리 중인 인자(argument)의 값입니다. (예: URL 파라미터로 받은 '123')
  • metadata: 처리 중인 인자(파라미터)에 대한 메타데이터 객체입니다. 인자의 타입(예: body, query), 인자의 이름(예: userId), 그리고 예상되는 TypeScript 타입(예: Number, String) 등의 정보를 담고 있습니다.

2.2. 구현 예시: 양수 정수 변환 및 검증 Pipe

URL 파라미터로 받은 값이 양의 정수(> 0)인지 확인하고, 정수 타입(number)으로 변환하는 파이프를 구현해 보겠습니다.

// parse-positive-int.pipe.ts
import { 
  PipeTransform, Injectable, ArgumentMetadata, BadRequestException 
} from '@nestjs/common';

@Injectable()
export class ParsePositiveIntPipe implements PipeTransform<string, number> {
  // <입력 타입, 출력 타입>
  transform(value: string, metadata: ArgumentMetadata): number {
    const num = parseInt(value, 10); // 1. 값 변환 시도

    // 💡 2. 변환 결과 검증 (정수인지 확인)
    if (isNaN(num)) {
      throw new BadRequestException(`유효성 검사 실패: "${value}"는 숫자가 아닙니다.`);
    }

    // 💡 3. 비즈니스 규칙 검증 (양수인지 확인)
    if (num <= 0) {
      // 검증 실패 시 BadRequestException을 발생시켜 요청을 중단시킵니다.
      throw new BadRequestException(`유효성 검사 실패: "${num}"은 양수여야 합니다.`);
    }

    // 4. 변환된 값 반환
    return num; 
  }
}
728x90

3. 📌 Custom Pipe 적용

커스텀 파이프는 특정 핸들러 인자에 직접 적용하여 사용합니다.

// posts.controller.ts
import { Controller, Get, Param } from '@nestjs/common';
import { ParsePositiveIntPipe } from './parse-positive-int.pipe';

@Controller('posts')
export class PostsController {
  
  // 💡 URL 파라미터 'id'에 Custom Pipe를 적용합니다.
  @Get(':id')
  findOne(@Param('id', ParsePositiveIntPipe) id: number) { 
    // 이제 이 핸들러에 도달하는 'id'는 number 타입의 양수임이 보장됩니다.
    
    console.log(typeof id); // 출력: 'number'
    return this.postsService.findOne(id);
  }
}

4. 🥇 Custom Pipe의 이점

  • 재사용성: 복잡한 데이터 변환 및 검증 로직을 여러 컨트롤러에서 쉽게 재사용할 수 있습니다.
  • 컨트롤러 코드 간소화: 복잡한 유효성 검사 및 타입 변환 로직을 컨트롤러에서 분리하여, 컨트롤러는 오직 HTTP 요청/응답 매핑에만 집중할 수 있게 합니다.
  • DI 지원: Pipe도 @Injectable()이므로, UsersService와 같은 다른 서비스를 주입받아 데이터베이스 조회 기반의 유효성 검사(예: "이메일이 이미 존재하는지")를 수행할 수도 있습니다.
728x90