AWS

AWS Lambda S3 트리거를 사용한 이미지 리사이징 + webp 변환을 통한 이미지 최적화

jae1004 2024. 3. 25. 02:27

이미지 리사이징

말 그대로 이미지의 크기를 조정하는 과정을 말합니다.

출처 : https://developer.chrome.com/docs/lighthouse/performance/uses-responsive-images#stack-specific_guidance

위의 사진은 Lighthouse에서 이미지의 크기가 적절하지 않다고 알려주는 화면입니다. 다시 말해, 렌더링 된 이미지의 사이즈와 실제 이미지의 사이즈가 많이 차이가 나는 경우로 이미지의 크기가 적절하지 않다는 것을 의미합니다. 이와 같이 불필요하게 큰 이미지를 사용하게 되면 리소스를 많이 사용하게 되어 바이트 낭비페이지 로드 시간이 느려져, 웹 성능 저하를 유발할 수 있습니다.

이미지 리사이징의 중요성

이미지 리사이징을 통해 파일 크기를 줄여, 웹 사이트의 총 리소스를 크게 줄일 수 있습니다. 이로 인해 웹 페이지 로딩 속도 향상사용자 경험을 개선할 수 있습니다. 그리고 TTI(Time to interaction) 지표를 개선할 수 있습니다.

 

쾌적한 웹 환경 구축에 가장 효율적 엔지니어링, ‘이미지 리사이징’
 

이미지 리사이징을 통해 웹 성능 개선하기

By 김요한

medium.com

이 블로그에서 이미지 리사이징에 대해 인상 깊었던 문구를 가져와봤습니다. 

구현 설명 및 코드

이미지 리사이징을 하는 다양한 방법이 존재합니다. 저는 그중 AWS Lambda와 S3 트리거를 사용하였습니다. 

 

why?

이미지 리사이징 작업은 CPU와 메모리를 많이 사용하기 때문에, 서버 부하가 발생할 수 있습니다. 이는 웹 성능에 부정적인 영향을 줄 수 있기 때문에 AWS Lambda를 통해 이미지 리사이징 작업을 진행했습니다.

 

AWS Lambda

AWS Lambda는 서버를 프로비저닝하거나 관리하지 않고도 코드를 실행할 수 있는 서버리스 컴퓨팅 서비스입니다.

  • 프로비저닝(provisioning) : IT 인프라를 생성하고 설정하는 프로세스

AWS Lambda가 프로비저닝 과정을 신경 쓰지 않아도 코드를 실행할 수 있기 때문에 개발자는 서비스 개발에 더 집중할 수 있고, 인프라 관리에 드는 시간과 비용을 절약할 수 있습니다. 

추가로 비용은 on-demand 형식으로 사용한 만큼만 요금을 지불합니다.

 

실행 순서 요약

  1. 사용자가 웹 페이지에서 이미지 업로드
  2. 서버에서 이미지 요청을 받아 S3 bucket에 업로드
  3. S3에 Event 발생
  4. S3 trigger가 발생되어 Lambda 함수 실행
  5. 함수에서는 이미지를 가져온 후, 이미지 리사이징 작업 후, 지정된 S3 bucket에 리사이징 이미지 업로드

구현

 

AWS Lambda Image Resize 도입기 | 올리브영 테크블로그

신규 상품 프로젝트에서 AWS Lambda 이미지 리사이징 적용하기

oliveyoung.tech

S3 관련 설정 및 Lambda 함수 생성은 이 블로그를 참고했습니다!

 

1. AWS S3 버킷 생성 및 버킷 정책 설정

2. AWS Lambda 함수 생성

3. 생성한 함수 기본 설정 편집

4. 트리거 생성

트리거 생성 후 모습

5. 함수 코드 작성

 index.js
const { S3Client, GetObjectCommand, PutObjectCommand } = require("@aws-sdk/client-s3");
const { Readable } = require("stream");
const sharp = require("sharp");

const s3 = new S3Client({ region: "ap-northeast-2" }); // 리전은 환경에 맞게 변경

