[AWS] S3 파일 업로드 - node.js

2024. 8. 29. 12:38·Server/node.js

1. S3 만들기

1. S3로 이동한다.

 

 

 

2. [버킷 만들기]로 이동한다.

 

 

 

 

3. 원하는 이름을 입력한다.

 

 

 

4. '모든 퍼블릭 액세스 차단'을 해제하고 퍼블릭 상태가 될 수 있음을 알고 있다는 박스에 체크 후 생성한다.

 

 

 

2. 직접 파일(이미지) 업로드 해보기

1. 생성된 버킷으로 이동 > [업로드] 선택한다.

 

 

 

2. [파일 추가]를 통해 추가하고 [업로드]를 한다.

 

 

 

 

3. 이미지 정보에 들어오면 객체 URL을 확인할 수 있다.

 

 

하지만 링크 이동하면 아래처럼 에러가 뜬다.

 

 

따라서 퍼블릭하게 접근할 수 있도록 추가 설정을 해야 한다.

 

 

 

4. 내 버킷 > 속성으로 간다.

 

 

 

 

5. 정적 웹 사이트 호스팅 > 편집으로 이동한다.

 

 

 

 

6. 아래처럼 설정을 변경한다.

 

 

 

 

7. 권한으로 이동한다.

 

 

 

8. 아래처럼 버킷 정책을 수정한다.

{
    "Version": "2012-10-17",
    "Id": "Policy1689489055171",
    "Statement": [
        {
            "Sid": "Stmt1689489029299",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "[여러분들의 S3 버킷 arn]/*"
        }
    ]
}

 

 

 

9.  아까 객체 URL을 실행하면 잘 되는 것을 확인할 수 있다.

 

 

 

 

3 Node.js에서 S3에 사진 업로드 세팅

1. 모듈을 다운받는다.

npm install aws-sdk

 

 

 

2. IAM으로 이동 > [사용자 생성]으로 이동한다. 

 

 

 

 

 

 

3. 아래와 같이 권한을 설정한다.

 

 

 

 

 

4. 보안 자격 증명 > 액세스 키 > 액세스 키 만들기로 이동한다.

 

 

 

5. AWS 외부에서 실행되는 애플리케이션을 선택한다.

 

 

 

 

6. .env 파일에 다음과 같이 저장한다.

  • 만약 elastic beanstalk을 통해 배포한다면 환경 > 구성 > 환경 속성 편집을 통해 추가해줘야 한다.
AWS_S3_REAGION=ap-northeast-2
AWS_S3_BUCKET_NAME=my-first-project-s3
AWS_S3_ACCESS_KEY=내 access key
AWS_S3_SECRET_KEY=내 secret key

 

 

 

7. uuid 사용을 위한 모듈을 설치한다.

npm install uuid

 

 

 

 

8. 버킷 내 파일 디렉토리 생성한다.

  • S3 대시보드에 [폴더 만들기] > 원하는 폴더 이름 입력 후 생성한다.

 

 

 

9. ./src/middleware/image.uploader.js에 다음과 같이 코드를 넣는다.

import AWS from "aws-sdk";
import multer from "multer";
import multerS3 from "multer-s3";

import { createUUID } from "./uuid.js";

import path from "path";
import dotenv from "dotenv";

import { BaseError } from "../../config/error.js";
import { status } from "../../config/response.status.js";

dotenv.config(); // .env 파일 사용

export const s3 = new AWS.S3({
  region: process.env.AWS_S3_REAGION,
  accessKeyId: process.env.AWS_S3_ACCESS_KEY,
  secretAccessKey: process.env.AWS_S3_SECRET_KEY,
});

// 확장자 검사 목록
const allowedExtensions = [".png", ".jpg", ".jpeg", ".bmp", ".gif"];

