2019. 08. 11 수정


1. AWS S3( Simple Storage Service )란?

S3는 AWS의 스토리지 서비스로 구글 드라이브, 네이버 클라우드와 비슷한 개념입니다.

그런데 S3는 HTTP 프로토콜로 파일 업로드 및 다운로드가 가능하다는 장점이 있습니다.


S3가 필요한 이유는 로컬에 저장된 이미지와 같은 미디어 파일은 다른 사람이 웹에서 볼 수 없기 때문에, 로컬 외부에서 미디어 파일을 보기 위해서입니다.

이것이 가능한 이유는 URL을 통해 미디어 파일에 접근할 수 있기 때문이죠.


이번 글에서는 클라이언트가 이미지 파일을 업로드하면, 이 파일을 S3에 저장하는 예제를 구현해보도록 하겠습니다.





2. 준비작업

  • AWS 계정
  • AWS 계정의 Access key 와 Secret key
    • key에 대해 잘 모르신다면 여기를 참고해주세요.
  • S3 버킷 생성





3. 준비작업 - AWS SDK 설치

Node.js에서 AWS sdk( JS )를 사용하기 위해서는 aws-sdk 모듈을 설치해야 합니다.

# npm install aws-sdk


저는 보통 sequelize를 사용하기 때문에, sequelize-cli 모듈을 설치하여 sequelize init 명령어를 실행하면 config 폴더가 생성됩니다.

이 때 생성된 config 폴더에 awsconfig.json 파일을 생성하도록 하겠습니다.


*** 참고

이번 예제에서 sequelize를 사용하지는 않지만, sequelize를 사용했다는 가정하에 작성되었습니다.

config 파일은 한 폴더에 정리해놓는 것이 좋기 때문에 config 폴더에 awsconfig.json 파일을 생성했습니다.

sequelize를 사용하지 않고 간단하게 테스트만 하고 싶으시다면 원하는 경로에 awsconfig.json 파일을 생성해주세요.

단, 예제에서는 awsconfig.json 파일이 config/awsconfig.json 경로에 위치한다는 점을 참고하여 진행해주세요.



config/awsconfig.json 파일에 AWS 계정의 Access key와 Secret key를 작성해주세요.

key 값이 작성된 awsconfig.json 파일은 절대로 외부에 노출되면 안됩니다 !

( Github에 올려야 한다면 .gitignore를 해주세요 )

{
"accessKeyId": "access key를 입력해주세요.",
"secretAccessKey": "secret key를 입력해주세요.",
"region": "ap-northeast-2"
}

AWS SDK를 로딩하기 위해 환경설정 파일을 json 파일로 작성합니다.


예제에서 작성한 region은 "서울 지역"을 의미하는 값입니다.

( region에 대한 정보는 여기를 참고해주세요. )



다음으로는 routes/index.js 파일에서 AWS SDK를 로딩하도록 하겠습니다.

const express = require('express');
const router = express.Router();
const AWS = require("aws-sdk");
AWS.config.loadFromPath(__dirname + "/../config/awsconfig.json");

let s3 = new AWS.S3();

router.get('/upload', function(req, res, next) {
res.render('upload');
});

module.exports = router;
  • SDK를 로딩하는 방법은 aws.config.loadFromPath() 메서드를 호출할 때 매개변수로 AWS SDK 환경 설정 파일 경로를 전달하면 됩니다.
  • aws의 S3 서비스를 사용할 것이므로 S3 객체가 필요합니다.
    • aws-sdk 모듈 객체에서 생성자 함수로 S3()를 호출하면 S3 객체를 얻을 수 있습니다.


이제 S3 API를 사용할 준비가 끝났습니다.





4. 뷰 페이지 작성

다음으로 이미지 파일을 업로드 하기 위한 뷰 페이지를 작성하도록 하겠습니다.


/routes/upload.ejs

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="imgFile">
<input type="submit" value="S3에 보내기">
</form>
</body>
</html>

클라이언트에서 서버로 파일을 보내기 위해서는 인코딩 타입으로 multipart/form-date를 명시해줘야 합니다.





5. 라우터 함수 작성

일바적으로 파일 업로드를 위해서는 multer 모듈이 필요한데요,

( Node.js에서 파일 업로드 하는 방법은 여기를 참고해주세요. )


다행히도 업로드된 파일을 S3에 바로 업로드할 수 있도록 도와주는 multer-s3 모듈이 있으므로, 이 모듈을 사용하도록 하겠습니다.

( multer-s3 모듈에 대한 자세한 정보는 깃헙을 참고해주세요. )


multer-s3 모듈을 사용하기 위해서는 multer 모듈이 필요하므로 두 개의 모듈을 설치하겠습니다.

# npm install multer # npm install multer-s3



이제 클라이언트로부터 받은 이미지 파일을 S3에 업로드하는 라우터 함수를 작성해보도록 하겠습니다.

/routes/index.js

