프로젝트

16강 미니 프로젝트(사진 공유 웹사이트)_3 (AWS)

Chansman 2025. 4. 11. 16:49

📌 개념 정리

  • Express Generator 구조 이해
    • views 폴더: 프론트엔드 영역, EJS(템플릿 엔진), HTML 등을 관리
    • routes 폴더: 백엔드 API 코드 관리
  • AWS SDK for JavaScript
    • AWS 서비스를 Node.js에서 사용할 수 있도록 하는 SDK
    • EC2 인스턴스에서 S3로 파일 업로드 가능
  • IAM 역할(Role)
    • AWS 서비스에 접근 권한을 부여할 때 사용
    • EC2 인스턴스에 S3 접근 권한 부여 시 사용
  • CloudFront와 OAC(Origin Access Control)
    • S3 버킷의 객체를 퍼블릭하게 접근할 수 있게 하는 기능
    • CDN을 통한 빠르고 안전한 이미지 전송

🚦 동작 원리 및 구조

  1. 사용자가 웹페이지에서 이미지를 업로드
  2. Node.js 서버(Express)가 이미지를 받아 EC2 인스턴스에 저장
  3. 저장된 이미지를 AWS SDK를 통해 S3 버킷으로 업로드
  4. 업로드된 이미지를 S3에서 목록으로 조회해 웹페이지에 표시
  5. 이미지 접근을 위해 CloudFront CDN을 활용 (OAC)

💻 코드 예시 및 흐름 분석

🔸 AWS SDK 설치 프로젝트 최상위 폴더에서 설치 cd project

npm install @aws-sdk/client-s3

🔸 images.js 수정 (라우트) 8번 밑 클라이언트 삽입 및 route get 삽입 ( bucket주소 변경) 

38번 버켓주소도변경

 

  1 var express = require('express');
  2 var router = express.Router();
  3 var fs = require('fs');
  4 var path = require('path');
  5 var multer = require('multer');
  6 var upload = multer({ dest: path.join(__dirname, '..', 'uploads') });
 
  8 var {
  9     S3Client,
 10     PutObjectCommand,
 11     ListObjectsV2Command,
 12 } = require('@aws-sdk/client-s3');
 13 const s3Client = new S3Client({ region: 'ap-northeast-2' });
 14
 15 router.get('/', function (req, res, next) {
 16     s3Client
 17         .send(
 18             new ListObjectsV2Command({
 19                 Bucket: 'rainsos-bucket-20250411',
 20                 MaxKeys: 50,
 21             })
 22         )
 23         .then((data) => {
 24             res.send(data.Contents);
 25         })
 26         .catch((error) => {
 27             console.log(error);
 28         });
 29 });
 30
 31 router.post('/', upload.single('new-image'), function (req, res, next) {
 32     console.dir(req.file);
 33
 34     fs.readFile(req.file.path, function (err, data) {
 35         s3Client
 36             .send(
 37                 new PutObjectCommand({
 38                     Bucket: 'rainsos-bucket-20250411',
 39                     Key: req.file.filename,
 40                     Body: data,
 41                     ContentType: req.file.mimetype,
 42                 })
 43             )
 44             .then((data) => {
 45                 console.log(data);
 46                 res.send(data);
 47             })
 48             .catch((error) => {
 49                 console.log(error);
 50             });
 51     });
 52 });
 53 module.exports = router;

 

- IAM 역할(Role) 등록 EC2 에서 보안눌려서 등록

- S3에서 버킷을 동일한 이름으로 만든다. 치

- Cloudfront 에서 배포 생성해서 S3블럭에 연동 배포 도메인주소필요 

 

밑에코드는 예시

const { S3Client, PutObjectCommand, ListObjectsV2Command } = require('@aws-sdk/client-s3');
const s3 = new S3Client({ region: 'ap-northeast-2' });

// 이미지 업로드
router.post('/', upload.single('image'), async (req, res) => {
  const params = {
    Bucket: '자신의-버킷-이름',
    Key: req.file.originalname,
    Body: fs.createReadStream(req.file.path),
    ContentType: req.file.mimetype,
  };

  try {
    const data = await s3.send(new PutObjectCommand(params));
    res.status(200).send(data);
  } catch (err) {
    console.error(err);
    res.status(500).send(err);
  }
});

