개발환경
- typescript
- aws lambda
- sharp
- awa lambda layer
- aws sdk
일반적으로 aws S3를 활용하여 이미지를 리사이징할 때 aws-sdk를 사용합니다. 그런데 해당 패키지는 용량이 꽤나 커서, 일반적인 람다 함수의 패키지 크기는 약 1MB인 반면, aws-sdk가 포함된 람다 함수의 패키지 크기는 약 5MB를 보임으로써, 경험적으로 대략 4MB의 용량을 차지하고 있었습니다. 그리고 람다 함수는 런타임에서 aws-sdk 패키지를 제공하기 때문에 @aws-sdk/** 패키지만 사용하여 용량을 최소화할 것입니다.
또한, 일반적으로 이미지 리사이징 함수를 여러 도메인에서 사용하는 경우, axios를 사용하여 해당 api를 호출하지만, 람다를 사용한다면 api 호출 없이 람다함수의 논리적 이름만으로 사용이 가능합니다. 그래서 @aws-sdk/client-lambda 패키지를 사용하여 리사이징 람다 함수를 빠르게 호출해볼 것입니다.
진행에 앞서, 전제조건은 sharp 모듈이 설치된 lambda layer를 계정에 미리 만들어두었다고 가정하겠습니다. (혹은 다른 계정에 만들어 둔 sharp layer 에 대해 permission을 설정하고 사용하는 방법도 무관합니다.) 아래 첨부된 레이어 관련 글을 참고해주세요.
👉 AWS 람다 레이어 활용하기 (with esbuild)
이미지 리사이징 람다 함수 작성하기
DTO
Dto는 다음과 같이 구성했습니다. 다양한 도메인에서 사용하는 경우를 가정해서, 저장될 버킷 위치와 리사이징 크기를 입력할 수 있도록 했으며, originUri는 https로 시작하는 s3이미지 uri입니다.
export class ImageConvertDto {
originUri: string;
headingBucket: string;
width?: number;
height?: number;
}
Module
import를 해야 하는 모듈은 다음과 같습니다. sharp 는 앞서 언급한 것처럼 aws lambda layer로 만들어서 sam template에서 이미지 리사이징 람다 함수에 Layer로 설정했고, s3에 업로드하기 위한 패키지와 스트림을 업로드 하기 위한 패키지를 설치했습니다.
import sharp from "sharp";
import { ImageConvertDto } from "./converter.dto";
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
import { Readable } from "stream";
S3Client
호출 되었을 때 s3Client 인스턴스가 반복되지 않고 한 번만 생성될 수 있도록 constructor에 리전과 함께 설정했습니다.
export class ConverterApplicationService {
private s3Client;
constructor() {
this.s3Client = new S3Client({ region: "aws_region" });
}
...
}
https 로 시작하는 s3 uri는 규칙성을 갖고 버킷명, 리전, 주소, key 등으로 구성되기 때문에 uri 에서 해당 규칙에 맞게 데이터를 파싱했습니다.
async getConvertedImageUri(imageConvertData: ImageConvertDto): Promise<string> {
const splitedUriArray = imageConvertData.originUri.split("/");
const objectKey = splitedUriArray.slice(3).join("/");
const filename = splitedUriArray.slice(3).pop();
const objectBucket = decodeURIComponent(splitedUriArray.slice(2, 3)[0].split(".")[0]);
...
}
버킷명과 키값으로 object를 가져오는 커맨드를 통해 s3 object 정보를 가져왔습니다.
const originImageInfo = {
Bucket: objectBucket,
Key: objectKey,
};
const originImageObject = await this.s3Client.send(new GetObjectCommand(originImageInfo));
Sharp
앞서 설정한 DTO를 보면 width, height는 optional 이기 때문에 number | undefined 타입을 가지며, 한 가지 값만 입력해주면 auto-scaling이 되어 기존 사진의 비율을 유지해줍니다. 이때, withMetadata()를 사용해 주어야 세로 사진이 가로로 출력되는 불상사를 방지할 수 있습니다.
const resizedImage = sharp()
.resize(imageConvertData.width, imageConvertData.height)
.withMetadata();
스트림을 업로드 하기 위해 nodejs의 pipe 메서드 사용하여, Readable 타입으로 선언된 값을 Writable 타입인 Sharp와 연결했습니다.
const imageBuffer = (originImageObject.Body as Readable).pipe(resizedImage);
이것을 upload해주고, 출력될 값을 입력하여 리턴값으로 설정했습니다.
const uploadResizedImageToS3 = async (): Promise<string> => {
const params = {
Bucket: bucket,
Key: `${key}/${filename}-resize`,
Body: imageBuffer,
ContentType: "image",
};
await new Upload({ client: this.s3Client, params: params }).done();
return `https://${bucket}.${aws_region}.amazonaws.com/${key}/${filename}-resize`;
};
return await uploadResizedImageToS3();
클라이언트 람다로 이미지 리사이징 람다 함수 사용하기
client 뭐시기를 사용하는 방법은 대개 동일합니다. client-s3와 같이 constructor에 인스턴스를 설정해주고, 설정했던 DTO에 맞게 매개변수들을 넣어주고, 필요하다면 삼항연산자를 활용하여 default 값을 설정해주고, 사용하고 싶은 람다 함수의 이름과 body 값을 보내주면 api 호출없이 앞서 설정했던 이미지 리사이징 함수를 사용할 수 있습니다!
import { InvokeCommand, LambdaClient } from "@aws-sdk/client-lambda";
export class ConvertService {
private lambda: LambdaClient;
constructor() {
this.lambda = new LambdaClient({ region: "aws_region" })
}
async returnConvertedImageUri(
originUri: string,
headingBucket: string,
width?: number,
height?: number
): Promise<string> {
const widthInput = width ? width : 400;
const imageConvertCondition: ImageConvertDto = {
originUri: originUri,
headingBucket: headingBucket,
width: widthInput,
height: height,
};
const result = await this.lambda.send(
new InvokeCommand({"convert-image", {
body: JSON.stringify(imageConvertCondition)
}});
const convertedImageUri = JSON.parse(result.body);
return convertedImageUri;
}
}
참고자료
'Dev > AWS' 카테고리의 다른 글
람다레이어로 sharp 패키지 활용하여 이미지 리사이징 하기 (0) | 2024.04.13 |
---|---|
AWS Lambda 사용 시 Cognito 를 활용한 인증 인가 구현 (0) | 2023.06.03 |
AWS 람다 레이어 활용하기 (with esbuild) (0) | 2023.02.18 |
AWS CLI (Command Line Interface) (0) | 2023.01.29 |
[AWS] 클라우드 서비스의 이해 AWS (0) | 2022.09.07 |