const express = require('express');
const router = express.Router();
const path = require("path");
const multer = require("multer");
const multerS3 = require('multer-s3');
const AWS = require("aws-sdk");
AWS.config.loadFromPath(__dirname + "/../config/awsconfig.json");

let s3 = new AWS.S3();

let upload = multer({
storage: multerS3({
s3: s3,
bucket: "yourBuecketName",
key: function (req, file, cb) {
let extension = path.extname(file.originalname);
cb(null, Date.now().toString() + extension)
},
acl: 'public-read-write',
})
})


router.post('/upload', upload.single("imgFile"), function(req, res, next){
let imgFile = req.file;
res.json(imgFile);
})

router.get('/upload', function(req, res, next) {
res.render('upload');
});

module.exports = router;

post() 라우터 미들웨어는 multer를 이용하여 파일 업로드를 했을 때와 크게 다르지 않습니다.

router.post() 함수의 두 번째 매개변수에서 파일을 업로드 해주고, 응답으로 업로드한 파일 객체를 전달합니다.


예제에서 중요한 것은 업로드 파일이 저장될 upload 객체를 정의하는 부분인데, multer-s3 깃헙에 사용법이 잘 정리되어 있습니다.



사진의 Key 값들은 multerS3( ) 함수를 호출 할 때, 인자로 전달할 수 있는 객체의 프로퍼티들입니다.

기본적인 Key 값들만 살펴보도록 하겠습니다.

  • bucket
    • 파일을 업로드 할 S3 버킷 이름
  • key
    • S3에 저장될 파일의 이름
  • acl
    • 파일에 대한 접근 권한 ( 밑의 사진을 참고 )
  • contentType
    • MIME 타입  ( 파일의 확장자가 없을 경우 반드시 MIME 타입을 설정 해야합니다. )


acl의 값으로 작성할 수 있는 것은 다음과 같습니다.




이제 서버를 실행하고 브라우저에서 localhost:3000/upload 를 요청하여, 파일을 업로드 해보세요.

그리고 AWS S3에도 파일이 잘 업로드가 되었는지 확인해보세요 !





*** 에러 해결 ***

파일을 업로드 할 때, Access Denied 에러가 발생하시나요?


이는 AWS계정 또는 S3의 권한 설정문제입니다.

  • AWS의 IAM의 계정이 있다면 role을 확인해주세요.
  • S3 버킷의 권한 부분에서 "AWS 계정에 대한 엑세스" , "퍼블릭 엑세스"를 모두 "예"로 체크해주세요.







6. S3에 저장된 이미지 파일 출력하기

S3에 저장된 이미지를 불러오는 방법은 여러 방법이 있겠지만,

간단한 방법은 버킷에서 불러오고자 하는 이미지를 클릭하여 링크를 그대로 img 태그의 src 속성에 작성하는 것입니다.

<img src = "https://s3.ap-northeast-2.amazonaws.com/버킷이름/파일이름.png" alt="">





이상으로 Node.js에서 AWS SDK를 설치하여 클라이언트가 업로드한 파일을 S3에 파일을 저장하는 방법에 대해 알아보았습니다.

하루 종일 해결 못하다가 multer-s3 라이브러리를 사용하니까 10분도 안되어서 구현이 됐네요....

댓글 펼치기 👇
  1. dddd 2018.12.28 13:10

    코드좀 복붙할려니 안되네요..ㅋㅋㅋㅋ --;

  2. filedownload 2019.01.17 14:12

    혹시 file 다운로드는 어떻게 해야할까요..?
    업로드는 했는데 다운로드를 못하고 있습니다 ㅠㅠ

    • Favicon of https://victorydntmd.tistory.com victolee 우르르응 2019.01.23 07:42 신고

      안녕하세요. 답변이 늦어서 이미 해결하셨을 것 같네요...

      제가 다운로드는 구현을 안해봤는데요... 이 글을 참고하시면 좋을 것 같습니다.
      https://grokonez.com/aws/amazon-s3/node-js-restapis-download-file-from-amazon-s3-using-express-aws-sdk

      나중에 기회가 된다면 다운로드도 구현해봐야겠네요.
      감사합니다~

  3. programmerpsk 2019.02.19 15:56

    저는 오류 나는것도 없는데 파일 s3에 들어가보면 파일 업로드가 되어있질 않네요.. 뭐 때문에 그러는 걸까요.. ㅋ..

  4. 팽펑 2019.04.22 15:43

    let extension = path.extname(file.originalname);

    이부분 path 정의 빠졌어요

  5. head 2019.09.03 12:33

    감사합니다. 잘 참고하였습니다. input태그의 name부분과 single.upload() 의 첫번째 param이 달라서 에러가 발생하는것을 한참 찾아 헤메었네요

  6. myoh 2020.07.02 15:33

    S3 처음으로 사용하려는데 이 게시글로 많은 도움을 얻었습니다. 정말 감사합니다.