export const imageUploader = multer({
  storage: multerS3({
    s3: s3, // S3 객체
    bucket: process.env.AWS_S3_BUCKET_NAME, // Bucket 이름
    contentType: multerS3.AUTO_CONTENT_TYPE, // Content-type, 자동으로 찾도록 설정
    key: (req, file, callback) => {
      // 파일명
      const uploadDirectory = req.query.directory ?? ""; // 디렉토리 path 설정을 위해서
      const extension = path.extname(file.originalname); // 파일 이름 얻어오기
      const uuid = createUUID(); // UUID 생성
      // extension 확인을 위한 코드 (확장자 검사용)
      if (!allowedExtensions.includes(extension)) {
        return callback(new BaseError(status.WRONG_EXTENSION));
      }
      callback(null, `${uploadDirectory}/${uuid}_${file.originalname}`);
    },
    acl: "public-read-write", // 파일 액세스 권한
  }),
  // 이미지 용량 제한 (5MB)
  limits: { fileSize: 5 * 1024 * 1024 },
});

 

 

 

 

10. ./src/middleware/uuid.js 에 다음과 같이 코드를 넣는다. 

import { v4 } from "uuid";

// uuid 생성
export const createUUID = () => {
  const tokens = v4().split("-");
  console.log("token", tokens);
  return tokens[2] + tokens[1] + tokens[0] + tokens[3] + tokens[4];
};

 

 

11. route에 아래 코드 추가

imageUploader.single('image')	// 단일 파일
imageUploader.array("image", 20)	// 여러 파일 (최대 20개)

 

 

 

4 Node.js에서 S3에 사진 업로드 코드 (feat. 리뷰 생성 API)

0. 직접 리뷰 생성 API로 예시를 들어보겠다.

 

1. ./src/models/review.sql.js

// 리뷰 생성
export const insertReviewSql =
`
INSERT INTO review (score, body, created_at) 
VALUES (?, ?, NOW());
`


// 리뷰 사진 생성
export const insertReviewImageSql =
 `
 INSERT INTO review_image (image_url, review_id) VALUES ?
 `

 

 

 

 

2. ./src/models/review.dao.js

import { pool } from "../../config/db.connect.js";
import { status } from "../../config/response.status.js";
import { BaseError } from "../../config/error.js";
import { insertReviewSql, insertReviewImageSql } from "../models/review.sql.js";

// 리뷰 생성
export const addReviewDao = async (reviewData) => {
  try {
    const conn = await pool.getConnection();

    const [review] = await pool.query(insertReviewSql, [
      reviewData.score,
      reviewData.body,
    ]);

    conn.release();
    return review.insertId;
  } catch (err) {
    throw new BaseError(status.PARAMETER_IS_WRONG);
  }
};

// 리뷰 생성 - 이미지
export const addReviewImagesDao = async (reviewId, imageList) => {

  try {
    const conn = await pool.getConnection();

    const reviewImages = imageList.map((location) => {
      return [location, reviewId];
    });

    await pool.query(insertReviewImageSql, [reviewImages]);

    conn.release();
  } catch (err) {
    throw new BaseError(status.PARAMETER_IS_WRONG);
  }
};

 

 

 

3. ./src/providers/review.provider.js

import { addReviewDao, addReviewImagesDao } from "../models/review.dao.js"

// 리뷰 생성
export const addReviewProvider = async (body, imageList) => {
    try{
        const reviewData = await addReviewDao({
            score: body.score, 
            body: body.body, 
        }, imageList);

        if(imageList && imageList.length > 0){
            await addReviewImagesDao(reviewData, imageList);
        }
        return "생성되었습니다.";
    } catch(err){
        throw err;
    }
}

 

 

 

 

4. ./src/controllers/review.controller.js

import { response } from "../../config/response.js";
import { status } from "../../config/response.status.js";
import { addReviewProvider } from "../providers/review.provider.js";

