aws s3는 aws내에서 제공하는 스토리지 서비스이다.
즉, 사진이나 다른 종류들의 파일들을 버킷
이라는 곳에 저장해서 사용할 수 있다.
로컬환경이었을땐 그냥 폴더 내에서 바로 이미지를 저장해주었었는데, 배포후에 다시 경로를 지정해주어야하기도하고 공부도 할겸 s3로 대체 해본다.
aws 내에서 s3창에 들어오면 버킷생성이 있다.
버킷이 생성됐다. (설정들은 전부 기본값으로 해줬다.)
aws 내에서 IAM(Identity and Acceess Management) 은 간단히 말하면 선택적 권한 부여
라고 생각하면 된다.
현재는 루트 계정을 사용하고 있는데 예를 들어서 회사에서 업무를 하고있다고 가정해보자.
이 s3를 여러명이서 사용해야하는데 루트 계정을 만약 모두가 사용한다면 문제가 발생할수도 있다.
왜냐면 루트 계정은 말그대로 본계정이기때문에, 마음대로 인스턴스나 버킷을 삭제, 생성할수도 있기 때문이다.
넓은 범위의 IAM 설정은 이렇지만, s3내에서는 사실 어플리케이션을 사용하면서 해당 리소스에 접근할수 있도록 권한을 주는것이다.
aws 콘솔창내에서 IAM을 선택하고 정책으로 들어가자.
이미 많은 정책들이 존재하지만 새로운 정책 생성을 클릭해주자.
여기서 s3에 대한 권한을 설정해줄수가 있다.
예를 들어서 어플리케이션 사용자는 이미지를 가져와야하므로 읽기 정책에서 GetObject
, 또한 게시글을 작성할수 있어야하므로 쓰기 정책에서PutObject
를 해줄수 있다.
그리고 게시글을 삭제한다고 가정할때 버킷에 있는 이미지를 동시에 삭제해주어야하므로 DeleteObject
도 필요하다.
또한 리소스에서 ARN추가를 클릭한다.
ARN은 aws내에서 리소스에 고유하게 식별할수 있는 이름을 지정해주는 것이라고 생각하면 된다.
아까 만든 버킷이름을 넣어주고 object는 모든 object name을 선택해주었다.
그 후에 해당 정책에 대한 이름과 설명을 또 추가해주는 칸이 있는데, 거기서 내용을 입력해주면 IAM 설정이 끝난다.
그러면 이 정책을 이용할 사용자를 만들어주어야한다.
사용자를 만들어주고, 아까 만든 정책을 연결해주기 위해서 직접 정책 연결을 선택한다.
그리고 해당 정책을 연결시켜준다.
만들어진 사용자에 들어가서 보안자격증명 칸을 보면 액세스키 만들기
가 있다.
AWS외부에서 사용하는거니 외부 어플리케이션을 선택해준다.
이렇게 되면 액세스 키와 비밀 액세스키가 생성되는데, 이 값들을 잘 기록해놓아야한다.
비밀 액세스 키같은 경우 저 창을 벗어나면 확인이 불가능하다.
aws-sdk/client-s3를 설치해주어서 s3쪽과 상호작용할수 있도록한다.
import { S3Client } from '@aws-sdk/client-s3';
dotenv.config();
const bucketName = process.env.BUCKET_NAME || '';
const bucketRegion = process.env.BUCKET_REGION || '';
const accessKey = process.env.ACCESS_KEY || '';
const secretAccessKey = process.env.SECRET_ACCESS_KEY || '';
const s3 = new S3Client({
region: bucketRegion,
credentials: {
accessKeyId: accessKey,
secretAccessKey: secretAccessKey,
},
});
express 내에서 환경변수로 담은 s3 버킷 설정 값들과 액세스 키값들을 넣어준다.
그럼으로써 s3 객체를 만들어주었다.
npm install --save multer-s3
npm i @types/multer-s3
multer-s3 패키지를 설치해준다.
const s3 = new S3Client({
region: bucketRegion,
credentials: {
accessKeyId: accessKey,
secretAccessKey: secretAccessKey,
},
});
const postThumbnailImageUpload = multer({
storage: multerS3({
s3: s3,
bucket: bucketName,
metadata: function (req, file, cb) {
cb(null, { fieldName: file.fieldname });
},
key: function (req, file, cb) {
const filename = `${Date.now().toString()}-${file.originalname}`;
cb(null, filename);
},
}),
});
export default postThumbnailImageUpload;
유의할점은 버킷 내의 파일명은 고유해야한다. uuid로 구현하는 케이스도 있는데, 본인은 그냥 Date.now()를 이용해서 값을 만들어주었다.
그 후에 컨트롤러 함수에서 req.file
을 통해 해당 이미지 정보에 접근할수 있다.
설정을 잘 해주었는데도 서버에서 500에러를 반환했다.
문제는 프론트측에서 요청을 할때 요청헤더에 Content-Type 이부분을 json으로 명시해주어서였다.
이렇게 하면 서버쪽에서 폼 데이터를 json파일로 변환하려 하기때문에 에러가 발생한다.
요청헤더에 단순히 토큰값만 담아서 보내주고, 백엔드에선 토큰 검증만 해주는식으로 구현해주었다.
이제 백엔드 측에서 req.file을 출력해보면 이런식으로 나온다.
또 Sharp
라이브러리를 사용해서, 해당 이미지를 리사이징을 해준후에 리사이징 된 이미지로 교체를 해준다.
const uploadCompressedImageByKey = async (key: string) => {
try {
const compressedKey = `compressed_${key}`;
const config = {
Bucket: bucketName,
Key: key,
};
// S3에서 이미지 불러오기
const { Body } = await s3.send(new GetObjectCommand(config));
if (!Body) throw new Error('ERROR MESSAGE');
// 스트림을 버퍼로 변환
const buffer = await streamToBuffer(Body);
// 이미지 리사이징
const imageBuffer = await sharp(buffer).resize({ height: 1920, width: 1080 }).toBuffer();
// 리사이징한 이미지 업로드
await s3.send(
new PutObjectCommand({
Bucket: bucketName,
Key: compressedKey,
Body: imageBuffer,
ContentType: 'image/jpeg',
}),
);
// 기존 이미지 삭제
await s3.send(new DeleteObjectCommand(config));
console.log('compressedKey : ', compressedKey);
return compressedKey;
} catch (error) {
console.error('이미지를 찾는데에 에러가 발생한 key', error);
throw error;
}
};
cloudfront는 aws의 CDN(Content Delivery Network)
서비스이다.
만약 aws의 리전을 미국으로 지정했다고 가정하고, 서울에서 이미지 요청을 한다고 생각해보면, 리전이 서울일때보다 더 시간이 오래 걸릴 것이다.
cloud front를 사용하면 리전을 서울로 지정하고 파일을 받는 속도를 더욱 향상시킬수 있으며, 캐싱, 성능최적화, 보안 등 여러 기능들을 제공해주기때문에 정적 파일들을 더욱 효율적으로 다룰수 있다.
aws 내에서 cloudfront 입력 후 생성을 클릭한다.
해당 도메인과 이름은 사용하고싶은 s3버킷으로하고, 그 후에 원본 액세스 제어 설정을 클릭해준다.
저 버튼을 누르면 정책이 없다고 나올수 있는데, 정책을 새롭게 s3버킷에 맞도록 하나 생성해준다.
여기서 서울 리전을 선택해준다.
이는 설명에 써있듯이 캐싱 레이어를 하나 추가해줌으로써 파일 전송 속도를 높여주고 리소스 소모를 줄여준다.
여기서 뷰어 프로토콜 정책을 Redirect HTTP to HTTPS
로 변경해준다.
또한 기본값 루트 객체를 index.html로 설정해준다.
만약 주소창에 /를 입력하고 들어온다면 index.html을 반환해준다.
모든 설정이 완료되면 정책 업데이트를 하라고 하는데, 저 정책을 복사해서 s3 버킷 정책에 붙여넣기를 해주면 된다.
또한 퍼블릭 액세스 차단을 해주어야한다.
해당 주소로 접속하니, 해당 이미지가 정상적으로 나온다