...
multer-s3 사용법
전 시간에 Node.js 에서 AWS SDK S3를 다루는 법에 대해서 알아보았다.
그럼 만약 게시글 이미지를 S3에 저장하도록 구성하고 싶다면 어떻게 할까?
보통 노드 프로젝트에서 파일 업로드 관련 작업에 가장 많이 사용되는 multer 모듈에 아마존 S3을 연동해서 프로세스를 구성 해야 할 것이다.
따라서 AWS S3 버킷에 이미지 파일을 저장하고, DB엔 그 버킷의 이미지 파일 경로(이미지 주소)를 저장하고, 서버는 이 경로를 클라이언트로 응답하는 식으로 프로세스를 구축하여야 한다.
다행히 이미 multer-s3 이라는 모듈이 만들어져 있어 우리는 그저 가져다 쓰기만 하면 된다.
> 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 모듈도 설치해야 한다. 사용법은 여기 참조
> 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해야 한다.
이 글이 좋으셨다면 구독 & 좋아요
여러분의 구독과 좋아요는
저자에게 큰 힘이 됩니다.