// 이벤트 생성
export const addReviewController = async (req, res, next) => {
  console.log("리뷰 생성을 요청하였습니다!");
  console.log("body", req.body);
  const reviewImage = req.files;

  const imageList = [];
  if (reviewImage) {
    reviewImage.forEach((v) => {
      imageList.push(v.location);
    });
  }

  res.send(
    response(
      status.SUCCESS,
      await addReviewProvider(req.body, imageList)
    )
  );
};

 

 

 

5. ./src/routes/review.route.js

import express from "express";
import asyncHandler from 'express-async-handler';
import { addReviewController } from "../controllers/review.controller.js";
import { imageUploader } from "../middleware/image.uploader.js";

export const reviewRouter = express.Router();

reviewRouter.post('/', imageUploader.array("image", 20), asyncHandler(addReviewController));   // 생성

 

 

 

6. index.js

( ... )
import { reviewRouter } from './src/routes/review.route.js';

( ... )

app.use('/reviews', reviewRouter);

( ... )

 

 

 

 

 

7. ./swagger/review/swagger.yaml

paths:
  /reviews:
    post:
      tags:
        - Review
      summary: 리뷰 생성
      consumes:
        - multipart/form-data
      parameters:
        - in: query
          name: directory
          schema:
            type: string
            example: reviews
        - in: formData
          name: image
          type: array
          items:
            type: file
            format: binary
            description: 파일 업로드 (최소 0개, 최대 20개)
        - in: formData
          name: score
          type: integer
          schema:
            type: integer
            description: 점수
          required: true
        - in: formData
          name: body
          type: string
          schema:
            type: string
            description: 내용
          required: true
      responses:
        '200':
          description: 리뷰 생성 성공
          schema:
            type: object
            properties:
              isSuccess:
                type: boolean
                example: true
              code:
                type: integer
                example: 2000
              message:
                type: string
                example: "SUCCESS!"
              result:
                type: array
                example: "생성되었습니다."

 

 

 

 

** 참고용 내 코드

https://github.com/dokyung-kang/UMC_node.js/tree/feat%233

 

 

'Server > node.js' 카테고리의 다른 글

[node.js] Swagger 세팅  (0) 2024.08.31
[node.js] DB 연동  (0) 2024.08.31
[CICD] Node.js + Github Action + Elastic Beanstalk 무중단 배포 (3-完)  (0) 2024.08.28
[CICD] Node.js + Github Action + Elastic Beanstalk 무중단 배포 (2)  (0) 2024.08.28
[CICD] Node.js + Github Action + Elastic Beanstalk 무중단 배포 (1)  (0) 2024.08.28
'Server/node.js' 카테고리의 다른 글
  • [node.js] Swagger 세팅
  • [node.js] DB 연동
  • [CICD] Node.js + Github Action + Elastic Beanstalk 무중단 배포 (3-完)
  • [CICD] Node.js + Github Action + Elastic Beanstalk 무중단 배포 (2)
2월2
2월2
  • 2월2
    서벅돌의 성장일기
    2월2
  • 전체
    오늘
    어제
    • 분류 전체보기 (120)
      • TIL (2)
      • Server (28)
        • spring (7)
        • node.js (16)
        • 기타 (5)
      • App&Web (17)
        • Web (1)
        • Android (16)
        • iOS (0)
      • 공부 (59)
        • 깃&깃허브 (3)
        • 파이썬 (17)
        • 유니티 (4)
        • 자료구조 | 알고리즘 (15)
        • 자바 (3)
        • 운영체제 (8)
        • AI와 데이터 (9)
      • 대외활동 (12)
        • NPC 동아리 (1)
        • UMC 동아리 (11)
      • 대학교 (1)
        • 교직 (1)
      • 기타 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 글 관리
  • 링크

  • 공지사항

    • Notice
  • 인기 글

  • 태그

    Lua
    Unity
    java
    C
    자바
    유니티
    Python
    안드로이드
    루아
    파이썬
    kotlin
    mysql
    코틀린
    Android
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
2월2
[AWS] S3 파일 업로드 - node.js
상단으로

티스토리툴바