NestJS Custom Validation Pipe 구현 일지

정비호·2024년 5월 30일

Troubleshooting

목록 보기
7/8

제목은 거창해 보이지만 사실 단순 ValidationPipe의 option을 재정의할 수 있는 CustomValidationPipe이다.

발단

사이드 프로젝트에서 파일 업로드 API를 구현할 때의 이야기이다.
File Upload API를 구현하면서 생긴 일 (feat. TSID, BigInt)
해당 글과 같은 시점이다.
우리 프로젝트에서 ValidationError의 경우 Global 레벨로 적용한 ValidationPipe의 exceptionFactory를 이용해서 custom한 error를 던지고 있었다.

errors: validationErrors.flatMap((validationError) => {
          return {
            property: validationError.property,
            value: validationError.value,
            reason: Object.values(validationError.constraints)[0] || '',
          };
        }),
  // 대충 이런 식.

에러가 발생한 value까지 그대로 출력하고 있었는데, 여기서 예상치 못한 변수가 발생한다.

에러의 일부이다.
스크롤이 끝도 없다.
swagger를 통해 request를 보냈을 때 swagger가 멈춰 버리길래 '뭐지..?' 했는데 이런 문제가 있을 줄은 꿈에도 몰랐다.
그리고 단순히
'음 그러면 해당 요청에선 Validation option을 재정의 하면 되겠다!!'
라고 생각했는데 해당 router 레벨에서 Validation option을 재정의해도 적용이 되지 않았다..
참조 링크1
그리고 나는 바로 구글링을 시작했다.

문제 해결

위의 참조 링크1과 같은 이슈에서 나온 해결 방안인데, 흐름은 이렇다.

import { REWRITE_VALIDATION_OPTIONS } from './constants';
import { ValidatorOptions } from '@nestjs/common/interfaces/external/validator-options.interface';
import { SetMetadata } from '@nestjs/common';
export function RewriteValidationOptions(options: ValidatorOptions) {
  return SetMetadata(REWRITE_VALIDATION_OPTIONS, options);
}
// extends global pipe
import { ValidationPipe, ArgumentMetadata, Injectable } from '@nestjs/common';
import { REWRITE_VALIDATION_OPTIONS } from '../decorators/constants';
@Injectable()
export class MyValidationPipe extends ValidationPipe {
  async transform(value: any, metadata: ArgumentMetadata) {
    const options = Reflect.getMetadata(REWRITE_VALIDATION_OPTIONS, metadata.metatype);
    let originOptions;
    if (options) {
      originOptions = Object.assign({}, this.validatorOptions);
      this.validatorOptions = Object.assign(this.validatorOptions, options);
    }
    try {
      const result = super.transform(value, metadata);
      if (originOptions) {
        this.validatorOptions = originOptions;
      }
      return result;
    } catch (error) {
      throw error;
    }
  }
}
  1. ValidationOption을 재정의 하고 싶은 Dto에 재정의 할 options를 받는 데코레이터를 달아준다.
  2. 그럼 해당 데코레이터에서 parameter로 받은 options를 키와 함께 메타데이터로 등록한다.
  3. CustomValidationPipe(위 예시 코드에선 MyValidationPipe) 에서 해당 키값으로 등록된 options를 확인하는 절차를 거친다.
  4. 새로 정의된 options이 있다면 해당 option을 통한 validation을 진행한다.

아무튼 CustomValidationPipe 및 데코레이터를 새로 만들어서 file upload 시에 사용되는 Dto에서는 { validationError: { value: false } }로 option을 재정의 해주었고 클라이언트 측에서 response를 받다가 터져버리는 일을 방지할 수 있게 되었다.

profile
잘하고 싶은 개발자

0개의 댓글