입사한 지 1년 3개월 만에 드디어 AWS S3의 추가/삭제/복사 기능을 구현했다.
팀장님 계실 때도 못했던 걸... ChatGTP와 팀원분과의 합동으로 해결해 냈다.
무한한 감사를...
이 글이 AWS S3에 늪에서 헤매고 있는 개발자분들에게 도움이 되기를 바라며...
- AWS S3 설정하는 방법
- next/image src에 AWS S3 이미지 삽입하는 방법
- 이미지 추가
- 이미지 삭제
- 이미지 복사
AWS S3 설정하는 방법
이 부분은 다른 블로그에도 설명이 잘 되어 있어서 간결하게 넘어갑니다
- AWS 접속 후 로그인하고 S3에 버킷에 들어간다.
- 새로운 버킷 생성
- 버킷 상세로 들어가서 권한에 버킷 정책과 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"],
},
출처
이미지 추가/삭제/복사(이동)
- 이 부분에서 새롭게 알게 된 것
- 폴더 안에 이미지/파일이 없으면 폴더가 자동으로 삭제된다.
- 이미지 복사 기능 구글링 할 때 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 오류가 발생하는 경우
'더 알아보기 > 기능' 카테고리의 다른 글
[React + Typescript] 부모 자식 간에 useRef 사용하는 방법 (0) | 2023.10.19 |
---|---|
[React-Toastify] 토스트 기능 구현해보기 (0) | 2023.09.26 |
axios 여러 개 요청하기 (multiple request) (0) | 2023.03.30 |
document scroll event 대신 new IntersectionObserver 사용해보기 (0) | 2023.03.13 |
[react-datepicker] 달력 구현 및 주말/양력 휴일/음력 휴일 비활성화 하기 (0) | 2023.01.20 |
댓글