AWS Lambda 완전 가이드
AWS Lambda는 서버 프로비저닝 없이 코드를 실행할 수 있는 서버리스 컴퓨팅 서비스다. 이벤트 기반으로 동작하며, 실행한 만큼만 비용을 지불한다.
Lambda란?
서버리스 컴퓨팅
전통적 방식:
EC2 인스턴스 → 24시간 실행 → 사용하지 않아도 비용 발생
Lambda 방식:
이벤트 발생 → Lambda 실행 → 실행 시간만큼 비용 → 종료
Lambda의 핵심 특징
| 특징 | 설명 |
|---|---|
| 서버리스 | 인프라 관리 불필요 |
| 이벤트 기반 | 다양한 AWS 서비스와 연동 |
| 자동 스케일링 | 요청량에 따라 자동 확장 |
| 초 단위 과금 | 1ms 단위로 실행 시간 계산 |
| 고가용성 | 멀티 AZ 자동 배포 |
Lambda 작동 방식
실행 흐름
1. 이벤트 소스가 Lambda 호출
2. Lambda 서비스가 실행 환경 준비 (Cold Start)
3. 핸들러 함수 실행
4. 응답 반환
5. 일정 시간 후 실행 환경 제거 또는 재사용 (Warm)
호출 유형
동기 호출 (Synchronous)
├── API Gateway → Lambda
├── CLI/SDK 직접 호출
└── 호출자가 응답 대기
비동기 호출 (Asynchronous)
├── S3 이벤트
├── SNS
├── EventBridge
└── 즉시 반환, 백그라운드 실행
폴링 기반 호출 (Poll-based)
├── SQS
├── Kinesis
├── DynamoDB Streams
└── Lambda가 소스를 폴링
지원 런타임
AWS 관리형 런타임
| 런타임 | 버전 | 식별자 |
|---|---|---|
| Node.js | 20.x, 18.x | nodejs20.x, nodejs18.x |
| Python | 3.12, 3.11, 3.10, 3.9 | python3.12, python3.11 |
| Java | 21, 17, 11, 8 | java21, java17, java11 |
| .NET | 8, 6 | dotnet8, dotnet6 |
| Ruby | 3.3, 3.2 | ruby3.3, ruby3.2 |
| Go | 1.x (AL2) | provided.al2 |
| Rust | Custom Runtime | provided.al2023 |
커스텀 런타임
# provided.al2023 사용
# bootstrap 파일 필요
#!/bin/sh
set -euo pipefail
while true
do
# 이벤트 가져오기
HEADERS="$(mktemp)"
EVENT_DATA=$(curl -sS -LD "$HEADERS" \
"http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next")
REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2)
# 함수 실행
RESPONSE=$(./handler "$EVENT_DATA")
# 응답 전송
curl -sS -X POST \
"http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" \
-d "$RESPONSE"
done
핸들러 함수 작성
Node.js
// index.mjs (ES Module)
export const handler = async (event, context) => {
console.log('Event:', JSON.stringify(event, null, 2));
const response = {
statusCode: 200,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: 'Hello from Lambda!',
requestId: context.awsRequestId
})
};
return response;
};
// index.js (CommonJS)
exports.handler = async (event, context) => {
// 남은 실행 시간 확인
const remainingTime = context.getRemainingTimeInMillis();
return {
statusCode: 200,
body: JSON.stringify({ remainingTime })
};
};
Python
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
logger.info(f"Event: {json.dumps(event)}")
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json'
},
'body': json.dumps({
'message': 'Hello from Lambda!',
'requestId': context.aws_request_id
})
}
Java
package com.example;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
public class Handler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
context.getLogger().log("Request ID: " + context.getAwsRequestId());
APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent();
response.setStatusCode(200);
response.setBody("{\"message\": \"Hello from Lambda!\"}");
return response;
}
}
// POJO 방식
public class Handler implements RequestHandler<Map<String, Object>, Map<String, Object>> {
@Override
public Map<String, Object> handleRequest(Map<String, Object> event, Context context) {
Map<String, Object> response = new HashMap<>();
response.put("statusCode", 200);
response.put("body", "Hello!");
return response;
}
}
Go
package main
import (
"context"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
func handler(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return events.APIGatewayProxyResponse{
StatusCode: 200,
Body: `{"message": "Hello from Lambda!"}`,
Headers: map[string]string{
"Content-Type": "application/json",
},
}, nil
}
func main() {
lambda.Start(handler)
}
Context 객체
주요 속성
| 속성 | 설명 |
|---|---|
awsRequestId |
요청 고유 ID |
functionName |
Lambda 함수 이름 |
functionVersion |
함수 버전 |
memoryLimitInMB |
할당된 메모리 |
logGroupName |
CloudWatch 로그 그룹 |
logStreamName |
CloudWatch 로그 스트림 |
identity |
Cognito 자격 증명 (있는 경우) |
clientContext |
클라이언트 컨텍스트 |
유용한 메서드
// Node.js
context.getRemainingTimeInMillis() // 남은 시간 (ms)
// Python
context.get_remaining_time_in_millis()
// Java
context.getRemainingTimeInMillis()
context.getLogger().log("message")
이벤트 소스와 트리거
주요 이벤트 소스
API 연동
├── API Gateway (REST, HTTP, WebSocket)
├── Application Load Balancer
└── Lambda Function URL
스토리지
├── S3 (객체 생성/삭제)
├── DynamoDB Streams
└── EFS
메시징
├── SQS
├── SNS
├── EventBridge
├── Kinesis
└── MSK (Kafka)
개발자 도구
├── CodeCommit
├── CodePipeline
└── CloudFormation
기타
├── CloudWatch Events/Logs
├── Cognito
├── Alexa
├── IoT
└── Step Functions
S3 트리거 예시
// S3 이벤트 처리
export const handler = async (event) => {
for (const record of event.Records) {
const bucket = record.s3.bucket.name;
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
const eventName = record.eventName;
console.log(`Event: ${eventName}, Bucket: ${bucket}, Key: ${key}`);
// S3 객체 처리 로직
}
};
SQS 트리거 예시
def lambda_handler(event, context):
batch_item_failures = []
for record in event['Records']:
try:
message_body = record['body']
# 메시지 처리
process_message(message_body)
except Exception as e:
batch_item_failures.append({
'itemIdentifier': record['messageId']
})
return {'batchItemFailures': batch_item_failures}
DynamoDB Streams 예시
export const handler = async (event) => {
for (const record of event.Records) {
const eventName = record.eventName; // INSERT, MODIFY, REMOVE
if (eventName === 'INSERT') {
const newImage = record.dynamodb.NewImage;
console.log('New item:', JSON.stringify(newImage));
} else if (eventName === 'MODIFY') {
const oldImage = record.dynamodb.OldImage;
const newImage = record.dynamodb.NewImage;
console.log('Updated:', JSON.stringify({ oldImage, newImage }));
} else if (eventName === 'REMOVE') {
const oldImage = record.dynamodb.OldImage;
console.log('Deleted:', JSON.stringify(oldImage));
}
}
};
Cold Start와 Warm Start
Cold Start란?
Cold Start (최초 호출 또는 스케일 아웃 시)
1. 실행 환경 생성
2. 런타임 초기화
3. 의존성 로드
4. 핸들러 외부 코드 실행
5. 핸들러 함수 실행
→ 수백 ms ~ 수 초 지연
Warm Start (재사용)
1. 핸들러 함수 실행
→ ms 단위 응답
Cold Start 영향 요소
| 요소 | Cold Start 영향 |
|---|---|
| 런타임 | Java, .NET > Node.js, Python |
| 메모리 크기 | 적을수록 느림 |
| 패키지 크기 | 클수록 느림 |
| VPC 연결 | 추가 지연 (개선됨) |
| Provisioned Concurrency | Cold Start 제거 |
Cold Start 최적화
// 핸들러 외부에서 초기화 (재사용됨)
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({}); // Cold Start 시 1회만 실행
export const handler = async (event) => {
// 여기서는 client 재사용
// ...
};
import boto3
# 핸들러 외부 - 실행 환경 재사용 시 유지
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('MyTable')
def lambda_handler(event, context):
# table 객체 재사용
response = table.get_item(Key={'id': event['id']})
return response
Provisioned Concurrency
# Provisioned Concurrency 설정
aws lambda put-provisioned-concurrency-config \
--function-name my-function \
--qualifier prod \
--provisioned-concurrent-executions 10
Lambda 설정
기본 설정
| 설정 | 범위 | 기본값 |
|---|---|---|
| 메모리 | 128MB ~ 10,240MB | 128MB |
| 타임아웃 | 1초 ~ 900초 (15분) | 3초 |
| 임시 스토리지 | 512MB ~ 10,240MB | 512MB |
메모리와 CPU
메모리 ↑ = CPU ↑ = 비용 ↑
128MB → 최소 CPU
1,769MB → 1 vCPU
3,008MB → 2 vCPU
10,240MB → 6 vCPU
환경 변수
# AWS SAM template.yaml
Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Environment:
Variables:
TABLE_NAME: my-table
LOG_LEVEL: INFO
API_ENDPOINT: https://api.example.com
// Lambda에서 사용
const tableName = process.env.TABLE_NAME;
const logLevel = process.env.LOG_LEVEL;
암호화된 환경 변수
import { KMSClient, DecryptCommand } from '@aws-sdk/client-kms';
const kmsClient = new KMSClient({});
// 암호화된 환경 변수 복호화
const decryptedValue = await kmsClient.send(new DecryptCommand({
CiphertextBlob: Buffer.from(process.env.ENCRYPTED_SECRET, 'base64')
}));
const secret = Buffer.from(decryptedValue.Plaintext).toString('utf-8');
Lambda Layers
Layer란?
Layer: 라이브러리, 런타임, 설정 등을 패키징하여 공유
함수 코드 (10MB)
↓
Layer 1: 공통 라이브러리 (50MB)
Layer 2: 설정 파일 (1MB)
↓
총 배포 패키지: 61MB
Layer 생성
# 디렉토리 구조
layer/
└── nodejs/
└── node_modules/
├── axios/
└── lodash/
# 압축
cd layer
zip -r ../my-layer.zip .
# Layer 생성
aws lambda publish-layer-version \
--layer-name my-shared-libs \
--zip-file fileb://my-layer.zip \
--compatible-runtimes nodejs18.x nodejs20.x
Python Layer
# 디렉토리 구조
python/
└── lib/
└── python3.11/
└── site-packages/
└── requests/
# 또는 간단히
python/
└── requests/
Layer 사용
aws lambda update-function-configuration \
--function-name my-function \
--layers arn:aws:lambda:ap-northeast-2:123456789012:layer:my-shared-libs:1
제한사항
| 항목 | 제한 |
|---|---|
| 함수당 Layer 수 | 최대 5개 |
| 총 압축 해제 크기 | 250MB (코드 + 모든 Layer) |
| Layer 버전 수 | 무제한 |
VPC 연결
VPC Lambda 필요 케이스
VPC 내부 리소스 접근 시:
- RDS (Private Subnet)
- ElastiCache
- EC2 인스턴스
- 내부 API
VPC 설정
# SAM template
Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
VpcConfig:
SecurityGroupIds:
- sg-12345678
SubnetIds:
- subnet-11111111
- subnet-22222222
NAT Gateway 구성 (인터넷 접근 필요 시)
VPC 구성:
├── Public Subnet
│ └── NAT Gateway
├── Private Subnet 1
│ └── Lambda (az-a)
└── Private Subnet 2
└── Lambda (az-b)
Route Table (Private):
0.0.0.0/0 → NAT Gateway
VPC Endpoints (NAT 없이 AWS 서비스 접근)
VPC Endpoint 활용:
├── S3 Gateway Endpoint (무료)
├── DynamoDB Gateway Endpoint (무료)
├── SQS Interface Endpoint
├── SNS Interface Endpoint
├── Secrets Manager Interface Endpoint
└── Systems Manager Interface Endpoint
버전과 별칭
버전 (Version)
# 버전 발행
aws lambda publish-version \
--function-name my-function \
--description "Production release v1.0"
# 결과
# arn:aws:lambda:region:account:function:my-function:1
별칭 (Alias)
# 별칭 생성
aws lambda create-alias \
--function-name my-function \
--name prod \
--function-version 1
# 별칭 업데이트
aws lambda update-alias \
--function-name my-function \
--name prod \
--function-version 2
가중치 기반 라우팅 (Canary 배포)
# 90% → 버전 1, 10% → 버전 2
aws lambda update-alias \
--function-name my-function \
--name prod \
--function-version 1 \
--routing-config AdditionalVersionWeights={"2"=0.1}
동시성 관리
동시성 유형
Reserved Concurrency (예약 동시성)
→ 특정 함수에 동시성 예약
→ 다른 함수가 사용 불가
→ 설정 무료
Provisioned Concurrency (프로비저닝된 동시성)
→ 미리 실행 환경 준비
→ Cold Start 제거
→ 추가 비용 발생
동시성 설정
# Reserved Concurrency 설정
aws lambda put-function-concurrency \
--function-name my-function \
--reserved-concurrent-executions 100
# 동시성 확인
aws lambda get-function-concurrency \
--function-name my-function
계정 동시성 한도
기본 한도: 1,000 (리전별)
→ 증가 요청 가능
Burst 한도: 500 ~ 3,000 (리전별 상이)
스로틀링 처리
// 비동기 호출 시 자동 재시도 (2회)
// 동기 호출 시 429 오류 반환
// 클라이언트에서 처리
try {
await lambda.invoke({ ... });
} catch (error) {
if (error.name === 'TooManyRequestsException') {
// 재시도 로직
await delay(1000);
// retry...
}
}
오류 처리
동기 호출 오류
export const handler = async (event) => {
try {
const result = await processEvent(event);
return {
statusCode: 200,
body: JSON.stringify(result)
};
} catch (error) {
console.error('Error:', error);
// API Gateway 형식
return {
statusCode: error.statusCode || 500,
body: JSON.stringify({
message: error.message,
errorType: error.name
})
};
}
};
비동기 호출 오류
# 실패 시 DLQ로 전송
Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
DeadLetterQueue:
Type: SQS
TargetArn: !GetAtt DeadLetterQueue.Arn
목적지 (Destinations)
# 성공/실패 시 다른 서비스로 라우팅
EventInvokeConfig:
DestinationConfig:
OnSuccess:
Type: SQS
Destination: !GetAtt SuccessQueue.Arn
OnFailure:
Type: SNS
Destination: !Ref FailureTopic
Lambda 함수 URL
Function URL이란?
API Gateway 없이 HTTPS 엔드포인트 제공
https://<url-id>.lambda-url.<region>.on.aws/
설정
# Function URL 생성
aws lambda create-function-url-config \
--function-name my-function \
--auth-type NONE # 또는 AWS_IAM
# 리소스 정책 추가 (공개 접근)
aws lambda add-permission \
--function-name my-function \
--action lambda:InvokeFunctionUrl \
--principal "*" \
--function-url-auth-type NONE \
--statement-id public-access
CORS 설정
aws lambda create-function-url-config \
--function-name my-function \
--auth-type NONE \
--cors '{
"AllowOrigins": ["https://example.com"],
"AllowMethods": ["GET", "POST"],
"AllowHeaders": ["content-type"],
"MaxAge": 86400
}'
모니터링과 로깅
CloudWatch Logs
// 자동으로 CloudWatch Logs에 기록
console.log('Info message');
console.error('Error message');
console.warn('Warning message');
// 구조화된 로깅
console.log(JSON.stringify({
level: 'INFO',
message: 'Processing request',
requestId: context.awsRequestId,
data: { userId: '123' }
}));
CloudWatch Metrics
| 메트릭 | 설명 |
|---|---|
| Invocations | 호출 횟수 |
| Duration | 실행 시간 |
| Errors | 오류 횟수 |
| Throttles | 스로틀 횟수 |
| ConcurrentExecutions | 동시 실행 수 |
| IteratorAge | 스트림 처리 지연 |
X-Ray 추적
import AWSXRay from 'aws-xray-sdk-core';
import AWS from 'aws-sdk';
// AWS SDK 래핑
const TracedAWS = AWSXRay.captureAWS(AWS);
const dynamodb = new TracedAWS.DynamoDB.DocumentClient();
export const handler = async (event) => {
// 서브세그먼트 생성
const segment = AWSXRay.getSegment();
const subsegment = segment.addNewSubsegment('process-data');
try {
// 비즈니스 로직
const result = await processData(event);
subsegment.close();
return result;
} catch (error) {
subsegment.addError(error);
subsegment.close();
throw error;
}
};
Lambda Insights
# 향상된 모니터링
Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Layers:
- arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:21
Policies:
- CloudWatchLambdaInsightsExecutionRolePolicy
제한 및 할당량
함수 제한
| 항목 | 제한 |
|---|---|
| 메모리 | 128MB ~ 10,240MB |
| 타임아웃 | 최대 900초 (15분) |
| 환경 변수 크기 | 4KB |
| 임시 스토리지 (/tmp) | 512MB ~ 10,240MB |
| 함수 레이어 수 | 5개 |
배포 제한
| 항목 | 제한 |
|---|---|
| 압축 패키지 크기 (.zip) | 50MB (직접 업로드) |
| 압축 해제 크기 | 250MB |
| 컨테이너 이미지 | 10GB |
| 코드 + 레이어 합계 | 250MB |
동시성 제한
| 항목 | 제한 |
|---|---|
| 계정당 동시 실행 | 1,000 (기본, 증가 가능) |
| Burst 동시성 | 500 ~ 3,000 (리전별) |
| 함수별 Reserved | 계정 한도 - 100 |
요금
요금 구성
총 비용 = 요청 비용 + 컴퓨팅 비용
요청 비용:
- 100만 요청당 $0.20
- 첫 100만 요청/월 무료
컴퓨팅 비용:
- GB-초당 $0.0000166667
- 400,000 GB-초/월 무료
예시 계산
함수 설정: 512MB 메모리, 평균 200ms 실행
월 호출: 3,000,000회
요청 비용:
(3,000,000 - 1,000,000) × $0.0000002 = $0.40
컴퓨팅 비용:
GB-초 = 3,000,000 × 0.5GB × 0.2초 = 300,000 GB-초
무료 한도 내 → $0
총 비용: $0.40/월
Provisioned Concurrency 비용
프로비저닝 비용 + 실행 비용
프로비저닝 비용:
- GB-시간당 $0.000004463
실행 비용:
- GB-초당 $0.0000041667 (일반 대비 저렴)
배포 방법
AWS CLI
# 코드 패키징
zip -r function.zip index.js node_modules/
# 함수 생성
aws lambda create-function \
--function-name my-function \
--runtime nodejs20.x \
--role arn:aws:iam::123456789012:role/lambda-role \
--handler index.handler \
--zip-file fileb://function.zip
# 코드 업데이트
aws lambda update-function-code \
--function-name my-function \
--zip-file fileb://function.zip
AWS SAM
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: nodejs20.x
Timeout: 30
MemorySize: 256
Resources:
HelloFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/
Handler: index.handler
Events:
Api:
Type: Api
Properties:
Path: /hello
Method: get
# 빌드 및 배포
sam build
sam deploy --guided
Serverless Framework
# serverless.yml
service: my-service
provider:
name: aws
runtime: nodejs20.x
region: ap-northeast-2
functions:
hello:
handler: handler.hello
events:
- http:
path: hello
method: get
# 배포
serverless deploy
컨테이너 이미지
# Dockerfile
FROM public.ecr.aws/lambda/nodejs:20
COPY index.js package*.json ./
RUN npm ci --production
CMD ["index.handler"]
# 빌드 및 푸시
docker build -t my-lambda .
aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_URI
docker tag my-lambda:latest $ECR_URI/my-lambda:latest
docker push $ECR_URI/my-lambda:latest
# Lambda 생성
aws lambda create-function \
--function-name my-function \
--package-type Image \
--code ImageUri=$ECR_URI/my-lambda:latest \
--role arn:aws:iam::123456789012:role/lambda-role
Best Practices
코드 최적화
// 1. 핸들러 외부에서 초기화
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({});
// 2. Keep-Alive 사용 (HTTP 연결 재사용)
import https from 'https';
const agent = new https.Agent({ keepAlive: true });
// 3. 불필요한 의존성 제거
// package.json의 dependencies 최소화
export const handler = async (event) => {
// 4. 비동기 작업 병렬 실행
const [user, orders] = await Promise.all([
getUser(event.userId),
getOrders(event.userId)
]);
return { user, orders };
};
보안
// 1. 최소 권한 IAM 역할
// 필요한 권한만 부여
// 2. 민감 정보는 환경 변수 + KMS 암호화
// 또는 Secrets Manager 사용
import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
const secretsClient = new SecretsManagerClient({});
// 캐싱 (Cold Start 시 1회만)
let cachedSecret;
async function getSecret() {
if (!cachedSecret) {
const response = await secretsClient.send(
new GetSecretValueCommand({ SecretId: 'my-secret' })
);
cachedSecret = JSON.parse(response.SecretString);
}
return cachedSecret;
}
// 3. 입력 검증
export const handler = async (event) => {
if (!event.userId || typeof event.userId !== 'string') {
throw new Error('Invalid userId');
}
// ...
};
멱등성 보장
import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({});
export const handler = async (event) => {
const requestId = event.requestContext?.requestId || event.Records[0].messageId;
// 중복 처리 방지
try {
await client.send(new PutItemCommand({
TableName: 'ProcessedRequests',
Item: {
requestId: { S: requestId },
processedAt: { S: new Date().toISOString() }
},
ConditionExpression: 'attribute_not_exists(requestId)'
}));
// 실제 처리
await processEvent(event);
} catch (error) {
if (error.name === 'ConditionalCheckFailedException') {
console.log('Duplicate request, skipping');
return;
}
throw error;
}
};
실무 예제
API Gateway + Lambda + DynamoDB
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, GetCommand, PutCommand, ScanCommand, DeleteCommand } from '@aws-sdk/lib-dynamodb';
const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);
const TABLE_NAME = process.env.TABLE_NAME;
export const handler = async (event) => {
const { httpMethod, pathParameters, body } = event;
try {
let result;
switch (httpMethod) {
case 'GET':
if (pathParameters?.id) {
result = await getItem(pathParameters.id);
} else {
result = await listItems();
}
break;
case 'POST':
result = await createItem(JSON.parse(body));
break;
case 'DELETE':
result = await deleteItem(pathParameters.id);
break;
default:
return response(405, { message: 'Method not allowed' });
}
return response(200, result);
} catch (error) {
console.error('Error:', error);
return response(500, { message: error.message });
}
};
async function getItem(id) {
const { Item } = await docClient.send(new GetCommand({
TableName: TABLE_NAME,
Key: { id }
}));
return Item;
}
async function listItems() {
const { Items } = await docClient.send(new ScanCommand({
TableName: TABLE_NAME
}));
return Items;
}
async function createItem(item) {
await docClient.send(new PutCommand({
TableName: TABLE_NAME,
Item: { ...item, id: crypto.randomUUID() }
}));
return item;
}
async function deleteItem(id) {
await docClient.send(new DeleteCommand({
TableName: TABLE_NAME,
Key: { id }
}));
return { deleted: true };
}
function response(statusCode, body) {
return {
statusCode,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
};
}
S3 이미지 리사이징
import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import sharp from 'sharp';
const s3 = new S3Client({});
const DEST_BUCKET = process.env.DEST_BUCKET;
export const handler = async (event) => {
const srcBucket = event.Records[0].s3.bucket.name;
const srcKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
// 원본 이미지 가져오기
const { Body, ContentType } = await s3.send(new GetObjectCommand({
Bucket: srcBucket,
Key: srcKey
}));
const imageBuffer = await streamToBuffer(Body);
// 리사이징
const sizes = [
{ suffix: 'thumb', width: 150, height: 150 },
{ suffix: 'medium', width: 500, height: 500 },
{ suffix: 'large', width: 1000, height: 1000 }
];
await Promise.all(sizes.map(async (size) => {
const resized = await sharp(imageBuffer)
.resize(size.width, size.height, { fit: 'inside' })
.toBuffer();
const destKey = srcKey.replace(/(\.[^.]+)$/, `-${size.suffix}$1`);
await s3.send(new PutObjectCommand({
Bucket: DEST_BUCKET,
Key: destKey,
Body: resized,
ContentType
}));
}));
console.log(`Resized ${srcKey} to ${sizes.length} sizes`);
};
async function streamToBuffer(stream) {
const chunks = [];
for await (const chunk of stream) {
chunks.push(chunk);
}
return Buffer.concat(chunks);
}
정리
| 항목 | 내용 |
|---|---|
| 런타임 | Node.js, Python, Java, Go, .NET, Ruby, Custom |
| 최대 실행 시간 | 15분 (900초) |
| 메모리 | 128MB ~ 10,240MB |
| 임시 스토리지 | 512MB ~ 10,240MB |
| 패키지 크기 | 50MB (압축), 250MB (압축 해제), 10GB (컨테이너) |
| 동시성 | 1,000 (기본, 증가 가능) |
| 무료 티어 | 100만 요청 + 400,000 GB-초/월 |
Lambda 선택 기준
Lambda가 적합한 경우:
- 이벤트 기반 처리
- 간헐적인 워크로드
- 빠른 스케일링 필요
- 인프라 관리 최소화
Lambda가 부적합한 경우:
- 15분 이상 실행 필요
- 일정한 고성능 요구
- 특정 하드웨어 필요
- 복잡한 상태 관리
Lambda는 서버리스 아키텍처의 핵심 서비스다. 적절한 설정과 최적화로 비용 효율적인 애플리케이션을 구축할 수 있다.
댓글