...

multer-s3 ์ฌ์ฉ๋ฒ
์ ์๊ฐ์ Node.js ์์ AWS SDK S3๋ฅผ ๋ค๋ฃจ๋ ๋ฒ์ ๋ํด์ ์์๋ณด์๋ค.
๊ทธ๋ผ ๋ง์ฝ ๊ฒ์๊ธ ์ด๋ฏธ์ง๋ฅผ S3์ ์ ์ฅํ๋๋ก ๊ตฌ์ฑํ๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํ ๊น?
๋ณดํต ๋ ธ๋ ํ๋ก์ ํธ์์ ํ์ผ ์ ๋ก๋ ๊ด๋ จ ์์ ์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ multer ๋ชจ๋์ ์๋ง์กด S3์ ์ฐ๋ํด์ ํ๋ก์ธ์ค๋ฅผ ๊ตฌ์ฑ ํด์ผ ํ ๊ฒ์ด๋ค.
๋ฐ๋ผ์ AWS S3 ๋ฒํท์ ์ด๋ฏธ์ง ํ์ผ์ ์ ์ฅํ๊ณ , DB์ ๊ทธ ๋ฒํท์ ์ด๋ฏธ์ง ํ์ผ ๊ฒฝ๋ก(์ด๋ฏธ์ง ์ฃผ์)๋ฅผ ์ ์ฅํ๊ณ , ์๋ฒ๋ ์ด ๊ฒฝ๋ก๋ฅผ ํด๋ผ์ด์ธํธ๋ก ์๋ตํ๋ ์์ผ๋ก ํ๋ก์ธ์ค๋ฅผ ๊ตฌ์ถํ์ฌ์ผ ํ๋ค.
๋คํํ ์ด๋ฏธ multer-s3 ์ด๋ผ๋ ๋ชจ๋์ด ๋ง๋ค์ด์ ธ ์์ด ์ฐ๋ฆฌ๋ ๊ทธ์ ๊ฐ์ ธ๋ค ์ฐ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
[EXPRESS] ๐ multer ๋ฏธ๋ค์จ์ด ์ฌ์ฉ๋ฒ ๐ฏ ์ ๋ฆฌ
multer ๋ชจ๋ ๋ฉํฐ๋ ์ฌ์ฉ ๋ฐฉ๋ฒ์ด ๋ค์ ์ด๋ ค์ด ๋ฏธ๋ค์จ์ด๋ค. ์ด๋ฏธ์ง, ๋์์ ๋ฑ์ ๋น๋กฏํ ์ฌ๋ฌ ๊ฐ์ง ํ์ผ๋ค์ ๋ฉํฐํํธ ํ์์ผ๋ก ์ ๋ก๋ํ ๋ ์ฌ์ฉํ๋ ๋ฏธ๋ค์จ์ด์ด๋ค. ๋ฉํฐํํธ ํ์์ด๋ enctype์ด mul
inpa.tistory.com
GitHub - anacronw/multer-s3: multer storage engine for amazon s3
multer storage engine for amazon s3. Contribute to anacronw/multer-s3 development by creating an account on GitHub.
github.com
> npm i multer
> npm i multer-s3@2.10.0 # aws sdk v2 ๋ฒ์ ผ์ ์ฌ์ฉํ๊ธฐ์ multer-s3๋ v2์ฉ์ผ๋ก
const express = require('express');
const multer = require('multer');
const path = require('path');
const AWS = require("aws-sdk");
const multerS3 = require('multer-s3');
const dotenv = require('dotenv');
dotenv.config();
const router = express.Router();
//* aws region ๋ฐ ์๊ฒฉ์ฆ๋ช
์ค์
AWS.config.update({
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
region: 'ap-northeast-2',
});
//* AWS S3 multer ์ค์
const upload = multer({
//* ์ ์ฅ๊ณต๊ฐ
// s3์ ์ ์ฅ
storage: multerS3({
// ์ ์ฅ ์์น
s3: new AWS.S3(),
bucket: 'test-bucket-inpa',
acl: "public-read",
contentType: multerS3.AUTO_CONTENT_TYPE,
key(req, file, cb) {
cb(null, `${Date.now()}_${path.basename(file.originalname)}`) // original ํด๋์์๋ค ํ์ผ์ ์ ์ฅ
},
}),
//* ์ฉ๋ ์ ํ
limits: { fileSize: 5 * 1024 * 1024 },
});
//* ๋ก์ปฌ multer ์ค์
const upload2 = multer(); // upload2.none() ์ฉ์ผ๋ก ์ฌ์ฉ
//* ์ฑ๊ธ ์ด๋ฏธ์ง ํ์ผ ์
๋ก๋ -> uploads/ ๋๋ ํ ๋ฆฌ์ ์ด๋ฏธ์ง๋ฅผ ์ฌ๋ฆฐ๋ค.
router.post('/img', isLoggedIn, upload.single('img'), (req, res) => {
console.log(req.file);
res.json({ url: req.file.location });
});
//* ๊ฒ์๊ธ ์
๋ก๋
//* ํ๋ก ํธ์์ multipart/form-data" > textarea ํ์์ผ๋ก ๋์ด์ค๊ธฐ ๋๋ฌธ์ upload2.none()ํ์์ผ๋ก ๋ฐ์์ ์ค๋ค.
router.post('/', isLoggedIn, upload2.none(), async (req, res, next) => {
// ...
});
module.exports = router;
์์์ ์ค์ ํ upload๋ผ๋ ํจ์๊ฐ ์คํ๋๋ฉด, ์ค์ ํ ์ด๋ฆ์ Bucket์ ํ์ผ์ ์ ๋ก๋ํ ์ ์๊ฒ ์ฒ๋ฆฌ๋๋ค.
key ์์ฑ์ ์ ๋ก๋ํ๋ ํ์ผ์ด ์ด๋ค ์ด๋ฆ์ผ๋ก ๋ฒํท์ ์ ์ฅ๋๋๊ฐ์ ๋ํ ์์ฑ์ด๋ค.
์ ์์ฑ๋๋ก๋ผ๋ฉด, ๋ฒํท์ ์ ๋ก๋๋๋ ํ์ผ(๊ฐ์ฒด)์ ์ด๋ฆ์ ํ์ฌ ์๊ฐ_ํ์ผ๋ช ์ด ๋๊ฒ ๋๋ค.
ํด๋ผ์ด์ธํธ์์ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ๊ณ , ์๋ฒ๋ก ํ์ด๋ก๋์ ํผ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๋ณด๋ด๋ฉด, ์ ์ ์ AWS S3 ๋ฒํท์ ์ ๋ก๋ํ ํ์ผ(๊ฐ์ฒด)๋ฅผ ์ ์ฅ๋๊ฒ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด๋ ๊ฒ ์ ์ฅ๋ ์ด๋ฏธ์ง์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ req.file ๋ก ๋ฐ์ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ ์๋ต์ผ๋ก req.file.location ์ ๋ณด๋ฅผ ๋ณด๋ด์ฃผ๋ฉด, s3 ๊ฐ์ฒด url์ด ์ ๋ฌ๋์ด ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ฌ ์ ์๋ค.
req.file ๊ฐ์ฒด ๋ฐ์ดํฐ๋ ๋ค์๊ณผ ๊ฐ๋ค.

