[ 주의 ] 삽질하면서 코딩한 경험을 바탕으로 작성한 것임으로 틀린것도 많으니 더 좋은 정보가 있으면 알려주세요!

[ 잡담 ]

요즘들어 새로 시작한 프로젝트가 있어서 12시에 기상해서 새벽 4~6시까지 코딩만 죽어라 하고 있다.

실제로 유저들에게 상용시킬 서비스를 제작하는 것이란 대학교 때 졸업작품을 개발하는 것과는 비교도 안될 정도로 많은 변수를 생각해야하기 때문에 시간이 많이 든다.

덕분에 정말 많은 삽질을 하고 있다. 하지만 이 삽질 끝에 문제를 해결하고 잘 작동하는 모습을 보면 너무 너무 재밌다.

그럼 본론으로 들어가 Multer에 대해서 알아보겠다.

+ 지금까지 Multer를 사용하면서 겪은 3가지 삽질

[ Multer ]

정의: Multersms Multypart / form-data 형식의 Request을 다루기 위한 Node.js 미들웨어이다.

설치

> npm install --save multer

1. Formdata 받기

나는 처음에 Multer의 존재를 몰랐다. 프론트에서 Formdata를 axios로 넘겨줬지만 req.body, res.file, res.files 전부 console.log로 찍어봤지만 빈값만 들어가 있었다. 이게 새벽 2~4시까지 골머리를 썩혔다.

찾다 찾다보니 Multer라는 친구를 발견했고 아래와 같이 작성해주면 formdata 값을 받을 수 있다고 해서 적용해 봤다.

1) single

  • 파일 한 개만 받을 경우에 사용한다.
  • req.file에 이미지 데이터가 담겨있다.
  • avatar는 formdata.append("avatar": value); 에서 value의 key값을 의미한다.
app.post('/profile', upload.single('avatar'), function (req, res, next) {})

2) array

  • 여러개의 이미지 파일을 하나의 key값으로 받아올 수 있다.
  • 구분없이 여러개의 이미지를 받을 때 좋은듯?
  • req.files에 데이터가 담겨있다.
  • 12 숫자는 넘겨 받는 이미지 갯 수를 나타낸다. 한계를 정해두고싶지 않다면 그냥 key값만 적으면 된다.
app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {})

3) fields

  • 전체적으로 array와 큰 차이를 모르겠다. 확장 버전인듯?
  • key값을 구분하여 데이터를 분류해서 받고 싶다면 사용하자.
  • req.file에 데이터가 담겨있다.
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/cool-profile', cpUpload, function (req, res, next) {})

4) none

  • 안써봐서 몰라.
  • text만 담긴 formdata 받으라고 한다.

5) any

  • 마찬가지로 안써봄.
  • 유선으로 오는 모든 파일 다 받을 수 있다고 적혀잇다.

2. 그래도 값이 넘어오지 않는 경우

자! 열심히 알아봤다. 이제 받아져야지? 라고 생각한 나의 실수였다. 계속해서 데이터가 넘어오지 않았다. 여기서 삽질 또 시작.

문제는 프론트의 axios에 있었다. axios를 사용하면 받아지지 않았지만 아래의 코드를 사용하니 값이 넘어왔다.

var req = new XMLHttpRequest()
var file = new Blob(['This is a test'], { type: 'text/plain' })
var data = new FormData()

data.append('photo', file, 'test.txt')

req.open('POST', '/upload')
req.send(data)

3. 저장 폴더를 유동적으로 바꾸기

난 aws s3에 이미지 파일을 저장해야했기 때문에 multer-s3도 사용했다.

multer의 구조는 좀 이상했다. 프론트로 받은 데이터를 기반으로 이미지가 저장될 폴더를 만들고 싶었지만 

app.post('/image', s3.upload.single('avatar'), function (req, res, next) {})

이렇게 req로 받기도 전에 파일에 저장을 때리는데 어떻게 하란 말인가...

그래서 아래와 같은 방법으로 파일명을 고쳐보기로 했다.

/* router file */

const express = require('express');
const router = express.Router();
const ctrl = require('../api/c_board');
const multer = require("multer");
const {new_s3_storage} = require("../utils/u_s3_storage");


// s3를 빈 값으로 선언했더니 아래 라우터에서 빈 값에 
// array() 함수가 없다고 에러가 떠서 저장 폴더가 고정된 s3객체를 넘겨줬다
let s3 = require("../utils/u_s3");

// 해당 middleware부터 먼저 실행되고 아래 /image가 실행된다
router.use('/image', (req, res, next) => {

    // 프론트로부터 받은 데이터
    var bo_id = req.headers.bo_id;
    
    // 데이터를 매개변수로 넘겨 새로운 s3 storage 객체 생성
    var s3_storage = new_s3_storage(bo_id);
    
    // 기존 s3에 새로만들어진 s3 storage로 덮어준다
    s3 = multer({ storage : s3_storage });
    
    next();
})

router.post('/image', s3.array('img'), ctrl.image);

module.exports = router;

middleware를 사용해서 /image로 들어가기 전에 header 값으로 프론트로부터 받은 데이터(req.headers.bo_id)로 새로운 storage 객체를 만들어 s3에 덮어씌울 계획이었다.

 결과는 실패!

let s3 = require("../utils/u_s3"); 값이 middleware에서 아무리 덮어씌어도 안바뀌더라.

그렇게 구글을 이리저리 떠돌면서 해답을 찾던 도중 정답을 발견했다.

▼ 완성 코드

/* router file */

// lib
const express = require('express');
const router = express.Router();
const ctrl = require('../api/c_board');

// modules
let s3 = require("../utils/u_s3");

router.post('/image', s3.upload.array('img'), ctrl.image);

module.exports = router;

/* ../utils/u_s3.js */

// lib
const AWS = require('aws-sdk');
const multer = require('multer');
const multerS3 = require('multer-s3');
const path = require('path');

// config
const aws_crediential = require("../config/aws");

const s3 = new AWS.S3(aws_crediential);

let params = {
  Bucket: 'deac-project',
  ACL: 'public-read-write'
};

let s3Storage = multerS3({
  s3: s3,
  bucket: params.Bucket,
  key: function(req, file, cb) {
    // 요거 하나로 해결
    var bo_id = req.headers.bo_id;
    let extension = path.extname(file.originalname);
    let basename = path.basename(file.originalname, extension);
    // 아래 코드에서 header로 받은 bo_id를 활용하여 저장되는 파일이 유동적으로 바뀜
    cb(null, `images/${bo_id}/${basename}-${Date.now()}${extension}`);
  },
  acl: 'public-read-write',
  contentDisposition: 'attachment',
  serverSideEncryption: 'AES256'
});

exports.upload = multer({ storage: s3Storage });

그냥 multerS3에 있는 key 함수에서 req.headers.bo_id를 사용하니 불러와졌다.

multerS3안의 key함수의 req에도 프론트에서 보낸 데이터가 똑같이 포함되어 있을 줄은 몰랐다.

file데이터만 있을줄 알았는데...

 

이렇게 삽질이 끝났다.

 

출처

https://velog.io/@josworks27/2020-01-18-0001-%EC%9E%91%EC%84%B1%EB%90%A8-qrk5iamlmv

+ Recent posts