const AWS = require('aws-sdk');
const dotenv = require('dotenv');
dotenv.config(); // .env 환경변수 사용
// aws region 및 자격증명 설정
AWS.config.update({
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
region: 'ap-northeast-2',
});
// 자격증명 데이터를 따로 파일로 관리한다면 다음으로 호출할수 있다.
// AWS.config.loadFromPath('./config.json');
// S3 객체 얻기
const s3 = new AWS.S3();
위처럼 따로따로 생성자를 통해 서비스 객체를 얻을수 있지만, 바로 개별 서비스 클래스를 바로 import 할수도 있다. 바로 new S3() 사용
이런식으로 구성할경우 메모리를 절약할 수 있다는 장점이 있다.
AWS CLI 같은 경우, $ aws s3 rb s3://버킷명 --force 명령어를 통해 버킷안에 내용물이 있어도 강제적으로 삭제할 수가 있다.
그러나 기본적으로 AWS SDK 에서는 버킷 강제 삭제를 지원하지 않는다.
따라서 먼저 버킷의 내용물 목록을 조회한후, 내용물을 삭제하고, 그다음 버킷을 삭제하는 프로세스를 짜야 한다.
s3.listObjectsV2() : 버킷의 내용물(객체) 목록을 얻는다. s3.deleteObjects() : 버킷의 내용물(객체)들을 여러개 한꺼번에 삭제한다. 바로 뒤에서 자세히 배운다.
const bucketName = 'node-sdk-sample-123123123';
//* 버킷 삭제 (cli와 달리 --force 옵션이 없다. 따라서 직접 버킷 내용물을 지우고 삭제해줘야 한다.)
async function emptyS3Bucket(bucketName) {
// 해당 버킷의 내용물(객체)들을 얻어와 저장
const listedObjects = await s3.listObjectsV2({ Bucket: bucketName }).promise();
console.log('listedObjects: ', listedObjects);
// 만일 내용물이 없으면 빈 버킷이니 리턴
if (listedObjects.Contents.length === 0) return;
// ... 내용물이 있다면
const deleteParams = {
Bucket: bucketName,
Delete: { Objects: [] },
};
listedObjects.Contents.forEach(({ Key }) => {
// 객체 키명을 얻어와 deleteParams의 배열에 저장
deleteParams.Delete.Objects.push({ Key });
});
await s3.deleteObjects(deleteParams).promise(); // 객체 삭제
// 기본적으로 listObjectsV2는 최대 1000개까지의 객체만 얻어온다. 만일 내용물이 1000개 이상이 있을 경우 재귀적으로 처리
if (listedObjects.IsTruncated) await emptyS3Bucket(bucket);
}
await emptyS3Bucket(bucketName); // 버킷 내용물 비우기
await s3
.deleteBucket({ Bucket: bucketName })
.promise()
.then((data) => {
console.log('success : ', data);
})
.catch((error) => {
console.error(error);
});
SDK 객체 메소드
객체 목록 조회
const bucketName = 'node-sdk-sample-123123123';
//* 버켓의 객체 리스트 출력
let objectlists = [];
await s3
.listObjectsV2({ Bucket: bucketName })
.promise()
.then((data) => {
console.log('Object Lists : ', data);
for (let i of data.Contents) {
objectlists.push(i.Key);
}
console.log('objectlists : ', objectlists);
})
.catch((error) => {
console.error(error);
});
listObjects() 와 listObjectsV2() listObjectsV2는 listObjects의 개선 버젼이라고 보면 된다. 다만 내부 로직이 완전히 바뀌었기 때문에 기존 메소드를 업데이트하는 방식이 불가능하여 이렇게 아예 새로운 메서드명으로 추가하였다. 기본적으로 라이브러리는 구 코드의 호환성을 항상 고려해야 되기 때문에 불가피하게 생긴 조치라고 보면 된다.
객체 업로드
간단하게 파일명(keyName) 과 파일 내용(keyValue)을 지정하고 upload() 메소드로 보낸 예제이다.
버킷 폴더 지정하기 sdk 내에 폴더 지정에 대한 메서드는 없다. 다만 객체 이름에 들어가는 후행문자 /로 폴더를 구분해줄 수 있다. 객체 생성 시 key를 "uploads/file.txt"로 작성하면 자동으로 uploads라는 폴더를 생성하고, 그 안에 객체를 저장한다.
파일명이 한글일 경우 한글이 들어간 파일을 S3에 업로드할 경우, S3 콘솔에서는 한글을 출력하지만 , node에서 해당 객체를 읽을 경우 인코딩된 문자열이 나온다. 따라서 본인의 코드 구현 시 필요에 따라 decodeURIComponent()를 사용해 디코딩이 필요하다.
const bucketName = 'node-sdk-sample-123123123';
const keyName = 'hello_world.txt'; // 파일명
const keyValue = 'Hello World !!'; // 성능을 위해 파일 내용을 chunck로 나누어 읽는다.
//* 단순 객체 업로드
const objectParams = {
Bucket: bucketName,
Key: `imgFolder/${keyName}`, // 폴더 안에다가 업로드
Body: keyValue, // 파일 내용
};
await s3
.upload(objectParams)
.promise()
.then((data) => {
console.log('Upload Success! : ', data.Location);
})
.catch((error) => {
console.error(error);
});
위와 같이 해당 버킷 경로에 파일이 들어있음을 확인 할 수 있다.
하지만 만일 이미지 와 같은 대용량 파일을 S3에 업로드할때는 어떻게 할까?
이때 사용하는 것이 Node 모듈의 스트림 모듈 이다.
스트림을 통해 대용량 이미지 파일 내용을 읽어오고, 그 스트림 파일을 S3로 보내주면 알아서 파싱되어 버킷에 저장되게 된다.