Key | ์ค๋ช | Note |
size | ๋ฐ์ดํธ ํฌ๊ธฐ | |
bucket | ๋ฒ์ผ ์ด๋ฆ | S3Storage |
key | ํค ์ด๋ฆ | S3Storage |
acl | ์ด ํ์ผ์ ๋ํ ์ ๊ทผ ๊ถํ | S3Storage |
contentType | MIME ํ์ (ํ์ผ์ ํ์ฅ์๊ฐ ์์ ๊ฒฝ์ฐ ๋ฐ๋์ MIME ํ์ ์ ์ค์ ) | S3Storage |
metadata | The metadata object to be sent to S3 | S3Storage |
location | ํ์ผ url ๊ฒฝ๋ก | S3Storage |
contentDisposition | The contentDisposition used to upload the file | S3Storage |
storageClass | S3 ํฐ์ด ์คํ ๋ฆฌ์ง ํด๋์ค | S3Storage |
versionId | S3 ๋ฒ์ ๋์ ํ์ฑํํ์์ ๋ถ์ฌ๋๋ ๋ฒ์ ์์ด๋ | S3Storage |
contentEncoding | The contentEncoding used to upload the file | S3Storage |
๊ทธ๋ฆฌ๊ณ ์ฃผ์ํ ์ ์ ์ด๋ถ๋ถ ์ด๋ค.
upload ํจ์๋ S3์ฉ multer ํจ์์ด๊ธฐ์ form-data๋ก ๋์ด์จ text ๋ฅผ ๋ฐ๋ก ์ฒ๋ฆฌํด์ฃผ์ด์ผ ํ๋ค.
์๋ฅผ๋ค์ด ๊ฒ์๊ธ์ ์์ฑํ๊ณ ์๋ฒ์ ํฌ์คํ api๋ฅผ ๋ณด๋ผ๋, ๊ฒ์๊ธ์ ์ฌ์ฉ๋ ์ด๋ฏธ์ง๋ S3์ ์ ์ฅ๋๊ฒ ํ๊ณ , ๊ฒ์๊ธ ๊ธ ๋ด์ฉ์ DB์ ์ ์ฅํด FULLTEXT INDEX ์ฒ๋ฆฌ๋ฅผ ํด์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
//* ๋ก์ปฌ multer ์ค์
const upload2 = multer(); // upload2.none() ์ฉ์ผ๋ก ์ฌ์ฉ
// ...
//* ๊ฒ์๊ธ ์
๋ก๋
//* ํ๋ก ํธ์์ multipart/form-data" > textarea ํ์์ผ๋ก ๋์ด์ค๊ธฐ ๋๋ฌธ์ upload2.none()ํ์์ผ๋ก ๋ฐ์์ ์ค๋ค.
router.post('/', isLoggedIn, upload2.none(), async (req, res, next) => {
// ...
});
Multer S3 ์ต์ ์ ๋ฆฌ
S3 ํด๋ ๋ณ๋ก ์ ๋ก๋
๋ง์ผ ๋ณด๋ค ๋ช ํํ ํ์ผ ๊ด๋ฆฌ๋ฅผ ์ํ์ฌ ๋ฐ๋ก ํด๋๋ฅผ ๋ง๋ค์ด ๊ทธ ํด๋ ์์์๋ง ์ด๋ฏธ์ง ํ์ผ์ ๊ด๋ฆฌํ๊ณ ์ถ๋ค๋ฉด ์ด๋ป๊ฒ ํ ๊น?
๊ฐ๋จํ๋ค. multer s3 ์ค์ ์์ ํ์ผ๋ช ์์ฒด์ ๊ฒฝ๋ก๋ฅผ ์ค์ ์ค์ ํ๋ฉด ๋๋ค.

