Node.js에서 multer로 S3 버킷에 데이터 추가하기
by Yangeok
Node.js 웹서버에서 프로그래밍적으로 S3 버킷 데이터를 사용하기 위한 전처리 작업입니다. S3 버킷에서 데이터를 추가하는 작업을 할 예정입니다. 실제 데이터를 브라우저를 통해 POST요청을 보낼떄 버킷에 업로드함과 동시에 업로드된 이미지 url을 데이터베이스에 추가하기 위해 필요합니다.
작업환경
- koa
- koa-router
- aws-sdk
- multer
- multer-s3
작업순서
S3 버킷을 만들었다면 accessKeyId
와 secretAccessKey
를 다운받기 위해 우측상단에 있는 내 계정을 클릭해서 내 보안 자격 증명(Credential) 로 들어갑니다.
새 액세스 키 만들기 를 눌러 액세스키를 새로 만듭니다.
액세스 키 표시 를 누릅니다.
다음과 같이 액세스 키 ID
와 보안 액세스 키
가 나타납니다.
아래와 같이 패키지를 설치합니다.
yarn add koa koa-router aws-sdk koa-multer multer-s3
라우팅을 할 수 있게 기본적인 웹서버 세팅을 합니다.
// app.js
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
app.use(router.routes());
app.listen(3000);
라우팅까지 끝났으면 S3를 node.js에서 연결시킵니다. 필요한 패키지들을 불러온 다음 아까 발급받은 accessKeyId
와 secretAccessKey
를 사용하고 지역은 버킷을 만들때 아시아 태평양(서울) 로 설정했다면 ap-northeast-2
로 작성하면 됩니다.
// s3.js
const AWS = require('aws-sdk');
const multer = require('koa-multer');
const multerS3 = require('multer-s3');
const path = require('path');
aws-sdk에 새로운 s3객체를 만들어 키를 입력합니다. 방금 발급받은 정보들을 입력하고 다른 s3 api를 이용하기 위해서 파라미터 객체를 만듭니다.
// s3.js
const s3 = new AWS.S3({
accessKeyId: AWS_ACCESS_KEY_ID,
secretAccessKey: AWS_SECRET_ACCESS_KEY,
region: AWS_REGION
});
let params = {
Bucket: 'BUCKET_NAME',
ACL: 'public-read-write'
};
이제 파일을 웹에서 업로드하는 일만 남았는데요. multer-s3는 koa-multer를 이용해서 사용할 수 있습니다. koa에서 multer를 사용하면 다음과 같이 에러가 발생하기 때문에 사용하는 프레임워크에 맞는 패키지를 설치해야합니다.
TypeError: req.pipe is not a function
일단 먼저 multer의 기본적인 코드는 다음과 같습니다.
// s3.js
let storage = 'upload/';
exports.upload = multer({ storage: storage });
// app.js
const s3 = require('./s3');
app.post('/upload/singe', s3.upload.single('file_name'), ctx => {
body = ctx.req.file;
});
app.post('/upload/array', s3.upload.array('file_name'), ctx => {
body = ctx.req.files;
});
app.post('/upload/any', s3.upload.array('file_name'), ctx => {
body = ctx.req.files;
});
multer()
의 옵션에 해당하는 인자에 객체를 입력합니다. 객체의 키로는 파일을 저장할 위치를 가리키는 dest
나 storage
를 사용할 수 있습니다.
dest
: 저장되는 디렉토리만 설정할 수 있습니다.storage
에서 사용하는 값은 올 수 없습니다.storage
: 저장되는 파일명이나 인코딩 등을 조작할 수 있습니다.dest
의 값으로 올 수 있는 디렉토리 이름도 사용할 수 있습니다.
storage
키와 같이 사용할 수 있는 옵션이 바로 multer에서 말하는 스토리지 엔진 입니다. 디스크 스토리지는 파일은 디스크에 저장하기 위한 모든 제어기능을 제공합니다. destination
과 filename
두가지의 옵션을 사용할 수 있습니다. multer 공식문서에서 다른 옵션을 볼 수 있습니다.
koa 라우터에 미들웨어로 들어가는 옵션은 다음과 같습니다.
single()
: 인자에 명시된 이름의 단수 파일을 받습니다.body.req.file
에 저장됩니다.array()
: 인자에 명시된 이름의 파일 전부를 배열 형태로 받습니다.body.req.files
에 저장됩니다.any()
: 전달된 모든 파일을 허용합니다.body.req.files
에 저장됩니다.
multer 공식문서에 들어가시면 더 많은 옵션을 볼 수 있습니다.
이대로 파일을 업로드하면 파일명도 확장자도 알아서 바껴버리기 때문에 아까 언급한 스토리지 엔진을 아래와 같이 사용합니다.
// s3.js
let diskStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'upload');
},
filename: (req, file, cb) => {
let extension = path.extname(file.originalname);
let basename = path.basename(file.originalname, extension);
cb(null, `images/${basename}-${Date.now()}${extension}`);
}
});
exports.upload = multer({ storage: diskStorage });
upload 디렉토리에 파일을 올리고 파일명은 파일 중복을 막기 위해 Number
타입으로 리턴된 날짜를 파일명 앞에 접착합니다.
이제 포스트맨에서 파일이 업로드가 제대로 되는지 테스트를 해보겠습니다. 요청을 보내는 순서는 아래와 같습니다.
- HTTP 메서드를 POST로 설정합니다.
- Body에서 form-data를 선택합니다.
- KEY를 app.js에 post 라우터에 들어간
s3.upload.array('file_name')
에서file_name
과 같이 동일한 이름으로 설정한다. - VALUE에 파일을 업로드합니다.
- SEND를 누릅니다.
3개의 파일을 로컬로 업로드했습니다. 응답에 데이터가 뜨는 것과 프로젝트 디렉토리에 있는 upload
디렉토리에 파일이 생성됐음을 확인합니다.
[
{
"fieldname": "file",
"originalname": "1.txt",
"encoding": "7bit",
"mimetype": "text/plain",
"destination": "upload",
"filename": "1-1553254524481.txt",
"path": "upload\\1-1553254524481.txt",
"size": 1
},
{
"fieldname": "file",
"originalname": "2.txt",
"encoding": "7bit",
"mimetype": "text/plain",
"destination": "upload",
"filename": "2-1553254524482.txt",
"path": "upload\\2-1553254524482.txt",
"size": 1
},
{
"fieldname": "file",
"originalname": "3.txt",
"encoding": "7bit",
"mimetype": "text/plain",
"destination": "upload",
"filename": "3-1553254524482.txt",
"path": "upload\\3-1553254524482.txt",
"size": 1
}
]
다음은 multer-s3를 적용해서 s3버킷에 보내고 바디를 받아오겠습니다. multer-s3 사용법은 multer 스토리지 엔진 부분에 모듈을 집어넣어주는 것이 전부입니다.
// s3.js
let s3Storage = multerS3({
s3: s3,
bucket: params.Bucket,
key: function(req, file, cb) {
let extension = path.extname(file.originalname);
let basename = path.basename(file.originalname, extension);
cb(null, `images/${basename}-${Date.now()}${extension}`);
},
acl: 'public-read-write',
contentDisposition: 'attachment',
serverSideEncryption: 'AES256'
});
exports.upload = multer({ storage: s3Storage });
key
설정에서 파일명 앞에 디렉토리명을 써서 올리면 해당 디렉토리 안으로 파일이 업로드됩니다. 자세한 옵션들은 multer-s3 npm 문서를 참고하시기 바랍니다.
s3에 업로드하면 바디에 다음과 같이 찍힙니다.
[
{
"fieldname": "file",
"originalname": "1.txt",
"encoding": "7bit",
"mimetype": "text/plain",
"size": 1,
"bucket": "BUCKET_NAME",
"key": "images/1-1553254491794.txt",
"acl": "public-read-write",
"contentType": "application/octet-stream",
"contentDisposition": "attachment",
"storageClass": "STANDARD",
"serverSideEncryption": "AES256",
"metadata": null,
"location": "AWS_S3_URL",
"etag": "\"c4ca4238a0b923820dcc509a6f75849b\""
},
{
"fieldname": "file",
"originalname": "2.txt",
"encoding": "7bit",
"mimetype": "text/plain",
"size": 1,
"bucket": "BUCKET_NAME",
"key": "images/2-1553254491795.txt",
"acl": "public-read-write",
"contentType": "application/octet-stream",
"contentDisposition": "attachment",
"storageClass": "STANDARD",
"serverSideEncryption": "AES256",
"metadata": null,
"location": "AWS_S3_URL",
"etag": "\"c81e728d9d4c2f636f067f89cc14862c\""
},
{
"fieldname": "file",
"originalname": "3.txt",
"encoding": "7bit",
"mimetype": "text/plain",
"size": 1,
"bucket": "BUCKET_NAME",
"key": "images/3-1553254491795.txt",
"acl": "public-read-write",
"contentType": "application/octet-stream",
"contentDisposition": "attachment",
"storageClass": "STANDARD",
"serverSideEncryption": "AES256",
"metadata": null,
"location": "AWS_S3_URL",
"etag": "\"eccbc87e4b5ce2fe28308fd9f2a7baf3\""
}
]
파일이 올라갔고 버킷안에 있는 모든 파일 목록을 불러오는 api를 사용합니다.
// s3.js
s3.listObjectsV2({ Bucket: params.Bucket }, (err, data) => {
if (err) {
throw err;
} else {
let arr = [];
let contents = data.Contents;
contents.forEach(content => arr.push(content.Key));
console.log({ dataList: arr });
}
});
다음과 같이 콘솔에 출력됩니다.
{ dataList:
[ 'images/11553254491794.txt',
'images/21553256161466.txt',
'images/31553254491795.txt' ] }
깃헙에 코드를 공유했으니 전체 코드가 필요하신 분은 여기를 눌러주세요.