
대용량 이미지 처리 파이프라인을 전통적인 EC2 + Cron + 파일 서버 방식으로 운영하다 보면
과 같은 문제를 겪게 됩니다.
AWS Lambda 와 S3 를 결합한 서버리스(Serverless) 접근은 이런 문제를 근본적으로 해소합니다.
| 선택 | 이유 |
|---|---|
| Lambda | 이벤트 기반 실행과 초 단위 과금 ► 비용 최적화 / 인프라 관리 부담 최소화 |
| S3 | 내구성 11 9(99.999999999%)·무제한 확장 / 정적 URL 배포(CloudFront) 연계 용이 |
본 글에서는 Spring Boot API 서버 ↔ Lambda(Image Processor) ↔ S3 로 이루어진 파이프라인을 예제로,
를 단계별로 정리합니다.
| 컴포넌트 | 역할 |
|---|---|
| API 서버 (Spring Boot + ECS Fargate) | 이미지 URL 스크랩핑 → RDB 저장 → Lambda Invoke |
| Image Processor Lambda | 중복 검사 → 이미지 다운로드 → S3 업로드 → 콜백 |
| S3 버킷 | 최종 이미지 저장 / CloudFront Origin |
| 캐시 DB | url ⇆ s3Uri 매핑 유지(30 일) |
| RDB | 비즈니스 메타데이터 보관 |
[API 서버]
│
│ Invoke (async)
▼
[Lambda]
├───▶ HEAD / Cache ───▶ [MongoDB·Redis]
├───▶ Download ───────▶ [원본 URL]
├───▶ PutObject ──────▶ [S3]
└───▶ Callback ───────▶ [API 서버]
호환성 Tip: 원본이 이미 S3 안에 있다면 CopyObject 로 대체해 네트워크 I/O 비용을 줄일 수 있습니다.
serverless.yml 요약service: image-processor
frameworkVersion: '3'
useDotenv: true # .env 로컬 변수 자동 로드
provider:
name: aws
region: ap-northeast-2
runtime: nodejs18.x
timeout: 900 # 15 분 – 대용량 이미지 처리 대비
iamRoleStatements: # 최소 권한만 부여
# S3 업로드
- Effect: Allow
Action: [ s3:PutObject, s3:HeadObject ]
Resource: arn:aws:s3:::prod-bucket/*
# (선택) SQS 전송
- Effect: Allow
Action: sqs:SendMessage
Resource: arn:aws:sqs:ap-northeast-2:123456789012:fc-review-*
functions:
processImage:
handler: dist/handler.processImage
memorySize: 512
plugins:
- serverless-dotenv-plugin # 환경변수
- serverless-esbuild # 고속 번들/최적화
custom:
esbuild:
bundle: true
minify: true
external: [] # 모든 의존성을 번들링해 콜드스타트 최소화
target: node18
콜드 스타트 최적화
esbuild + minify + external [] + 512 MB 메모리 조합으로 초기 로딩 시간을 ~200 ms 이하로 단축할 수 있습니다.
Lambda를 VPC Subnet에 넣으면 ENI 할당 → 초기 콜드 스타트가 길어짐
Solutions
| API Gateway | InvokeFunction (SDK) |
|---|---|
| REST 엔드포인트 공개 → 추가 과금($3.5/mil) | 무료 (Lambda 호출 비용만) |
| 멀티 파이프라인일 때 라우팅 복잡 | 내부 서비스 간 간단 호출 |
| 인증·Throttle 설정 필요 | IAM Role 정책만 있으면 끝 |
내부 BFF(Batch) 작업이나 비공개 서버↔Lambda 시나리오는 Invoke가 비용·보안 측면에서 유리합니다.
@Service
class ImageLambdaInvoker(
private val lambda: LambdaClient,
@Value("\${app.lambda.functionName}")
private val functionName: String
) {
fun invoke(recordId: Long, originalUrl: String, callbackUrl: String) {
val payload = mapOf(
"recordId" to recordId,
"originalUrl" to originalUrl,
"callbackUrl" to callbackUrl
)
lambda.invoke {
functionName(functionName)
invocationType(InvocationType.EVENT) // 비동기 호출
payload(SdkBytes.fromUtf8String(ObjectMapper().writeValueAsString(payload)))
}
}
}
SendMessage → API 서버가 큐를 폴링 또는 SQS Listener(@SqsListener)POST /images/{id}/callback@PostMapping 으로 수신export const handler = async (event) => {
const { recordId, originalUrl, callbackUrl } = JSON.parse(event.body!);
// 1) 캐시 조회 (Mongo/Redis/S3)
const cached = await getCachedS3Uri(originalUrl);
let s3Uri = cached;
if (!cached) {
// 2) 이미지 다운로드
const { data, contentType } = await fetchImage(originalUrl);
// 3) S3 업로드 (날짜별 경로)
const key = `${new Date().toISOString().slice(0,10)}/${Date.now()}.jpg`;
s3Uri = await uploadToS3(data, contentType, key, process.env.S3_BUCKET!);
// 4) 캐시 저장
await setCache(originalUrl, s3Uri);
}
// 5) 콜백 (Webhook or SQS)
await postCallback(recordId, callbackUrl, s3Uri);
};
| 방법 | TTL 적용 | 장점 | 단점 |
|---|---|---|---|
| MongoDB TTL 인덱스 | expireAfterSeconds | 문서형 메타데이터, upsert 편리 | 연결 수·콜드스타트 비용 |
Redis SETEX | Key 자체 만료 | 초고속 읽기/쓰기 | 메모리 비용 |
S3 객체 패턴 HeadObject | 없음 | 외부 DB 불필요 | HeadObject 비용·레이턴시 |
소규모 트래픽 → MongoDB 무료 티어,
초고속 응답 요구 → Redis,
DB 운영 최소화 → S3 객체 패턴이 추천됩니다.
GET 만 허용, 액세스 로그 활성화지금까지 Lambda Invoke + S3 조합으로 이미지 파이프라인을 서버리스 아키텍처로 옮기는 과정을 살펴봤습니다. 전통적인 EC2 배치 작업에 비해
이라는 이점을 가져갈 수 있었습니다.
다만 서버리스는 만능 열쇠가 아니며, 콜드 스타트·ENI 할당·퍼블릭 액세스 설정 등 새로운 운영 이슈가 생깁니다. 이 글에서 정리한
같은 베스트 프랙티스를 적용한다면 대부분의 문제를 예방할 수 있습니다.
| 주제 | 링크 |
|---|---|
| AWS Lambda Best Practices | https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html |
| AWS S3 Public Access Block | https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html |
| Serverless Framework Docs | https://www.serverless.com/framework/docs/ |
| Lambda Provisioned Concurrency | https://docs.aws.amazon.com/lambda/latest/dg/provisioned-concurrency.html |