Multer-S3를 이용한 사진업로드

Jiyoung·2021년 5월 25일
0

오늘은 하루종일 Multer모듈을 이용한 사진업로드 기능구현에 시간을 쏟았다. 사실 Multer는 퍼스트 프로젝트 때 사용한 적이 있지만, 다른 팀원분이 구현하고 나는 이해만 하고 넘어갔던지라 막상 직접 구현하려고 하니 처음엔 조금 어려웠다. 역시 직접 구현해봐야 기억에 오래 남는가보다.

사진업로드는 먼저 로컬 서버에 폴더를 만들어 업로드하는 방법AWS S3버킷 내 폴더에 업로드하는 방법이 있다.


Multer

Multer는 npm 패키지를 이용하여 설치한 후, uploads와 같이 사진이 업로드 될 폴더를 만들고 이를 destination으로 설정해주면 uploads폴더에 사진 파일이 저장된다.

const path = require('path');
const multer = require('multer');

const upload = multer({
    storage: multer.diskStorage({
        destination: (req, file, cb) => {
            cb(null, './uploads/');
        }, //uploads폴더에 사진이 업로드 됨
        filename: (req, file, cb) => {
            const ext = path.extname(file.originalname);
            cb(null, path.basename(file.originalname, ext) + Date.now() + ext);
        },
    }),
    limits: { fileSize: 5 * 1024 * 1024 },
}); // 이미지 업로드 로컬 테스트용(업로드 시 로컬 서버에 이미지 파일이 저장됨)

exports.upload = upload;

라우터는 사진을 한장씩만 올릴 수 있도록 upload.single메소드를 사용하였다.

// * POST /diaries/upload
router.post("/diaries/upload", upload.single('img'), async (req, res) => {
        const picUrl = await req.file.filename
        console.log(req.file)
        res.json({ picUrl: picUrl, message: '그림이 등록되었습니다.' })
    }); // 이미지 업로드 로컬 테스트용

req.file은 아래와 같이 업로드 된 사진의 정보를 담고 있는 곳으로, 나는 사진 파일명이 DB의 picUrl칼럼에 저장되게 하기 위해 picUrl의 value로 req.file.filename을 가져왔다.


라우터를 설정할 때 한가지 중요한 점이 있는데, 예를 들어 나 같은 경우엔 그림일기를 작성하는 기능을 구현하기 위해 1)먼저 그림을 그려서 업로드한 후, 2)밑에 일기 내용을 작성하여 완료 버튼을 클릭하면 그림일기 1개가 작성될 수 있도록 하는 방식을 생각했는데, 처음에는 1번과 2번의 라우터를 동일하게 작성하였더니 업로드가 계속 안됐었다. 알고보니 각각의 라우터를 다르게 작성해주어야 된다는 것..!! 1번은 /diaries/upload, 2번은 /diaries이런 식으로 말이다. 이것때메 또 한참 헤맸다😭


Multer-S3

Multer-S3는 AWS S3버킷 내 폴더에 사진을 업로드하는 방식인데 서비스를 배포하여 사용하려면 로컬서버가 아닌 S3에 사진이 저장될 수 있도록 해야한다. 처음에는 이 개념(?)이 잘 이해가 안됐었는데 구현하고 테스트해보니 저장 경로의 차이일 뿐 Multer를 이용한 구현과 큰 차이가 없었다.

S3버킷 정보(accessKeysecretAccessKey)를 환경변수로 입력하고 이것을 storage부분에 넣어주기만 하면 된다. 그리고 key에서 cb(null, `uploads/${Date.now()}_${file.originalname}`) 이 부분에 uploads와 같이 S3버킷에 생성한 폴더명을 넣어주면 된다. 한가지 기능이 아닌 여러 기능에 사진업로드가 필요하다면 각각 다른 폴더명을 지정해주면 된다.

const multerS3 = require('multer-s3');
const aws = require('aws-sdk');

const s3 = new aws.S3({
    accessKeyId: process.env.AWS_ACCESS_KEY,
    secretAccessKey: process.env.AWS_SECRET_KEY,
    region: process.env.AWS_S3_REGION,
});

const upload = multer({
    storage: multerS3({
        s3: s3,
        bucket: 'picanote.me',
        contentType: multerS3.AUTO_CONTENT_TYPE,
        acl: 'public-read',
        key: function (req, file, cb) {
            cb(null, `uploads/${Date.now()}_${file.originalname}`)
        }
    })
}) // S3로 이미지 업로드(그림일기)

exports.upload = upload;

라우터 설정도 Multer와 비슷한데 여기서는 파일명이 아닌 사진파일의 URL이 DB에 저장될 수 있도록 picUrl값을 req.file.location로 가져왔다.

// * POST /diaries/upload
router.post("/diaries/upload", upload.single('img'), async (req, res) => {
    const picUrl = await req.file.location // 이미지 URL 정보가 담긴 곳
    console.log(req.file)
    res.json({ picUrl: picUrl, message: '그림이 등록되었습니다.' })
}); // S3에 이미지 업로드 라우터


포스트맨으로 요청하면 아래와 같이 사진파일이 업로드 된 URL이 나온다. uploads폴더명 뒤에 파일명이 찍힌 걸 확인할 수 있다.


그리고 AWS S3버킷으로 들어가보면 ta-da!
아래처럼 사진들이 업로드 된 것을 확인할 수 있다. 신기신기😎👏


오류 발생 및 해결

포스트맨으로 form-data입력 시 Can't select file at form-data와 같은 오류가 발생했던 적이 있다.


조금 막막하던 차에 아래의 글을 발견! 포스트맨 설정 상의 Working directory에 있는 폴더명(Postman)과 실제 내 컴퓨터에 있는 폴더명(Postman Agent)이 달라서 생긴 오류였다. 따라서 내 컴퓨터 폴더명을 Postman으로 수정해주었더니 해결되었다.


참고 자료:

profile
경계를 넘는 삶

0개의 댓글