낙서장이자 오답 노트이자 컨닝 페이퍼

패스트캠퍼스 환급챌린지 23일차 : 코드팩토리의 백엔드 아카데미 : 한 번에 끝내는 NestJS 패키지 - 기초부터 MSA까지 강의 후기 본문

NestJS

패스트캠퍼스 환급챌린지 23일차 : 코드팩토리의 백엔드 아카데미 : 한 번에 끝내는 NestJS 패키지 - 기초부터 MSA까지 강의 후기

NangIn 2025. 4. 23. 21:03

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.

 

강의 내용 정리   

 

기본 Pipe 사용해보기

 

파이프의 적용 우선순위 

  • Global Pipe
    • → main.ts에서 app.useGlobalPipes()로 등록 → 전체 라우트에 적용
  • Controller Pipe → 잘 안씀
    • → @UsePipes()를 컨트롤러 상단에 작성 → 해당 컨트롤러의 모든 핸들러에 적용
  • Route Pipe → 잘 안씀
    • → @UsePipes()를 메서드 단위에 작성 → 해당 라우트에만 적용
  • Parameter Pipe → 가장 흔히 사용됨
    • → 파라미터 단위로 변환·검증 수행

 

파라미터 Pipe 적용 이유

  • 서버 에러 예방:
    • 클라이언트가 숫자 대신 문자열 등 이상한 값을 보낼 경우, 서버에서 예외 발생
    • → ParseIntPipe 등으로 유효성 검사하면 BadRequestException으로 방어 가능
  • 실제 요청 예시:
    • /movies/1 → 정상
    • /movies/test → 서버 에러 발생 (예: NaN 에러)

 

기본 제공 파이프 실습 

파이프명 기능  사용 예
ParseIntPipe 문자열 → 정수 변환, 실패 시 예외 발생 @Param('id', ParseIntPipe)
ParseFloatPipe 문자열 → 실수 변환 @Param('id', ParseFloatPipe)
ParseBoolPipe 문자열 → boolean으로 변환 (true, false) @Param('flag', ParseBoolPipe)
ParseArrayPipe 문자열을 배열로 해석 (a,b,c → ['a','b','c']) @Query('ids', new ParseArrayPipe({ items: Number }))
ParseUUIDPipe UUID 형식인지 검증 @Param('uuid', ParseUUIDPipe)
ParseEnumPipe enum 값인지 검증 @Param('type', new ParseEnumPipe(MyEnum))
DefaultValuePipe 값이 없을 경우 기본값 지정 @Query('page', new DefaultValuePipe(1))

 

에러 메시지 커스터마이징 (ParseIntPipe 예시)

@Param('id', new ParseIntPipe({
  exceptionFactory: () => new BadRequestException('숫자를 입력해주세요.')
}))
  • exceptionFactory: 사용자 정의 에러 메시지 처리 함수
  • 기본 예외 메시지보다 더 직관적으로 사용자에게 안내 가능

 

Pipe 변환 확인 실습 예시

@Get(':id')
getMovie(@Param('id', ParseIntPipe) id: number) {
  console.log(typeof id); // 'number' 출력
}
  • 파이프 미적용 시: typeof id === 'string'
  • 파이프 적용 시: typeof id === 'number'

 

ParseEnumPipe 실습

enum CarBrand {
  Lamborghini = '람보르기니',
  Ferrari = '페라리',
}

@Get(':brand')
getBrand(@Param('brand', new ParseEnumPipe(CarBrand)) brand: CarBrand) {
  // CarBrand enum 값만 허용됨
}
  • 람보르기니, 페라리는 허용
  • 포르쉐 등 다른 값 → BadRequestException

 

DefaultValuePipe 실습

@Get()
getList(@Query('page', new DefaultValuePipe(1)) page: number) {
  // page가 없으면 기본값 1 사용
}
  • ?page=2 → 2 전달
  • ?page= 또는 쿼리 없음 → 1 사용

 

파일 파이프는 사용 안 함

  • ParseFilePipe는 사용하지 않음
  • multer 등의 라이브러리를 활용하여 파일 업로드 처리 예정

 

프로젝트 Pipe 적용하기

 

누락된 전제 조건: 인터셉터 누락 확인

  • 클래스 밸리데이터를 사용하려면 ValidationPipe가 필요
  • 그런데 ValidationPipe는 TransformInterceptor가 함께 있어야
    • → DTO 변환을 보장함
  • 해결:
    • GenreController, DirectorController에도 @UseInterceptors(TransformInterceptor) 명시
    • 기존 MovieController에는 이미 적용되어 있었음

 

모든 라우트에 ParseIntPipe 일괄 적용

  • 목적:
    • URL 파라미터(@Param)에 문자열이 아닌 숫자만 들어오도록 강제
    • 숫자가 아닌 값이 들어올 경우 BadRequestException 발생
  • 적용 대상
    • MovieController
      • @Get(':id'), @Patch(':id'), @Delete(':id') 등
    • GenreController, DirectorController
      • 각 ID 기반 라우트 (조회, 수정, 삭제 등)
  • 적용 방식
    • @Get(':id')
      findOne(@Param('id', ParseIntPipe) id: number) {
        ...
      }
      
      • number 타입 명시 생략 가능하지만, 명시해주는 것이 명확성에 좋음

 

Custom Validation Pipe

 

왜 커스텀 파이프를 만들어야 하나요?

  • 기본 제공되는 파이프들(ParseIntPipe, ValidationPipe, ParseBoolPipe 등)은 매우 유용함
  • 하지만 특정 도메인 로직에 맞춘 맞춤형 유효성 검사가 필요할 때가 있음
  • 예시: 영화 제목은 무조건 3글자 이상이어야 한다는 비즈니스 규칙 → 기본 파이프 X

 