๊ฐ์ฒด ACL ์ค์ ํ๊ธฐ
๋ฒํท์ ํ์ผ์ ์ ๋ก๋ํ ๋ ๋ค์๊ณผ ๊ฐ์ด ์ถ๊ฐ ํ๋กํผํฐ๋ก acl์ ์ง์ ํ์ฌ ๋ณด๋ผ ์ ์๊ฒ ์ค์ ํ ์ ์๋ค.
๋ณดํต ๊ฒ์๊ธ์ ์ด๋ฏธ์ง๋ ๋ธ๋ผ์ฐ์ ์ ๋ฐ๋ก ๊ฒ์ํด์ ๋ณผ์ ์๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๋ public-read ๋ก ์ค์ ํ์๋ค.
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
acl: 'public-read', // ๊ณต๊ฐ
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
ACL ์ต์ | ACL์ ์ถ๊ฐ๋ ๊ถํ |
private | ๋ค๋ฅธ ์ฌ๋์ ์ก์ธ์ค ๊ถํ์ด ์์ต๋๋ค(๊ธฐ๋ณธ๊ฐ). |
public-read | AllUsers๊ทธ๋ฃน์ด ์ก์ธ์ค READ๊ถํ์ ์ป์ต๋๋ค. |
public-read-write | AllUsers๊ทธ๋ฃน์ด ๊ฐ์ ธ ์ค๊ณ READ์ก์ธ์ค WRITEํฉ๋๋ค. ๋ฒํท์ ์ด๋ฅผ ๋ถ์ฌํ๋ ๊ฒ์ ์ผ๋ฐ์ ์ผ๋ก ๊ถ์ฅ๋์ง ์์ต๋๋ค. |
aws-exec-read | Amazon EC2๋ Amazon S3์์ Amazon ๋จธ์ ์ด๋ฏธ์ง(AMI) ๋ฒ๋ค ์ READ์ก์ธ์คํ ์ ์์ต๋๋ค . |
authenticated-read | AuthenticatedUsers๊ทธ๋ฃน์ด ์ก์ธ์ค READ๊ถํ์ ์ป์ต๋๋ค. |
bucket-owner-read | ๋ฒํท ์์ ์๊ฐ READ์ก์ธ์ค ๊ถํ์ ์ป์ต๋๋ค. ๋ฒํท์ ์์ฑํ ๋ ๋ฏธ๋ฆฌ ์ค๋น๋ ์ด ACL์ ์ง์ ํ๋ฉด Amazon S3๊ฐ ์ด๋ฅผ ๋ฌด์ํฉ๋๋ค. |
bucket-owner-full-control | ๋ฒํท์ ์์ฑํ ๋ ๋ฏธ๋ฆฌ ์ค๋น๋ ์ด ACL์ ์ง์ ํ๋ฉด Amazon S3๊ฐ ์ด๋ฅผ ๋ฌด์ํฉ๋๋ค. |
log-delivery-write | LogDelivery๊ทธ๋ฃน์ ๋ฒํท WRITE์ READ_ACP๋ํ ๊ถํ์ ์ป์ต๋๋ค. |
Cache-Control ํค๋ ์ค์
cache๋ฅผ ์ฌ์ฉํ๊ฒ ๋ค๋ ์๋ฏธ์ด๋ฉฐ, max-age 31536000์ ์บ์ ์ ํจ์๊ฐ์ ์ ํ๋ ๊ฐ์ด๋ค.
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
cacheControl: 'max-age=31536000',
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
Content-Type ์ค์
์ ํ์ contentType ์ต์ ์ ์ฌ์ฉํ์ฌ ํ์ผ์ Content/mime ์ ํ์ ์ค์ ํ ์ ์๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ์ฝํ ์ธ ์ ํ์ application/octet-stream์ผ๋ก ์ค์ ๋๋ค.
๋ฐ๋ผ์ ์ด๋ฏธ์ง์ ๊ฐ์ ํ์ผ์ ๋ค๋ฃจ๊ธฐ ์ํด์๋ multer-s3๊ฐ ํ์ผ์ ๋ด์ฉ ์ ํ์ ์๋์ผ๋ก ์ฐพ๋๋ก multerS3.AUTO_CONTENT_TYPE ์์๋ฅผ ์ฌ์ฉํ์ฌ contentType์ ์ง์ ํ๋๋ก ํด์ผ ๋๋ค.
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
contentType: multerS3.AUTO_CONTENT_TYPE,
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
multer-s3-transform์ ์ด์ฉํ ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ง
๊ณ ์ฉ๋์ ์ด๋ฏธ์ง๋ฅผ ๊ทธ๋๋ก S3์ ์ฌ๋ฆฌ๋ ๊ฒ์ ํจ์จ์ ์ด์ง ์๋ค.
๋ฐ๋ผ์ ๊ณ ์ฉ๋์ ์ด๋ฏธ์ง๋ฅผ ์๋ฒ์์ ์ง์ ๋ฆฌ์ฌ์ด์ง์ ํตํด ์ต์ ํํ S3์ ์ฌ๋ ค๋ณด์.
transform ๊ณผ์ ์์ resizing ์์ ์ ์ํด ๋ฐ๋ก sharp ๋ชจ๋๋ ์ค์นํด์ผ ํ๋ค. ์ฌ์ฉ๋ฒ์ ์ฌ๊ธฐ ์ฐธ์กฐ
multer-s3-transform
Streaming multer storage engine for AWS S3. Latest version: 2.10.3, last published: 2 years ago. Start using multer-s3-transform in your project by running `npm i multer-s3-transform`. There are 4 other projects in the npm registry using multer-s3-transfor
www.npmjs.com
> npm install multer-s3-transform sharp
const multer = require("multer");
const multerS3 = require("multer-s3-transform"); // multer-s3์ด ์๋ multer-s3-transform์ ์ํฌํธ
const sharp = require("sharp");
const ImageUpload = multer({
storage: multerS3({
s3,
bucket: "๋ฒ์ผ๊ฒฝ๋ก",
contentType: multerS3.AUTO_CONTENT_TYPE,
shouldTransform: true,
transforms: [
{
id: "resized",
key: function (req, file, cb) {
let extension = path.extname(file.originalname);
cb(null, Date.now().toString() + extension);
},
transform: function (req, file, cb) {
cb(null, sharp().resize(100, 100)); // ์ด๋ฏธ์ง๋ฅผ 100x100 ์ผ๋ก ๋ฆฌ์ฌ์ด์ง
},
},
],
acl: "public-read-write",
}),
});
const uploadImageMulterMiddleware = ImageUpload.single("file");
multer-s3๋ฅผ requireํ๋ฉด ์๋๋ค. multer-s3-trasform์ requireํด์ผ ํ๋ค.
์ด ๊ธ์ด ์ข์ผ์ จ๋ค๋ฉด ๊ตฌ๋ & ์ข์์
์ฌ๋ฌ๋ถ์ ๊ตฌ๋
๊ณผ ์ข์์๋
์ ์์๊ฒ ํฐ ํ์ด ๋ฉ๋๋ค.