[TIL] Nest.js Multer 23.08.09

이상훈·2023년 8월 9일
0

[내일배움캠프]

목록 보기
50/68

직전 타입스크립트 개인과제를 진행하면서 만들어 둔 업로드 미들웨어를 이번 팀프로젝트때 적용하려 했는데 form-data에 들어있는 값을 읽지 못하고, file에 location을 찾을 수 없다는 현상이 발생했다.

// upload-middleware.ts
import { HttpStatus, Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import * as multer from 'multer';
import * as multerS3 from 'multer-s3';
import * as AWS from 'aws-sdk';
import * as path from 'path';
import { uuid } from 'uuidv4';

@Injectable()
export class UploadMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    try {
      const s3 = new AWS.S3({
        accessKeyId: process.env.S3_ACCESS_KEY,
        secretAccessKey: process.env.S3_ACCESS_KEY_SECRET,
        region: process.env.AWS_REGION,
      });

      const allowedExtensions = ['.png', '.jpg', '.jpeg', '.jfif', '.exif', '.tiff', '.bmp', '.gif'];

      const upload = multer({
        storage: multerS3({
          s3,
          bucket: process.env.BUCKET_NAME,
          contentType: multerS3.AUTO_CONTENT_TYPE,
          shouldTransform: true,
          key: function (_, file, callback) {
            const fileId = uuid();
            const type = file.mimetype.split('/')[1];

            if (!allowedExtensions.includes(path.extname(file.originalname.toLowerCase())) || !file.mimetype.startsWith('image/')) {
              const errorMessage = '이미지 파일만 업로드가 가능합니다.';
              const errorResponse = { errorMessage };
              return res.status(HttpStatus.BAD_REQUEST).json({ errorResponse });
            }

            const fileName = `${fileId}.${type}`;
            callback(null, fileName);
          },

          acl: 'public-read-write',
          limit: { fileSize: 5 * 1024 * 1024 },
        }),
      });
      upload.single('newFile')(req, res, next);
    } catch (error) {
      console.error(error);
      res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ errorMessage: '파일 업로드 에러' });
    }
  }
}

이게 직전 프로젝트에서 사용하던 것인데 location을 찾을 수 없다는 에러는 직접 index.d.ts파일을 만들어서 type을 지정해주고 해결했었던 문제였다.

//index.d.ts
declare namespace Express {
  export interface Request {
    file: Express.Multer.File;
    user: { userId: number; isAdmin: boolean };
  }
}

그래서 이번에도 타입을 직접 지정해줘야하나 싶어 추가해봤는데 추가를 해도 계속 Multer를 찾을 수 없다는 에러가 발생하여 이것저것 찾아보다 팀원분에게 여쭤보고 해결방법을 찾았다.

// 내가 적용했던 코드
import { Request } from 'express';
import { User } from '../entities/user.entity';

export interface IRequest extends Request {
  user?: User;
  file: Express.Multer.File
}

이 부분에서 처음엔 Multer를 찾을 수 없다고 나오다가 vscode를 껐다키니 Request를 확장하여 사용할 수 없다는 에러가 발생하여 아래와 같이 수정하여 해결

import { Request } from 'express';
import { User } from '../entities/user.entity';

export interface IRequest extends Request {
  user?: User;
  file: any;
}

먼저 file을 any타입으로 변경해주고

// upload-middleware.ts
key: function (_, file: Express.Multer.File, callback) {
            const fileId = uuid();
            const type = file.mimetype.split('/')[1];

업로드 미들웨어의 file을 Express.Multer.File로 직접 넣어주니 req.file.location을 읽어올 수 있었다.

다음은 form-data의 값을 가져올 수 없는 문제가 있어서 nest-form-data 패키지를 다운받고 데코레이터를 적용했더니 unexpected end of form이라는 오류가 발생했다.
stackoverflow에 나와 같은 문제를 겪은 사람이 많아 댓글을 읽어보니 multer의 버전을 1.4.3을 사용하면 된다해서 @types/multer의 버전을 1.4.3으로 다운그레이드 후 nest-form-data패키지를 삭제하고 실행해보니 정상작동했다.

profile
코린이

1개의 댓글

comment-user-thumbnail
2023년 8월 9일

이렇게 유용한 정보를 공유해주셔서 감사합니다.

답글 달기