[AWS] S3에 이미지 추가/삭제/복사(이동) 및 설정하는 방법
본문 바로가기
더 알아보기/기능

[AWS] S3에 이미지 추가/삭제/복사(이동) 및 설정하는 방법

by 은돌1113 2023. 7. 13.

 

입사한 지 1년 3개월 만에 드디어 AWS S3의 추가/삭제/복사 기능을 구현했다.

팀장님 계실 때도 못했던 걸... ChatGTP와 팀원분과의 합동으로 해결해 냈다.

무한한 감사를...

 

이 글이 AWS S3에 늪에서 헤매고 있는 개발자분들에게 도움이 되기를 바라며...

  • AWS S3 설정하는 방법
  • next/image src에 AWS S3 이미지 삽입하는 방법
  • 이미지 추가
  • 이미지 삭제
  • 이미지 복사

 

AWS S3 설정하는 방법

이 부분은 다른 블로그에도 설명이 잘 되어 있어서 간결하게 넘어갑니다

  • AWS 접속 후 로그인하고 S3에 버킷에 들어간다.

 

무료 클라우드 컴퓨팅 서비스 - AWS 프리 티어

Q: AWS 프리 티어란 무엇입니까? AWS 프리 티어는 고객에게 서비스별로 지정된 한도 내에서 무료로 AWS 서비스를 살펴보고 사용해 볼 수 있는 기능을 제공합니다. 프리 티어는 12개월 프리 티어, 상

aws.amazon.com

  • 새로운 버킷 생성
  • 버킷 상세로 들어가서 권한에 버킷 정책과 CORS(Cross-origin 리소스 공유)를 설정해 준다.
    • S3에서는 거의 아래와 같은 형태로 작성하는 것 같다. Actions는 필요한 게 따로 있으면 그거만 넣어도 됨
    • CORS 설정 때 "AllowOrigins"에 실서버 주소 안 넣으면 테스트할 때 cors error가 발생한다. (적용되기까지 시간이 조금 걸리는 Actions가 있는 것 같음 > 정확하지 않음)
// CORS (Cross-origin 리소스 공유)

[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "HEAD",
            "PUT",
            "DELETE"
        ],
        "AllowedOrigins": [
          "localhost:3000",
          ...실서버 주소
          
        ],
        "ExposeHeaders": [],
        "MaxAgeSeconds": 3000
    }
]

 

next/image src에 AWS S3 이미지 삽입하는 방법

  • AWS S3에 있는 버킷 주소를 next/image src에 삽입하려고 하면 아래와 같은 오류가 발생한다.
  • 그럴 때 next.config.js에 아래 코드를 삽입해 주면 된다.

// next.config.js

// next/image에 aws s3 이미지를 사용하기 위한 설정
images: {
  domains: ["sugarman-renewal.s3.ap-northeast-2.amazonaws.com"],
},

 

출처

 

[Next.js] image URL 사용(Amazon S3)

Next.js image URL 사용 방법

velog.io

 

이미지 추가/삭제/복사(이동)

  • 이 부분에서 새롭게 알게 된 것
    • 폴더 안에 이미지/파일이 없으면 폴더가 자동으로 삭제된다.
    • 이미지 복사 기능 구글링 할 때 copyObject라는 action을 사용하면 된다고 해서 한참을 찾았지만 존재하지 않는 설정이었고 대신 getObject와 putObject를 섞어서 구현할 수 있다는 걸 알게 됨
    • uploadToS3에서 2023.07.24 이전에는 아래처럼 forEach에 async/await를 사용했는 데 종종 이미지가 업로드 안되는 경우가 생겨서 알아보니 .forEach에 async/await를 사용하면 코드가 예상대로 작동하지 않을 수 있어 for...of 또는 map, await Promise.all()을 사용하는 것이 좋다고 하여 전체 코드처럼 변경했다.

 

forEach를 사용한 수정 전 코드

