프로젝트를 진행하면서 여러 장의 이미지를 업로드하는 기능을 구현했는데 복습하면서 정리를 해보도록 하겠다
처음에 단일 이미지를 업로드할 때 nest-form-data 패키지를 사용해서 이미지를 업로드했다
nest-form-data 패키지를 사용한 이유는 DTO로 file를 validation 하기 편해서 사용했다 하지만 여러 장의 이미지를 다루면서
오히려 DTO에서 validation 하면서 정상적으로 이미지를 가져오지 못하는 문제가 발생했다 해결하기 매우 힘들었고 결국 해결하지 못했다 그 이유는 정보가 매우 부족했기 때문이다 Nest.js 공식 홈페이지에서 file을 다룰 때 Multer를 사용하다 보니 nest-form-data보다 Multer를 활요하는 경우가 많아 정보가 없는 듯하다
그렇기 때문에 nest-form-data에서 Multer를 사용하는 방법으로 수정하기로 결정했다
Multer는 Express.js에서 파일을 다룰 때 사용하는 대표적인 패키지이다 Nest.js에서는 내장 모듈로 Multer를 지원한다
Multer를 사용해서 이미지를 다뤄보도록 하자
TypeScript에서 Multer를 사용하려면 @types/multer를 설치해야 한다
npm install @types/multer --save-dev
multerOptionsFactory를 만들자
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
import { diskStorage } from 'multer';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
export const multerOptionsFactory = (): MulterOptions => {
return {
storage: diskStorage({
filename(req, file, cb) {
const ext = path.extname(file.originalname);
cb(null, `${uuidv4()}-${new Date().getTime()}${ext}`);
},
}),
};
};
Multer모듈에 설정하기 위해 따로 만들어 관리하기 쉽도록 했다
storage는 diskStorage로 설정하고 file의 이름을 uuid를 이용해 중복이 없는 파일이름으로 변경 후 파일을 생성합니다
storage옵션에 memoryStorage도 있다 파일을 메모리에 저장한다 서버의 메모리를 사용하기 때문에 이미지가 큰 파일을 여러 개 메모리에 저장 후 사용하게 되면 위험하다고 생각해 디스크에 저장하는 diskStorage옵션을 사용하기로 했다
이제 MulterModule을 설정해 보자
MulterModule.registerAsync({
imports: [ConfigModule],
useFactory: multerOptionsFactory,
inject: [ConfigService],
}),
이제 Controller를 가서 이미지를 받아보도록 하자
Post('/collections/:collectionId/photospots')
@UserGuard
@UseInterceptors(FilesInterceptor('files'))
async createPhotospot(
@UploadedFiles(new FileVaildationPipe('create')) files: Express.Multer.File[],
@Body() createPhtospotDto: CreatePhotospotDto,
@Param('collectionId') collectionId: number,
@InjectUser('id') userId: number
): Promise<void> {
await this.photospotService.createPhotospot(createPhtospotDto, files, userId, collectionId);
}
요청을 가로채는@UseInterceptors를 @FilesInterceptor와 같이 이용해서 요청에 files를 처리할 수 있다
그리고 @UploadedFiles를 이용해서 Express.Multer.file[] 타입으로 파일을 받는다
단일 이미지를 다룰 때 nest-form-data를 사용한 이유가 validation을 하기 위해서였다 Multer에서도 validation을 해야 한다
커스텀 파이프를 이용해서 파일을 확인해 보자 그전에 new 키워드를 이용해 'create' 인자를 집어넣고 있는데 이유는
'modify'와 'create' 두 기능이 조금 다른 validation을 해야 하기 때문에 확인용으로 인자를 집어넣는다고 보면 된다
file validation을 통과하면 files에 이미지들이 담긴다
커스텀 파이프인 FileValidationPipe를 만들어보자
@Injectable()
export class FileVaildationPipe implements PipeTransform {
constructor(private readonly mode: string){}
transform(value: any, metadata: ArgumentMetadata) {
const imageType = ['IMAGE/PNG', 'IMAGE/JPEG', 'IMAGE/JPG'];
if (_.isEmpty(value)) {
if (this.mode === 'create') {
throw new BadRequestException('이미지 파일을 입력하셔야 합니다.');
} else if (this.mode === 'modify'){
return [];
}
}
if (value.length > 5) {
throw new BadRequestException('이미지 파일은 최대 5장만 가능합니다.');
}
value.forEach((val: any) => {
if (!imageType.includes(val.mimetype.toUpperCase())) {
throw new BadRequestException('이미지 파일만 업로드 할 수 있습니다.');
}
})
return value;
}
}
생성자로 mode를 받는다 'create' 또는 'modify'가 들어온다
mode와 imageType 그리고 파일 개수를 확인해서 예외처리를 진행한다
'과거공부모음' 카테고리의 다른 글
Nest.js에서 Google Vision API 사용하기 (0) | 2023.04.01 |
---|---|
Nest.js에서 이미지를 S3에 업로드 (0) | 2023.04.01 |
20230324 TIL - 사진 리사이징 (0) | 2023.03.25 |
20230321 TIL - 구글 비전을 이용한 이미지 라벨링 (0) | 2023.03.21 |
20230309 TIL - 여러장의 이미지 업로드 (0) | 2023.03.09 |