1편으로 AWS 인프라 사용하여 실무에서도 활용되고 있는 이미지 리사이징을 구현해봅니다.
필요한 전체 코드나 과정은 AWS 공식 가이드를 참고해서 했습니다.
가이드에서는 Source bucket과 Destination bucket 분리하여 사용했는데,
포스팅에서는 간단하게 Bucket 하나에 디렉토리(origin, resize) 구분해서 작업합니다.
흐름은 아래와 같습니다.
① S3 bucket의 origin/ 디렉토리에 원본 이미지 업로드
② 이벤트 감지해서 Lambda 실행, 이미지 리사이징 후 S3의 resize/ 디렉토리 저장
③ 사용자는 CloudFront 도메인 주소로 S3 이미지 호출
S3 버킷 생성
region은 서울로 하고 외부 URL로 S3 직접 접근을 못하도록 퍼블릭 엑세스 차단 설정합니다.
원본과 리사이징된 이미지를 같은 bucket 경로에 위치하게 할 경우 무한 재귀 호출될 수 있습니다.
origin 디렉토리에는 원본 이미지를, resize 디렉토리에는 Lambda 통해 결과 이미지를 저장하도록 합니다
Lambda 함수 생성
S3 버킷에 이미지 업로드 될 때 마다 실행될 람다 함수를 생성합니다
S3 접근 권한 설정
기본 Lambda 권한을 가진 새 역할을 선택하여 Lambda를 생성했습니다. S3에 대한 읽기, 쓰기 권한을 추가하도록 합니다.
(참고. 기존 역할을 사용한다면 CloudWatch에 대한 권한과 S3 권한을 추가하면 됩니다.)
신규 생성한 람다의 상세 페이지에서 [구성 > 권한] 탭으로 이동하면 바로가기 링크가 있습니다.
[편집] 버튼을 눌러서 권한을 추가하도록 합니다.
Lambda에서 S3 이미지 파일을 읽고, 쓰기 위한 권한을 아래와 같이 추가하도록 합니다. (만약 S3 전체 권한을 부여하고 싶다면 AmazonS3FullAccess를 추가하면 됩니다.)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
이미지 리사이징 후 업로드 함수가 동작하는데 다소의 시간이 소요될 수 있으므로, 람다의 기본 설정 편집에서 제한 시간을 늘려보았습니다. 기존 역할 부분에 보면 "Amazon CloudWatch Logs 업로드 권한이 있어야 한다"고 적혀있는데, 따로 역할 생성하여 관리하는 경우 S3 권한과 함께 CloudWatch Logs 권한도 추가하면 됩니다.
Lambda 함수 실행하기 위한 트리거 생성
S3 이미지 업로드(PUT) 될 때 트리거가 동작하여 람다 실행할 수 있도록 설정합니다.
접두사의 경우 origin/ 디렉토리를 설정해서 해당 디렉토리에 이미지가 추가될 때만 동작하도록 설정했습니다. 그리고 처음 언급한 것과 같이 재귀 호출에 대한 위험과 비용에 대한 설명이 나와 있는데 S3 디렉토리를 분리해뒀기 때문에 체크만 하고 넘어갑니다.
람다 스크립트 작성 및 업로드
람다 생성시 nodejs 20.x 선택해서 node 버전은 v20.11.0으로 작업했습니다.
$ mkdir lambda-image-resize
$ npm init -y
index.js
이벤트 트리거 되었을 때 s3 원본 이미지 파일을 가져와 sharp 라이브러리로 변환하고, s3 저장하는 내용입니다.
const AWS = require('aws-sdk');
const sharp = require('sharp');
const s3 = new AWS.S3(); // 람다가 접근할 수 있는 디렉토리가 하나
exports.handler = async (event, context, callback) => {
const bucket = event.Records[0].s3.bucket.name;
const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
// 지원 파일 확장자가 아닌 경우
const fileType = key.split('.').pop().toLowerCase();
if (fileType !== 'jpg' || fileType !== 'jpeg') {
console.log(`Unsupported file type: ${fileType}`);
return;
}
// 원본 이미지 호출, 리사이징
const originalImage = await s3.getObject({ Bucket: bucket, Key: key }).promise();
let resizedImage;
try {
resizedImage = await sharp(originalImage.Body)
.resize({ width: 200, height: 200 })
.toFormat('jpeg')
.toBuffer();
} catch (error) {
console.log(error);
return;
}
// 결과 파일 저장
try {
const newKey = key.replace('origin/', 'resize/');
await s3.putObject({
Bucket: bucket,
Key: newKey,
Body: resizedImage,
ContentType: 'image/jpeg'
}).promise();
} catch(error) {
console.log(error);
return;
}
console.log(`Successfully Image resized and uploaded to: ${newKey}`);
};
package.json
{
"name": "lambda-image-resize",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"aws-sdk": "^2.1691.0",
"sharp": "^0.33.5"
}
}
스크립트 작업 후 zip 파일을 생성합니다
$ npm install sharp aws-sdk
// 리눅스 환경 경우
$ npm install --arch=x64 --platform=linux --target=16x sharp aws-sdk
// zip 파일 압축
$ zip -r lambda-image-resize .
Lambda에서 파일 업로드를 하는데 S3에 zip 파일 업로드 후 연결하도록 합니다. (기본 업로드 파일 용량 제한 : 10MB)
직접 이미지 업로드 테스트
S3 origin 디렉토리에 직접 이미지 업로드 하여 람다 함수가 정상 실행되는지 확인해봅니다.
(참고로 람다 상세 페이지에서 "테스트" 탭에서도 실행 테스트 해보실 수 있습니다)
원본 이미지
origin 디렉토리 업로드
결과
정상적으로 람다 함수 실행되서 resize 디렉토리에 결과 파일이 저장되어 있는 것을 확인했습니다.
(참고로 람다 상세 페이지에서 "모니터링" 탭에서 실행 로그 및 결과 확인할 수 있습니다)
이미지 사이즈도 정상적으로 변환되었네요.
CloudFront - S3 연결하기
이번 포스팅은 단순하게 테스트만 하는거라서 ssl, 도메인 발급하지 않고 cloudfront 생성시 제공되는 도메인 주소 사용합니다.
CloudFront 배포 생성을 합니다
*원본 엑세스 제어 설정(권장)
-S3 컨텐츠를 CloudFront 통해서만 볼 수 있도록 제한합니다.
-S3 정적 컨텐츠 URL을 바로 접근하는게 아니라 CDN 통해서 접근하는 것입니다.
-추가로 S3 Bucket 정책에서 CloudFront 접근을 허용하도록 수정이 필요합니다.
WAF는 비활성화로 두고 나머지는 기본 설정 그대로 하여 CloudFront를 생성합니다
아래 CloudFront 도메인 주소로 이미지 호출하게 됩니다. 그 전에 먼저 s3 권한 수정하도록 합니다
생성한 s3의 권한으로 들어가서 버킷 정책 편집으로 이동합니다. 친절하게 CloudFront 신규 생성시 알림창으로 AWS에서 json과 바로가기 링크를 제공해줍니다. 그대로 복사 붙여넣기 하시면 됩니다.
{
"Version": "2008-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::ljw-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::367667470385:distribution/E1N1GF4ZBJDFXL"
}
}
}
]
}
최종적으로 이미지 정상 호출 확인할 수 있었습니다.
다음 포스팅에서는 람다 엣지를 사용하여 이미지 리사이징 하는 방법에 대해 살펴보겠습니다.
Reference
AWS 공식 - 자습서: Amazon S3 트리거를 사용하여 썸네일 이미지 생성
'공부 > AWS' 카테고리의 다른 글
[AWS] Lambda@Edge 활용하여 image resize - (2) (4) | 2024.09.08 |
---|---|
[AWS] EC2 서버 application heap dump 생성/다운로드 (with wsl2) (0) | 2023.01.16 |
[AWS] EC2 와 RDS 연결/접속 하기 (0) | 2023.01.14 |
[AWS] MFA 설정 가이드 (0) | 2022.03.14 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!