const uploadToS3 = async (path, files) => {
  const promises = []
  const fileNamesArr = []

  await files.forEach((file) => {
    if (file.file) {
      let params = {
        ACL: "public-read",
        Key: path + "/" + file.key,
        Body: file.file,
      }
      const uploadTask = myBucket.putObject(params)

      promises.push(uploadTask)
      fileNamesArr.push(file.key)

      uploadTask
        .on("httpUploadProgress", (evt) => {
          console.log("파일 업로드", Math.round((evt.loaded / evt.total) * 100))
        })
        .send((err) => {
          if (err) {
            // handle the error here
            console.log("err", err)
          }
        })
    }
  })
  Promise.all(promises)
  return fileNamesArr
}

 

전체 코드

import AWS from "aws-sdk"

AWS.config.update({
  accessKeyId: "#",
  secretAccessKey: "#",
  region: "#",
})

const s3 = new AWS.S3()

const myBucket = new AWS.S3({
  params: { Bucket: "#" },
})

// 이미지 업로드 함수
const uploadToS3 = async (path, files) => {
  const promises = []
  const fileNamesArr = []

  for (const file of files) {
    if (file.file) {
      let params = {
        ACL: "public-read",
        Key: path + "/" + file.key,
        Body: file.file,
      }
      const uploadTask = myBucket.putObject(params)

      promises.push(uploadTask)
      fileNamesArr.push(file.key)

      uploadTask
        .on("httpUploadProgress", (evt) => {
          console.log("파일 업로드", Math.round((evt.loaded / evt.total) * 100))
        })
        .send((err) => {
          if (err) {
            // handle the error here
            console.log("err", err)
          }
        })
    }
  }

  await Promise.all(promises) // 모든 프로미스가 해결될 때까지 기다림
  return fileNamesArr
}

// 이미지 제거 함수
const deleteToS3 = async (path) => {
  await myBucket.deleteObject(
    {
      Bucket: "#",
      Key: path, // ex) work/branch/오픈 순서/파일 이름
    },
    (err, data) => {
      if (err) {
        throw err
      }
      console.log("이전 파일 삭제 성공", data)
    },
  )
}

// 이미지 이동 함수
const copyToS3 = async (copySource, key) => {
  const copyParams = {
    Bucket: "#", // 버킷 이름
    CopySource: copySource, // 복사할 원본 객체의 경로 (버킷 이름을 제외한 경로만)
    Key: key, // 복사 당할 대상 객체의 경로 (버킷 이름을 제외한 경로만)
  }
  // ※ 주의 : 경로라고 해서 "#/1", "#/11"까지만 넣었는 데 그렇게 하면 "copying object: NoSuchKey" error가 발생한다. 꼭 파일 이름까지 넣어줘야 함
  // Error copying object: NoSuchKey: The specified key does not exist. > 원본 객체가 존재하지 않을 경우 발생

  // AWS S3에 copyObject 기능이 없기 때문에 getObject와 putObject를 섞어서 구현해야 함
  try {
    // getObject()를 사용하여서 s3에 있는 파일 정보를 Body에 받아온다.
    /*
      ※ 주의 : uploadToS3, deleteToS3에서는 myBucket을 사용했는 데 copyToS3에서는 s3를 사용해야 한다.
      왜냐하면 myBucket에서는 객체를 생성해서 params 속성을 사용하여 버킷 이름을 설정하고 있고, copyParams에서 한번 더 Bucket을 사용하기 때문에 따로 생성해야 한다.

      출처 : ChatGTP
    */
    const { Body } = await s3
      .getObject({
        Bucket: copyParams.Bucket,
        Key: copyParams.CopySource,
      })
      .promise()

    // Body가 있을 경우 putObject()를 사용하여서 copyParams.key를 Key에 넣어서 새로운 폴더를 생성하고 Body에 위에서 불러온 원본 객체의 정보를 Body에 넣어준다.
    await s3
      .putObject({
        Bucket: copyParams.Bucket,
        Key: copyParams.Key,
        Body: Body,
      })
      .promise()

    console.log("Object copied successfully")
  } catch (error) {
    console.log("Error copying object:", error)
  }
}

export { uploadToS3, deleteToS3, copyToS3, myBucket }

 

Access denined 오류가 발생하는 경우

 

[AWS CLI] Github에 Access Key 노출

IAM User AccessDenied

cloudest.oopy.io

 

댓글