AWS Identity and Access Management(IAM)은 AWS 리소스에 대한 액세스를 안전하게 제어할 수 있는 웹 서비스이다. IAM을 사용하면 사용자가 액세스할 수 있는 AWS 리소스를 제어하는 권한을 중앙에서 관리할 수 있다. IAM을 사용하여 리소스를 사용하도록 인증(로그인) 및 권한 부여(권한 있음)된 대상을 제어한다.
AWS IAM 콘솔로 이동해 왼쪽 사이드바에서 액세스 관리 -> 정책으로 이동
"정책 생성" 클릭
JSON 탭에서 밑에 내용대로 입력
"다음:태그" 클릭
태그는 생략한다.
이렇게 하면 IAM 정책 생성이 완료된다. 그 다음으로는 역할을 생성해 준다.
AWS IAM 콘솔로 이동해 왼쪽 사이드바에서 "액세스 관리" -> "역할"로 이동
"역할 만들기" 버튼을 클릭
신뢰할 수 있는 엔터티 유형에서 "AWS 서비스" 옵션을 선택한다.
사용 사례에서는 "Lambda"를 선택하고 "다음" 버튼을 클릭한다.
권한 추가 화면에서 IAM 정책 생성하기에서 생성해준 정책을 선택한 후에 "다음" 버튼을 클릭한다.
역할 이름 "ResizingImageRole"로 설정하고, 1단계 신뢰할 수 있는 엔터티 아래와 같이 편집해서 작성
6. 오른쪽 아래 "역할 생성" 버튼 클릭해서 생성 완료하기
AWS S3 콘솔로 이동하고 "버킷 만들기" 버튼 클릭
버킷 이름을 입력하고 AWS 리전은 서울로 설정을 한다.
퍼블릭 액세스 차단
새 ACL(액세스 제어 목록)을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단
임의의 ACL(액세스 제어 목록)을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단
새 퍼블릭 버킷 또는 액세스 지점 정책을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단
임의의 퍼블릭 버킷 또는 액세스 지점 정책을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단
고급 설정은 넘어가고 "버킷 만들기"를 클릭하여 버킷을 생성한다.
생성한 버킷에 "dev", "prod" 폴더를 만들어준다.
AWS CloudFront 콘솔로 이동한다.
"배포 생성" 버튼을 클릭한다.
CloudFront 생성 설정
CloudFront 콘솔의 사이드 메뉴에서 "배포"클릭 -> 내가 배포한 cloudfront 선택한 후 "동작 생성" 클릭
ClounFront 배포할 때와 거의 동일하다 단, 아래와 같이 작성하고 "동작 생성"한다.
( dev/ 에 대한 동작을 생성하고 나면, prod/*에 대한 동작도 생성해준다.)
AWS Lambda 콘솔에서 사이드바의 "함수"로 이동 -> "함수 생성" 클릭
아래와 같이 함수 이름은 "resize-image", 런타임 "Node.js 18x" 선택하고, "기본 실행 역할 변경"에서 "기존 역할"에서 이전에 생성한 역할 규칙 선택 사용
"함수 생성" 클릭해서 생성 완료하기
생성된 함수 상세 화면에서 "구성" 탭 -> "일반 구성" 편집으로 제한 시간은 30초로 수정.
(원하는 만큼 시간과 메모리를 수정하면 된다.)
AWS Cloud9 콘솔로 이동 -> "환경 생성" 클릭
이름을 설정해줌 (나머지는 모두 기본옵션)
cd resize-image
npm init -y
npm i sharp
'use strict';
import querystring from 'querystring'; // Don't install.
import AWS from 'aws-sdk'; // Don't install.
import Sharp from 'sharp';
const S3 = new AWS.S3({
region: 'ap-northeast-2'
});
const BUCKET = 'openmarkte-resize-image';
export const handler = async (event, context, callback) => {
const { request, response } = event.Records[0].cf;
// Parameters are w, h, f, q and indicate width, height, format and quality.
const params = querystring.parse(request.querystring);
// Required width or height value.
if (!params.w && !params.h) {
return callback(null, response);
}
// Extract name and format.
const { uri } = request;
const [, imageName, extension] = uri.match(/\/?(.*)\.(.*)/);
// Init variables
let width;
let height;
let format;
let quality; // Sharp는 이미지 포맷에 따라서 품질(quality)의 기본값이 다릅니다.
let s3Object;
let resizedImage;
// Init sizes.
width = parseInt(params.w, 10) ? parseInt(params.w, 10) : null;
height = parseInt(params.h, 10) ? parseInt(params.h, 10) : null;
// Init quality.
if (parseInt(params.q, 10)) {
quality = parseInt(params.q, 10);
}
// Init format.
format = params.f ? params.f : extension;
format = format === 'jpg' ? 'jpeg' : format;
// For AWS CloudWatch.
console.log(`parmas: ${JSON.stringify(params)}`); // Cannot convert object to primitive value.
console.log(`name: ${imageName}.${extension}`); // Favicon error, if name is `favicon.ico`.
try {
s3Object = await S3.getObject({
Bucket: BUCKET,
Key: decodeURI(imageName + '.' + extension)
}).promise();
} catch (error) {
console.log('S3.getObject: ', error);
return callback(error);
}
try {
resizedImage = await Sharp(s3Object.Body)
.resize(width, height)
.toFormat(format, {
quality
})
.toBuffer();
} catch (error) {
console.log('Sharp: ', error);
return callback(error);
}
const resizedImageByteLength = Buffer.byteLength(resizedImage, 'base64');
console.log('byteLength: ', resizedImageByteLength);
// `response.body`가 변경된 경우 1MB까지만 허용됩니다.
if (resizedImageByteLength >= 1 * 1024 * 1024) {
return callback(null, response);
}
response.status = 200;
response.body = resizedImage.toString('base64');
response.bodyEncoding = 'base64';
response.headers['content-type'] = [
{
key: 'Content-Type',
value: `image/${format}`
}
];
return callback(null, response);
};
8. Select Upload Type에서는 Directory를 선택.
9. Build directory에서는 "No"를 선택.
10. 그다음 "open" 클릭. 이렇게 하면 디렉토리가 있는 그대로 업로드 되고 배포 후 Lambda가 즉시 업데이트 된다.
Lambda 함수 작성 후 Upload Lambda를 하게 되면 알아서 최신 새 버전의 Lambda가 게시가 된다. (* Lambda 콘솔로 이동하여 "resize-image" 함수 상세 화면에 버전 탭을 통해서 최신 버전을 확인할 수 있음.)
위와 같은 resize-image 함수 상세 화면에서 "작업"버튼을 클릭해서 "Lambda@Edge배포"를 선택한다.
배포 설정을 해주는데, CloudFront event가 응답이기 때문에 Origin response로 설정을 해준다. (* Cache behavior는 입력하는 게 아닌 CloudFront에서 생성해준 동작을 선택하는 것이다 리스트가 안 뜬다면 CloudFront 동작 생성 부분이 이상한 것이다.)
dev/, prod/ 둘 다 배포를 해주고 나면 resize-image 버전에 들어가서 해당 버전의 상세 화면에 아래와 같이 CloudFront 트리거가 추가된 것들 확인할 수 있다.
이제 "https://[clounfrontId].cloudfront.net/[이미지파일이름]?파라미터" 이미지 리사이징 할 주소를 호출해서 제대로 리사이징 되고 있는지 확인해 보면 된다.
위의 과정대로 맞게 잘 설정을 했는데 503에러가 떠서 cloudwatch를 통해서 확인해 봤더니 index파일을 찾을 수 없다고 한다.
이유는 resize-image2 함수를 빌드하고 나서 함수가 작동하는 handler의 경로 설정이 안 되어 있기 때문이다. handler가 작성되어 있는 파일은 index.mjs이고 이 파일은 resize-image2폴더 안에 있기 때문에 런타임 설정에서 핸들러 부분에 경로설정을 resize-image2/index.handler로 수정해야 된다. 수정 후에는 함수가 정상적으로 실행된다.