파일 구조 및 구현 흐름

  1. 파일 생성
    • movie/pipes/movie-title-validation.pipe.ts
  2. 클래스 선언 및 데코레이터
    • @Injectable()
      export class MovieTitleValidationPipe implements PipeTransform<string, string> {
      
    • @Injectable()로 Nest 컨테이너에 등록
    • PipeTransform<InputType, OutputType> 인터페이스 구현
  3. 핵심 로직: transform() 메서드 구현
    • transform(value: string, metadat): string {
        if (value.length <= 2) {
          throw new BadRequestException('영화의 제목은 세 자 이상 작성해주세요.');
        }
        return value;
      }
      

 

컨트롤러에서 사용하기

  • 컨트롤러의 @Query() 또는 @Param()에 파이프 적용
    • @Get()
      findByTitle(@Query('title', MovieTitleValidationPipe) title: string) {
        ...
      }
      
  • 위와 같이 적용하면 클라이언트가 쿼리로 title=다크라고 보내면 통과되지만, title=다는 거절됨

 

파이프 내부의 Metadata 활용법

  • transform(value, metadata) → 2번째 인자 metadata로 다양한 정보 접근 가능  
    프로퍼티 설명
    type body / query / param 구분 가능
    metatype 해당 파라미터가 선언된 타입 (예: String, Number)
    data @Param('id', new CustomPipe('test'))의 'test'에 해당하는 키 값
  • 조건 분기, 로깅, 리팩토링 등에서 유용

 

에러 메시지 커스터마이징 예시

  • 사용자 친화적인 에러 메시지를 제공 가능
  • 단순히 throw new BadRequestException(...) 활용

 

MovieTitleValidationPipe 버그 수정

 

발견된 버그 개요

  • 커스텀 파이프인 MovieTitleValidationPipe가 쿼리 파라미터 title이 아예 없는 경우에도 무조건 실행
  • 따라서 title을 입력하지 않고 요청하면 길이 검증 로직에서 예외 발생
  • 기대 동작: title이 없는 경우엔 검증하지 않고 통과(Pass) 되어야 함

 

해결 방법

  • transform() 함수 내부에 아래와 같은 입력 존재 여부 검사 추가:
    • transform(value: string): string {
        // ✅ 쿼리 값이 아예 없으면 그냥 통과
        if (!value) return value;
      
        if (value.length <= 2) {
          throw new BadRequestException('영화의 제목은 세 자 이상 작성해주세요.');
        }
      
        return value;
      }
      
  • !value 조건으로 null, undefined, 빈 문자열 등 모든 falsy 값을 검사하여 early return 처리

 

적용 후 확인 사항

  • title 파라미터 생략한 경우 → 정상 요청 처리됨
  • title=Ri → "영화 제목은 세 자 이상" 에러 반환
  • title=Inception → 필터링 정상 동작

 

학습 후기         

 

NestJS의 파이프는 단순한 미들웨어로 생각하기 쉬웠지만, 이번 강의를 통해 요청 데이터를 가공하고 컨트롤러에 전달하기 전 핵심적인 전처리 도구라는 것을 제대로 이해하게 되었습니다. 특히 ParseIntPipe, ParseBoolPipe, ParseEnumPipe 등 기본적으로 제공되는 파이프들만으로도 웬만한 유효성 검증이나 타입 변환은 손쉽게 처리할 수 있었습니다. 그중에서도 파라미터 단위에 적용하는 파이프는 실무에서 가장 빈번하게 사용될 수밖에 없는 위치에 있다는 점이 인상 깊었습니다.

 

예를 들어 클라이언트가 /movies/test와 같은 잘못된 요청을 보냈을 때, 서버 내부에서 예기치 않은 에러가 발생해버리는 경우를 ParseIntPipe 하나로 손쉽게 방어할 수 있었습니다. 단순한 타입 변환 이상의 의미를 가지는 파이프는 NestJS에서 신뢰성 있는 API 서버를 구성하는 데 반드시 필요한 보호 장치라는 생각이 들었습니다. 또한 전역으로 적용하는 ValidationPipe와 함께 DTO를 검증하고, 파라미터에 Parse 계열의 파이프를 적용하는 구조는 코드의 명확성과 안정성을 동시에 확보할 수 있는 좋은 패턴이라고 느꼈습니다.

 

커스텀 파이프를 직접 만들어 본 것도 중요한 경험이었습니다. 특정 유즈케이스, 예컨대 영화 제목은 반드시 세 글자 이상이어야 한다는 도메인 규칙을 적용하는 데 기본 파이프만으로는 한계가 있었고, 이럴 때 커스텀 파이프를 만들어 적용하는 방식이 얼마나 유연하면서도 구조적인지 실감할 수 있었습니다. 처음엔 값이 아예 전달되지 않았을 때도 검증이 실행되는 버그가 있었지만, !value 체크를 통해 간단하게 해결할 수 있었고, 이런 예외 케이스를 직접 다뤄보면서 실무에서는 예상하지 못한 입력값까지 고려하는 것이 얼마나 중요한지도 함께 배울 수 있었습니다.

 

결론적으로 파이프는 단순한 필터링 수준을 넘어서, NestJS가 지향하는 구조적이고 명확한 서버 아키텍처를 구현하는 데 꼭 필요한 장치였습니다. 실무에서도 필수적으로 다뤄야 할 개념이며, 특히 커스텀 파이프를 통한 도메인 로직의 유효성 검증은 프론트엔드와의 계약을 지키고 사용자 경험을 보호하는 데 큰 역할을 할 수 있다고 느꼈습니다.

 

학습 인증샷       

                       

수강 인증 사진
학습 인증샷
공부 시작 시간
공부 종료 시간

https://abit.ly/lisbva