// 이미지 목록 조회
router.get('/', async (req, res) => {
  const params = {
    Bucket: '자신의-버킷-이름',
    MaxKeys: 50,
  };

  try {
    const data = await s3.send(new ListObjectsV2Command(params));
    res.status(200).send(data.Contents);
  } catch (err) {
    console.error(err);
    res.status(500).send(err);
  }
});

 

 

 

🔸 index.ejs (뷰) 최종 수정 19번라인 추가 /  31번부터 추가 그뒤에 52번에 배포 도메인수정

 <!DOCTYPE html>
  2 <html>
  3   <head>
  4     <title><%= title %></title>
  5     <link rel='stylesheet' href='/stylesheets/style.css' />
  6     <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG87    9m7" crossorigin="anonymous">
  7   </head>
  8   <body>
  9     <h1><%= title %></h1>
 10     <p>Welcome to <%= title %></p>
 11
 12     <div class="input-group mb-3">
 13     <label class="input-group-text" for="file-upload">Upload</label>
 14     <input type="file" class="form-control" id="file-upload" name="new-image" >
 15     </div>
 16     <div id="progres" class="progress" role="progressbar" aria-label="Animated striped example" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
 17         <div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 0%"></div>
 18     </div>
 19     <div id="image-list"></div>
 20
 21     <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
 22     <script src="https://code.jquery.com/ui/1.14.1/jquery-ui.min.js" integrity="sha256-AlTido85uXPlSyyaZNsjJXeCs07eSv3r43kyCVc8ChI=" crossorigin="anonymous"></script>
 23
 24     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js" integrity="sha384-k6d4wzSIapyDyv1kpU366/PK5hCdSbCRGRCMv+eplOQJWyd1fbcAu9OCUj5zNLiq" crosso    rigin="anonymous"></script>
 25     <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorig    in="anonymous"></script>
 26     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.min.js" integrity="sha384-VQqxDN0EQCkWoxt/0vsQvZswzTHUVOImccYmSyhJTp7kGtPed0Qcx8rK9h9YEgx+" crossorigin="    anonymous"></script>
 27     <script
 28             src="https://blueimp.github.io/jQuery-File-Upload/js/jquery.fileupload.js"
 29             crossorigin="anonymous"></script>
 30     <script>
 31            $(function () {
 32                 $('#file-upload').fileupload({
 33                     url: '/images',
 34                     dataType: 'json',
 35                     progressall: function (e, data) {
 36                         var progress = parseInt(
 37                             (data.loaded / data.total) * 100,
 38                             10
 39                         );
 40                         $('#progress .progress-bar').css(
 41                             'width',
 42                             progress + '%'
 43                         );
 44                     },
 45                 });
 46             });
 47             $.getJSON('/images', function (data) {
 48                 $.each(data, function (i, e) {
 49                     var img = $('<img>');
 50                     img.attr(
 51                         'src',
 52                         'https://d35jky9c8nnx1r.cloudfront.net/' + e.Key
 53                     )
 54                         .attr({ width: '200px', height: '200px' })
 55                         .addClass('img-thumbnail');
 56
 57                     $('#image-list').append(img);
 58                 });
 59             });
 60     </script>
 61
 62   </body>
 63 </html>

🧪 실전 사례

  • 웹사이트에서 프로필 사진이나 게시판 이미지 업로드할 때
  • 사용자 업로드 이미지를 효율적으로 관리 및 CDN을 통해 빠르게 제공할 때

🧠 고급 팁 or 자주 하는 실수

  • 📍 IAM 역할 설정: EC2 인스턴스가 S3에 접근할 권한을 반드시 IAM 역할을 통해 부여해야 함.
  • 📍 리전 주의: S3 버킷과 EC2 인스턴스의 리전을 동일하게 설정해야 문제 방지.
  • 📍 CloudFront 설정: 반드시 OAC를 설정하고 S3 버킷 정책 업데이트를 잊지 말 것.

마무리 요약 및 복습 포인트

  • views는 프론트, routes는 백엔드 담당
  • 파일 업로드는 multer 라이브러리 사용
  • S3 업로드는 AWS SDK로 구현
  • IAM 역할을 통해 EC2에 S3 접근 권한 부여
  • CloudFront로 이미지에 안전하게 접근 가능하게 설정

📌 실습하면서 발생하는 오류 메시지를 정확히 읽고 원인 파악 습관화!