[ 주의 ] 삽질하면서 코딩한 경험을 바탕으로 작성한 것임으로 틀린것도 많으니 더 좋은 정보가 있으면 알려주세요!
[ 잡담 ]
요즘들어 새로 시작한 프로젝트가 있어서 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
'Develop > Node.js' 카테고리의 다른 글
[Nodejs] 실패 없이 무료로 HTTPS 등록하기 (0) | 2020.06.20 |
---|---|
PM2 80, 443포트 사용 (0) | 2020.06.20 |
[Nodejs] Sequelize - 설치 & 마이그레이션 (0) | 2020.05.18 |