Express에서 파일 업로드시 Multer
라는 패키지를 사용하였다. Nest.js 또한 Multer
를 사용하여 파일을 저장한다. 로컬에 저장하는 방법, AWS S3에 저장하는 방법 두가지를 살펴볼 것이며, 우선 이 포스트에서는 단일 파일을 Local에 저장하는 방법을 살펴본다.
공식문서 : https://docs.nestjs.com/techniques/file-upload
먼저 MulterModule을 설정한다. register
메소드를 통해 등록을 해주는데, 이 메소드에 들어가는 옵션은 Express의 Multer 옵션과 동일하니 참고하자. 필요에 따라 다른 프로바이더를 주입받아 쓸 수 있는 registerAsync
도 사용할 수 있다.
multer의 readme를 보면 아래와 같은 옵션을 지정할 수 있다.
이중 fileFilter
와 limits
는 Nest.js가 Pipe로 제공하는것이 있기에 생략하고 storage만 지정해준다.
//multer.option.ts
export default (): MulterOptions => {
return {
storage: diskStorage({
destination: (req, file, cb) => {
cb(null, `${__dirname}/../../upload`);
},
filename: (req, file, cb) => {
cb(null, `${new Date().toISOString()}_${file.originalname}`);
},
}),
};
};
// MulterModule2.ts
@Module({
imports: [MulterModule.register(multerOption())],
MulterOption의 storage에는 두가지를 설정해 주어야 한다. 파일을 저장할 기본 위치 그리고 파일 이름을 어떻게 저장할것인지를 지정해 주어야 한다.
문서 : https://github.com/expressjs/multer#storage
주의사항은 아래 두가지가 있다.
destination
에 지정한 디렉토리는 사용자가 미리 생성해 주어야 한다. 존재하지 않는 디렉토리인 경우, 오류가 난다.filename
지정시, multer는 확장자를 따로 붙여주지 않는다. 사용자가 직접 확장자를 파일 이름에 명시해 주어야 한다.이제 파일을 받는 컨트롤러를 정의한다. 기본적으로 form data
를 받는 컨트롤러이므로, Http 메소드는 POST로 지정한다.
Nest.js에서는 파일을 받기 위해서는 FileInterceptor
와 UploadedFile
을 활용한다. FileInterceptor는 UploadedFile을 활용하여, Request로 부터 파일을 추출한다
@UseInterceptors(FileInterceptor('file'))
@Post('/local')
public multerLocal(@UploadedFile() file: Express.Multer.File) {
FileInterceptor
는 두개의 매개변수를 받는다.
file
이라고 가정한다.import
하면서 정의하였기에 생략한다.파일에 대해 검증을 해야하는 경우도 있다. 가장 대표적으로 파일의 크기와, 파일의 확장자를 예로 들 수 있다. Nest.js는 파일의 크기를 검증하는MaxFileSizeValidator
, 파일의 확장자를 검증하는FileTypeValidator
총 두가지의 검증 클래스를 제공한다. FileTypeValidator
는 파일의 내용에 대한 검증을 하지 못한다는것에 유의하자.
UploadedFile()
안에 ParseFilePipe()
혹은 ParseFilePipeBuilder()
를 통해 사용할 수 있다. 다만 주의할 점은 ParseFilePipe()
를 사용하게 되면, ParseFilePipe()
의 매개변수 옵션에 validate
라는 필드에 배열형태로 Validator들을 정의해줄 수 있다.
ParseFilePipeBuilder()
를 사용하면 위의 방식과 살짝 다르다. FileTypeValidator
대신 addFileTypeValidator
메소드를, MaxFileSizeValidator
대신 addMaxSizeValidator
메소드를 사용한다.
FileTypeValidator
,addFileTypeValidator
은 옵션으로 fileType
을 받으며, 코드의 주석에 적혀있듯이 파일 이름의 정규표현식을 받는다.
MaxFileSizeValidator
와 addMaxSizeValidator
는 maxSize
옵션을 받으며, byte단위 값을 입력해주어야한다.
ParseFilePipeBuilder()
끝에는 build
메소드를 호출해 주어야 한다. 기본적으로 ParseFileOption
에서 validate
필드를 제외한 값을 받는다.
코드 원본 : https://github.com/nestjs/nest/blob/master/packages/common/pipes/file/parse-file-pipe.builder.ts
ParseFileOption
에는 아래와 같이 errorHttpStatusCode
와 exceptionFactory
를 넘겨줄 수 있다.
errorHttpStatusCode
는 Nest.js에 내장된 Http오류코드를 반환해줄 수 있으며, exceptionFactory
는 커스텀 예외객체를 반환해줄 수 있다.Nest.js 내장 Http오류는 아래 원본 코드를 참고하자.
원본 코드: https://github.com/nestjs/nest/blob/master/packages/common/utils/http-error-by-code.util.ts
원본 코드 : https://github.com/nestjs/nest/blob/master/packages/common/enums/http-status.enum.ts
직접 만들지 않겠지만, 필요에 따라 Custom File Validator를 만들 수 도 있다. 구현을 위해서는 FileValidator
추상클래스를 상속받아야 한다.
원본 코드 : https://github.com/nestjs/nest/blob/master/packages/common/pipes/file/file-validator.interface.ts
위 코드에서 나오는 IFile 타입은 아래와 같다.
@UseInterceptors(FileInterceptor('file'))
@Post('/local')
public multerLocal(
@UploadedFile(
new ParseFilePipeBuilder()
.addFileTypeValidator({
fileType: '.(jpg|png)$',
})
.addMaxSizeValidator({
maxSize: 1000000,
})
.build({
errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY,
}),
)
file: Express.Multer.File,
){
...
이 예시에서는 .jpg
,.png
로 끝나는 이미지 파일만 받으며, 파일 최대 크기는 10MB로 설정하였다. 그리고 errorHttpStatusCode를 통해 예외 메세지를 Unprocessable Entity로 설정해주었다.
이제 Postman을 통해 파일이 잘 전송/저장되는지 확인해 본다. Body
를 form-data
로 변경해준 뒤, 필드 이름을 file
로 지정한다. FileInterceptor
에 Form 이름을 file
로 해주었기 때문이다.
Request Body file
필드의 데이터타입을 File
로 변경해 주어야 한다.
지정한 경로에 잘 생성된것을 볼 수 있다.