일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- ...args
- .toLocalString()
- 1주차
- 2020년 준비
- 2주차
- 4주차
- 5주차
- array
- array method
- async
- authentication
- AWS
- codestates
- commit
- Cookie
- CSS
- Data Structre
- Data Structure
- DataSturcutre
- Date.now()
- DB에 사진 저장하기
- Dev log
- DOM
- EC2
- EC2로 웹 만드는 방법
- EC2와 S3 연결하기
- element
- Es5
- ES6
- event 객체
- Today
- Total
souvenir
multer-S3 사용기 본문
이 글은 프로젝트 진행 중 팀 notion으로 작성했던 내용을 옮긴 것입니다.
원본 : www.notion.so/multer-s3-f8cdee6c24f2491bb9854ee240315bec
들어가며
사진을 DB에 저장하기 위해서는 DB에 사진 파일 자체를 저장하지 않고 사진의 위치(url)만 저장하는 방식으로 보통 저장하곤 한다. MySql에 image라는 형식이 있긴 하지만 이 타입을 쓰면 용량 많이 차지 하는데다가 이미지 로딩속도도 느려지기 때문이다. 사진 원본을 저장하는 위치를 서버 로컬 혹은 클라우드 시스템을 사용하곤 하는데, 정적 파일을 저장할 수 있은 AWS의 S3를 이용해서 사진을 저장하게 되었다.
해당 모듈은 multer-S3라는 모듈을 사용하면된다.
multer라는 기존 모듈도 있는데 이는 참고로 form-data를 클라이언트와 소통하고 저장할 수 있도록 하는 모듈이다. multer-S3는 이미지를 form-data로 받아 저장할 수 있도록 해주는 모듈이다. 검색해보니 대부분 '이미지만' 저장하는 예시만 있어 이미지와 함께 fomr-data로 넘어온 데이터도 다루는 방법은 색다른 경험이었다.
필요한 모듈
- multer(multer 기본모듈도 필요함)
- multer-s3
- AWS SDK(JS로 AWS 연결하기 위한 모듈)
세팅
- bucket은 퍼블릭 모드로 해두었음.
- 정책은 GetObject 뿐 아니라 혹시 몰라 PutObject도 허용
- 퍼블릭 액세스도 모두 허용
.env
AWS SDK를 사용하기 위해서는 액세스 키들이 필요하다.
S3의 경우 아래와 같이 필요함. 여기서 accessKeyId와 secretAccessKey 는 AWS 보안정책에서 사용자를 생성하여 받을 수 있다. S3 관련 접근을 모두 허용한 사용자를 만들어 해당 키들을 가져오면 된다.
참고 : https://tntdrive.com/where-do-i-get-my-access-keys.aspx
const s3 = new AWS.S3({
accessKeyId: process.env.KEYID,
secretAccessKey: process.env.KEY,
region: process.env.REGION,
});
위의 키들을 config.js에 넣는 경우도 있지만 나는 .env를 이용하여 저장해두었다.
파일 생성 위치
나의 경우 MVC 패턴을 사용하고 있기 때문에 router와 controller 폴더로 나눠서 만들고 있다.
보통 multer-S3를 사용하기 위해서는
1) multer와 AWS SDK 설정을 하는 파일,
2) AWS SDK 키들을 보관하는 파일,
3) 파일을 업로드하는 라우터,
4) 라우터의 실제 기능을 구현하는 컨트롤러 파일
이렇게 4가지가 필요하다. 물론 경우에 따라서는 3번과 4번을 라우터 파일에 한번에 작성하는 경우가 많다. reference에 올려둔 예시들이 모두 그러하다. 그러나 나는 MVC 패턴도 사용하고 있고, 실질적으로는 파일을 DB에 저장해야 하므로 컨트롤러 파일과 라우터 파일을 나눠서 작성하였다.
upload/upload.js
const multer = require('multer');
const multerS3 = require('multer-s3');
const path = require('path');
const AWS = require("aws-sdk");
const dotenv = require('dotenv');
dotenv.config({ path: './.env' });
const s3 = new AWS.S3({ //AWS SDK 설정 항목
accessKeyId: process.env.KEYID,
secretAccessKey: process.env.KEY,
region: process.env.REGION,
});
const storage = multerS3({ //multerS3 설정 항목
s3: s3,
bucket: 'nyam-nyam', //bucket 이름
contentType: multerS3.AUTO_CONTENT_TYPE,
acl: 'public-read-write', //읽고 쓰기 모두 허용
key: function (req, file, cb) {
let extension = path.extname(file.originalname);
cb(null, Date.now().toString() + extension)
},
})
exports.upload = multer({ storage: storage });
upload.js를 만들어 이 파일에는 multer 설정과 AWS SDK 설정을 해주었다.
(당연한 이야기이지만 폴더명과 파일명은 어떻게 설정하든 상관없다. multer-s3 기본 셋팅을 해두는 파일을 따로 만들어두는 것이 관리하기 편할 것 같아 upload라는 이름으로 만들어보았다)
multer-S3의 설정에 관해서는 npm 문서에 잘 설명되어 있다. contentType의 경우 자동으로 파일 형식을 지정해주려면 위와 같이 multerS3.AUTO_CONTENT_TYPE 상수를 작성해야한다.
key 항목은 파일 이름을 설정해주는 함수이다. 위의 경우는 파일은 원래 이름(file.originalname)에서 업로드한 날짜를 붙여 파일이름을 생성하였다. (https://www.npmjs.com/package/multer-s3)
그리고 upload라는 이름으로 export를 하여 다른 파일에서도 사용할 수 있도록 하였다.
route/editinfo.js
const express = require("express");
const router = express.Router();
const { upload } = require('../upload/upload')
const editInfoController = require("../controller/editInfo");
router.post("/image", upload.single('img'), editInfoController.image.post);
reference 예시에서는 라우터에서 바로 함수를 작성하였지만 나의 경우 미들웨어로 아까 작성한 upload 파일만 불러와 연결하였다. 파일을 img라는 키로 하나만 받을 것이기 때문에 upload.single('img')로 작성하였다. 두개 이상의 파일을 업로드할 경우에는 upload.array 나 upload.fields 로 올려야 한다.
출처 : https://github.com/expressjs/multer/blob/master/doc/README-ko.md#사용법
controller/editinfo/editImg.js
module.exports = {
post: async (req, res) => {
const jwt = require('jsonwebtoken');
const access_token = req.headers['x-access-token'];
const { user } = require('../../models')
try {
console.log("req.file: ", req.file.location);
const decoded = jwt.decode(access_token, { complete: true });
const account = decoded.payload.account;
await user.update({ userImg: req.file.location }, {where : { email : account }})
.catch(err => console.log(err))
res.status(200).json({ userImg: req.file.location });
} catch (err) {
console.log(err);
res.status(500).send("서버 에러")
}
}
};
실질적으로 기능을 하는 controller 파일이다.
우리는 토큰 기반 인증(JWT를 이용한 액세스 토큰, 리프레시 토큰)을 사용하고 있기 때문에 헤더로 보내준 액세스 토큰을 decode하여 해당 이메일을 가진 유저 정보에 req로 보내진 파일을 등록하고 있다. 포스트맨에서 form-data 형식으로 보낼 때 옵션을 text가 아닌 file로 하면 req.file.location에 해당 파일의 위치가 나오므로 파일의 위치를 DB에 저장하는 방식이 multer를 이용한 이미지 저장 방식이다.
이와 같이 하면 S3에도 파일이 등록되어 있음을 확인할 수 있다.
mysql 워크벤치에서도 확인할 수 있다.
form-data로 넘어온 text와 file 관리하기
form-data형식으로 text와 file을 함께 관리하는 것도 그리 어렵지 않다.
multer 자체가 폼데이터를 관리하는 것이기 때문에 위 방식과 동일하게 하면된다. 우리는 이 경우가 새로운 메뉴를 추가할 때 필요하였다. 메뉴 사진과 함께 가격등의 상세 정보를 저장해야했기 때문이다. 특정 한 key 안에 데이터가 들어있는 것이 관리하기 편하기 때문에 우리는 'data'라는 key 안에 이미지 파일과 폼데이터를 text 형태로 보냈다. data라는 key 안에 있는 폼데이터를 관리하기 위해서 router에서는 아래와 같이 관리해주면 된다.
routes/manageMenu.js
const express = require('express');
const router = express.Router();
const { upload } = require('../upload/upload')
const manageMenuController = require('../controller/manageMenu');
router.post('/addmenu', upload.single('data'),manageMenuController.addMenu.post);
module.exports = router;
컨트롤러서도 크게 다르지 않았다. reqest를 출력해보면 이미지 파일은 동일하게 req.file.location에 해당 저장 위치가 반영되어 있음을 확인할 수 있다. text는 설정해준 키 이름대로 req.body.data라는 곳에 객체형태로 담겨져 있었다. 키 설정만 해주면 되는 간단한 문제였던 것이다.
controller/manageMenu/AddMenu.js
module.exports = {
post: async (req, res) => {
console.log('add',req.body.data, req.file)
// req.body.data = { storeId, productionName, ingredient1, ingredient2, price, info, dessertType }
//req.file = { location : 파일주소 }
const { storeId, productionName, ingredient1, ingredient2, price, info, dessertType}
= req.body.data;
const location = req.file.location
//sequelize를 이용하여 전달된 데이터 저장
const addPro = await production.create({
productionName: productionName,
productionImg: location,
price: price,
info: info,
dessertType: dessertType,
type: 1,
});
},
};
이렇게 폼 데이터는 multer라는 모듈을 이용해 쉽게 다루고 저장할 수 있다.
다른 글보다도 npm과 해당 git 문서에 너무 자세히 설명되어 있어서 이를 정독해보면 더 다양하게 활용해볼 수 있을 것 같다.
reference
multer-s3 사용예시1
https://morningbird.tistory.com/62?category=641408
multer-s3 사용예시2
https://victorydntmd.tistory.com/70
포스트맨으로 파일 보내기
https://velog.io/@k904808/Postman으로-파일전송-테스트-하기
npm multer-s3
https://www.npmjs.com/package/multer-s3
'2020년 > Final-Project' 카테고리의 다른 글
로컬 로그인 구현_JWT (0) | 2020.11.10 |
---|---|
Final-Project 기획 및 준비 (0) | 2020.11.10 |