728x90

MulterModule 자체는 파일 크기 제한(limits)과 간단한 MIME 타입 필터링(fileFilter)을 제공하지만, 이들은 오류 발생 시 NestJS의 예외 처리 시스템과 완벽하게 통합되지 않아 사용자에게 명확한 메시지를 전달하기 어렵습니다.
NestJS에서는 ParseFilePipe와 Validator를 사용하여 파일 업로드의 유효성 검사를 보다 선언적이고 NestJS답게 처리합니다.
1. 🛠️ ParseFilePipe 소개 및 사용
ParseFilePipe는 NestJS의 내장 파이프 중 하나로, UploadedFile 데코레이터와 함께 사용되어 업로드된 파일의 유효성을 검사합니다. 이 파이프는 여러 개의 유효성 검사기(Validator)를 체인처럼 연결하여 사용할 수 있습니다.
1.1. 파일 크기 검사 (MaxFileSizeValidator)
파일의 크기가 지정된 바이트를 초과하는지 검사합니다.
// src/users/users.controller.ts (일부)
import { Post, UseInterceptors, UploadedFile, ParseFilePipe, MaxFileSizeValidator } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
@Post('avatar-size-check')
@UseInterceptors(FileInterceptor('avatar'))
uploadAvatarSizeCheck(
@UploadedFile(
new ParseFilePipe({
// 💡 파일 크기 제한 (1MB = 1024 * 1024 bytes)
validators: [
new MaxFileSizeValidator({ maxSize: 1024 * 1024, message: '파일 크기는 1MB를 초과할 수 없습니다.' }),
],
// 💡 파일이 필수가 아닌 경우 (optional)
fileIsRequired: false,
}),
)
file: Express.Multer.File,
) {
return file ? { message: `파일 크기 검사 통과: ${file.size} bytes` } : { message: '파일 없음' };
}
1.2. 파일 타입 검사 (FileTypeValidator)
파일의 MIME 타입이 지정된 정규식과 일치하는지 검사합니다.
// src/users/users.controller.ts (일부)
import { FileTypeValidator } from '@nestjs/common';
@Post('avatar-type-check')
@UseInterceptors(FileInterceptor('avatar'))
uploadAvatarTypeCheck(
@UploadedFile(
new ParseFilePipe({
validators: [
// 💡 JPEG와 PNG 이미지 파일만 허용 (정규식 사용)
new FileTypeValidator({ fileType: 'image/(jpeg|png)' }),
],
// fileIsRequired: true (기본값)
}),
)
file: Express.Multer.File,
) {
return { message: `파일 타입 검사 통과: ${file.mimetype}` };
}
2. 🛡️ 커스텀 파일 유효성 검사기 구현
내장 Validator만으로는 복잡한 검사 로직(예: 이미지의 종횡비, 특정 파일 헤더 검사)을 수행하기 어렵습니다. 이 경우, NestJS의 Validator 인터페이스를 구현하는 커스텀 클래스를 생성합니다.
2.1. 커스텀 Validator 클래스 생성
파일의 확장자가 특정 목록에 포함되는지 확인하는 커스텀 Validator를 만들어 보겠습니다.
import { FileValidator, Injectable } from '@nestjs/common';
// 💡 FileValidator 인터페이스를 구현하여 커스텀 Validator 생성
@Injectable()
export class FileExtValidator extends FileValidator {
constructor(private readonly allowedExtensions: string[]) {
// 💡 부모 클래스에 Validator 이름과 옵션을 전달
super({ validator: 'FileExtValidator' });
}
// 💡 유효성 검사 로직
isValid(file: Express.Multer.File): boolean {
if (!file || !file.originalname) {
return false;
}
const fileExtension = file.originalname.split('.').pop().toLowerCase();
// 💡 허용된 확장자 목록에 포함되어 있는지 확인
return this.allowedExtensions.includes(fileExtension);
}
// 💡 유효성 검사에 실패했을 때 반환할 오류 메시지
buildErrorMessage(): string {
return `허용되지 않은 파일 확장자입니다. 허용 목록: ${this.allowedExtensions.join(', ')}`;
}
}
2.2. 컨트롤러에서 커스텀 Validator 사용
커스텀 Validator를 ParseFilePipe에 배열로 전달하여 사용합니다.
// src/users/users.controller.ts (최종)
import { FileExtValidator } from '../common/pipes/file-ext-validator'; // 💡 커스텀 Validator 임포트
@Post('custom-validator-check')
@UseInterceptors(FileInterceptor('document'))
uploadDocument(
@UploadedFile(
new ParseFilePipe({
validators: [
// 💡 50KB 크기 제한
new MaxFileSizeValidator({ maxSize: 50 * 1024, message: '파일 크기는 50KB를 초과할 수 없습니다.' }),
// 💡 커스텀 Validator 적용: PDF와 DOCX 확장자만 허용
new FileExtValidator(['pdf', 'docx']),
],
}),
)
file: Express.Multer.File,
) {
return { message: '파일 유효성 검사 완료', originalname: file.originalname };
}
3. 🥇 Validation Pipe 사용의 이점
- 명확한 에러 메시지: 검증 실패 시, NestJS의 HttpException (Status 400 Bad Request)과 함께 buildErrorMessage에서 정의한 명확한 메시지를 반환하여 사용자에게 오류 원인을 정확히 알려줄 수 있습니다.
- DI 시스템 통합: 커스텀 Validator는 FileValidator를 확장하므로, 필요하다면 constructor를 통해 ConfigService나 다른 서비스들을 주입받아 동적으로 검증 로직(예: DB에 저장된 설정값 기반의 파일 크기 제한 등)을 구성할 수 있습니다.
728x90
'Nest.js를 배워보자 > 9. NestJS에서 파일 업로드 & 관리 — S3, 로컬 저장소' 카테고리의 다른 글
| 대용량 파일 처리 전략 (0) | 2025.12.02 |
|---|---|
| AWS S3 업로드: 클라우드 파일 관리 (0) | 2025.12.02 |
| NestJS 파일 업로드: MulterModule 설정 (0) | 2025.12.02 |