일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- buildx
- 비동기
- spring actuator
- Lock
- health check
- 결제누락
- 포트원
- url 단축기
- 레디스
- redis
- varchar
- JMeter
- cache miss
- ALB
- 페이징
- mysql
- 이메일 비동기
- AWS Lambda
- 빅맥
- 선착순
- 동시성 문제
- 실행계획
- Spring
- M오더
- Docker
- SMTP
- ECR
- scheduling
- explain
- 성능개선
- Today
- Total
jjunhub
게시글 목록 미리보기 성능 개선하기 - 2편 본문
부제 : AWS Lambda로 썸네일 이미지 생성하고 사용하기
목표
현재는 사용자가 이미지 원본을 S3로 presigned URL로 전송하고, 이 이미지를 그대로 저장하여 보여주는 구조이다. 이 경우에, 여러 이미지를 미리보기 형식으로 볼 경우에도 이미지 원본을 단지 비율에 맞게 축소해서 보여주기 때문에 비효율적인 네트워크 트래픽이 발생한다.
따라서 미리보기 형식으로 보여줄 이미지들에 대해서, 썸네일 방식으로 다시 저장하여 해당 이미지들을 필요할 때에 제공하는 방식으로 네트워크 트래픽을 줄여보고자 한다.
고려해야할 점
기존에는 10개의 이미지를 보여주기 위해 10번의 이미지 파일 요청이면 됐다. 하지만 변경할 구조에서는 10개의 이미지를 보여주기 위해서 최소 10번, 최대 20번까지 요청이 진행될 수 있다. 즉, 이미지 요청 횟수와 이미지 로딩 시간에 대한 트레이드오프를 고려해보고, 측정을 통해 무엇이 더 좋을지 판단해야한다.
방법
클라이언트 → 서버 ( 이미지 N개 업로드 요청 )
클라이언트 ← 서버 ( N개의 Presigned URL )
클라이언트 → S3 ( N개의 이미지 PUT 요청으로 업로드 )
S3 → S3 ( N개의 이미지를 lambda 식을 통해서 썸네일 방식으로 리사이징하여 thumbnail/… 로 전달 )
클라이언트 → 서버 ( 미리보기 요청 시, thumbnail/… 쪽을 가르키는 링크를 클라이언트에게 제공 )
클라이언트 → S3 ( 썸네일 이미지 요청 )
클라이언트 ← S3 ( 썸네일 이미지 제공 )
클라이언트 → S3 ( 썸네일 이미지 요청이 실패하면, 원본 이미지 요청 )
구현
- AWS S3 생성
- images 폴더 생성
- thumbnail 폴더 생성
- 로컬에서 AWS Lambda로 올릴 코드 작성
- 로컬에서 AWS Lambda로 올릴 코드 빌드
- 로컬에서 AWS Lambda로 올릴 코드 배포
- S3를 Lambda의 트리거로 등록
- 서버, 클라이언트 로직 수정
Lambda 함수 빌드에 필요한 것
lambda_function.py
import boto3
import os
import sys
import uuid
from urllib.parse import unquote_plus
from PIL import Image
import PIL.Image
s3_client = boto3.client('s3')
def resize_image(image_path, resized_path):
base_width = 300
with Image.open(image_path) as image:
w_percent = (base_width/float(image.size[0]))
h_size = int((float(image.size[1])*float(w_percent)))
image = image.resize((base_width,h_size), Image.ANTIALIAS)
image.save(resized_path)
def lambda_handler(event, context):
for record in event['Records']:
# Get the bucket name and key for the new file
bucket = record['s3']['bucket']['name']
# Get the object key
key = unquote_plus(record['s3']['object']['key'])
# Get the file name with split the key
file_name = key.split('/')[-1]
# Download the file from S3
download_path = '/tmp/{}{}'.format(uuid.uuid4(), file_name)
# Upload the resized file to S3
upload_path = '/tmp/resized-{}'.format(file_name)
# Download image from S3
s3_client.download_file(bucket, key, download_path)
# Resize the image
resize_image(download_path, upload_path)
# Upload the resized image to S3 ( upload_path mean the path of the resized image )
s3_client.upload_file(upload_path, '{}'.format(bucket), 'thumbnail/{}'.format(file_name))
requirements.txt
Pillow == 9.2.0
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
CreateThumbnail:
Type: AWS::Serverless::Function
Properties:
Handler: lambda_function.lambda_handler
Runtime: python3.9
Timeout: 10
Policies: AWSLambdaExecute
Events:
CreateThumbnailEvent:
Type: S3
Properties:
Bucket: !Ref SrcBucket
Events: s3:ObjectCreated:*
SrcBucket:
Type: AWS::S3::Bucket
참고)
- template.yaml에서 Resource Runtime의 파이썬 버전을 로컬과 동일하게 해야하는 것 같다.
- PIL 버전에 대해서 최신 버전이 호환이 안되는 것 같다. Python3.9와 Pillow 9.2.0의 조합은 통과한다.
원본 이미지 축소 결과
![]() |
![]() |
3.0MB -> 12.6KB로 감소하였다. 여기서 축소한 비율은 임의로 정한 것으로, 여러 비율에 대해서 테스트하여 사용자 경험을 해치지 않으면서 최대한으로 축소할 수 있는 비율을 찾아야한다.
프론트엔드 연동 결과
리액트로 이루어진 웹 환경에서, Profiler를 통해서 측정한 결과 2.4초에서 1.7초로 로딩 시간이 30프로나 빨라진 것을 파악할 수 있었다. 만약 사용자가 thumbnail이 생성되기 전에 요청한다면, 이에 따른 추가 요청을 반영한 결과는 아직 측정해보지 않았다. 자세하게 시나리오를 세워서 실제로 사용해도 괜찮을 지는 추가적으로 테스트해볼 예정이다.
결론
이미지 사이즈를 줄이니, 당연하게도 성능이 향상되었다. 하지만 아직도 고려해야할 점은 더 존재한다. 이를 모두 반영해서 정말 괜찮은 방법인지는 꾸준히 알아가봐야한다고 생각한다.
참고 자료
'Performance Improvement' 카테고리의 다른 글
게시글 목록 미리보기 성능 개선하기 - 3편 (0) | 2025.02.05 |
---|---|
게시글 목록 미리보기 성능 개선하기 - 1편 (0) | 2025.01.10 |
SMTP 환경에서 다수의 이메일 동시 전송하기 (0) | 2025.01.09 |