exports.handler = async (event) => {
  const bucket = event.Records[0].s3.bucket.name;
  // decodeURIComponent를 사용해 한글 깨짐 방지
  const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " "));

  // 리사이즈된 이미지나 특정 경로의 이미지는 처리하지 않음 (무한업로드 방지)
  if (key.startsWith("resized-image/")) {
    return;
  }

  const fileName = key.split("/").pop();
  const baseFileName = fileName.split(".").slice(0, -1).join("."); // 확장자를 제외한 파일명만 추출
  // resized-image 폴더에 리사이징 완료한 이미지(resized-{파일명}.webp) 형식으로 저장
  const dstKey = `resized-image/resized-${baseFileName}.webp`; 

  try {
    const response = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
    var stream = response.Body;

    if (!stream instanceof Readable) {
      console.log("Unknown object stream type");
      return;
    }

    const content_buffer = Buffer.concat(await stream.toArray());

    const width = 90; // 리사이징 후 너비
    const height = 90; // 리사이징 후 높이

    const output = await sharp(content_buffer)
      .resize(width, height, { fit: "inside" }) // 비율을 유지하면서 꽉 차게 리사이즈
      .webp({ lossless: true }) // 무손실 webp로 변환
      .toBuffer();

    await s3.send(
      new PutObjectCommand({
        Bucket: bucket,
        Key: dstKey,
        Body: output,
        ContentType: "image/webp",
      })
    );

    console.log("Successfully resized and uploaded");
  } catch (error) {
    console.error("Error processing file", error);
  }
};

 

 

GitHub - dmswn1004/AWS-Lambda-S3-Image-Resizing: AWS Lambda 트리거 + S3 이미지 리사이징 함수

AWS Lambda 트리거 + S3 이미지 리사이징 함수. Contribute to dmswn1004/AWS-Lambda-S3-Image-Resizing development by creating an account on GitHub.

github.com

폴더 전체가 궁금하시면 만들어둔 레포에서 확인해 보시면 될 것 같습니다😀

폴더 구조
📦lambda-S3
 ┣ 📂node_modules
 ┣ 📜index.js
 ┣ 📜package-lock.json
 ┗ 📜package.json

 

6. 코드 업로드

코드 업로드 시, 파일을 압축하여 업로드해야 합니다.

mac에서는 폴더 압축 시 파일 누락 방지를 위해 다음과 같은 명령어를 사용합니다.

zip -r lambda.zip .

 

이제 구현을 모두 마쳤으니, S3에 이미지를 업로드하여 결과를 확인하겠습니다.

리사이징 전 S3에 저장된 사진 용량

리사이징 + webp 변환 후 S3에 저장된 사진 용량

 

이미지의 사이즈가 약 80% 이상 줄어들었습니다!!⁉

트러블슈팅

sharp 모듈 설치

sharp 모듈 설치 시, 이 명령어로 설치했을 때 성공했습니다.(다른 명령어 실행했을 때는 계속 오류가 발생했어요🥲)

npm install --platform=linux --arch=x64 sharp@0.32.6

Lambda는 Amazon Linux 기반으로 돌아가기 때문에… 설치 시 꼭 리눅스 버전으로 설치해야 함수 실행이 가능합니다!

 

자습서: Amazon S3 트리거를 사용하여 썸네일 이미지 생성 - AWS Lambda

Amazon S3 버킷 이름은 전역적으로 고유하지만 리소스 기반 정책을 사용할 때는 버킷이 반드시 계정에 속하도록 지정하는 것이 가장 좋습니다. 버킷을 삭제하면 다른 AWS 계정가 동일한 Amazon 리소

docs.aws.amazon.com

추가로, 위의 AWS 공식 문서에서 최신 버전의 sharp(0.33)는 Lambda와 호환되지 않는다고 해서 0.32.6 버전을 다운로드했습니다.

무한업로드

이미지가 계속해서 업로드되는 무한 업로드가 발생하여, 리사이즈된 이미지나 특정 경로의 이미지는 처리하지 처리해 줬습니다. (코드 참고)

 

lambda s3 업로드 실패

asdadadadda

medium.com

이 블로그에 설명이 잘 되어있어서 참고해서 수정하셔도 될 것 같습니다.

테스트

테스트에 사용한 이벤트 JSON (awsRegion, arn, key 수정해서 사용)
{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "ap-northeast-2",
      "eventTime": "2024-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": bucket 이름 넣기,
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": S3 arn 넣기
        },
        "object": {
          "key": 사진 파일 이름 넣기,
          "size": 128,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}

테스트 성공 화면

추가적으로 권한 설정 시 CloudWatch 권한을 허용하면, 밑의 사진과 같이 CloudWatch metriec 및 Logs를 볼 수 있습니다.

728x90