Lambda 기초
AWS 서버리스의 첫 단추를 정리합니다. Lambda의 역할(vs ECS / EC2), runtime / handler / event / context 모델, 동기 / 비동기 / 스트림 호출, 동시성과 콜드 스타트, Reserved / Provisioned Concurrency, 메모리·시간 한도, 로깅과 Layers, 비용까지 다룹니다.
15장 ECS와 Fargate와 16장 ECR은 컨테이너가 항상 떠 있는 모델이었습니다. 트래픽이 0일 때도 컨테이너 한 개는 살아 있습니다. 트래픽 변동이 크거나, 짧은 처리만 필요하거나, 운영 부담을 더 줄이고 싶을 때는 다른 선택지가 어울립니다 — Lambda입니다.
본 챕터는 Lambda의 동작 방식, 모델(runtime / handler / event), 호출 방식, 콜드 스타트, 동시성과 한도, 로깅까지 한 번에 정리합니다. 여기서 잡는 모델은 18장 API Gateway + Lambda의 HTTP 노출, 19장 EventBridge / SQS / SNS의 이벤트 처리, 그리고 5부 31장 Lambda 깊이의 운영 패턴으로 이어집니다.
Lambda가 하는 일 #
AWS Lambda는 서버리스 함수 실행 플랫폼입니다. 이벤트가 들어오면 그제서야 함수가 깨어나고, 끝나면 다시 사라집니다. 트래픽이 0 이면 비용도 0입니다.
이벤트 (HTTP / S3 업로드 / SQS 메시지 / Cron)
│
▼
Lambda가 컨테이너 hot 또는 cold로 띄움
│
▼
내 핸들러 함수 실행 (수 ms ~ 15 분)
│
▼
응답 / 결과 → 호출자
│
▼
일정 시간 idle 후 컨테이너 종료언제 Lambda가 어울리는가 #
Lambda가 어울리는 경우 #
- 이벤트 주도 — S3 업로드 → 썸네일 생성, SQS 메시지 → 처리, EventBridge 스케줄 → 배치
- 변동 큰 트래픽 — 평소 0, 가끔 폭증. 0일 때 비용 안 듦
- 짧은 처리 — 보통 수 초 ~ 수 분
- 사이드 / 보조 워크로드 — 메인 시스템 옆의 보조 함수
- API의 일부만 — 모든 API가 Lambda 일 필요는 없음
Lambda가 맞지 않는 경우 #
| 경우 | 이유 |
|---|---|
| 항상 트래픽이 도는 큰 API | 동시성 / 콜드 스타트 / 비용에서 ECS가 유리 |
| 15분 넘는 처리 | Lambda 한 호출은 최대 15분 |
| 매우 큰 메모리 / GPU | Lambda는 최대 10GB 메모리, GPU 없음 |
| Stateful 연결 (WebSocket의 백엔드 등) | 가능하지만 설계 복잡 |
| 항상 켜져 있는 DB connection pool | 호출마다 새로 연결되는 방식 |
비교 #
| EC2 | ECS / Fargate | Lambda | |
|---|---|---|---|
| 켜져 있는 시간 | 항상 | 항상 (Service) | 호출마다 |
| 운영 부담 | 큼 | 중간 (Fargate 작음) | 작음 |
| 콜드 스타트 | 없음 | 작음 | 있음 (수십 ms ~ 수 초) |
| 시간 한도 | 무한 | 무한 | 15 분 |
| 트래픽 0 비용 | 큼 | 중간 | 0 |
| 동시 처리 | OS 레벨 | 컨테이너 1개에 다중 | 함수 인스턴스 1개당 동시 1 개 |
마지막 줄이 중요합니다. Lambda는 함수 인스턴스 한 개가 동시에 한 개의 호출만 처리합니다. 동시 호출이 N 개면 인스턴스도 N 개로 자동 확장됩니다.
첫 Lambda — Hello, World #
함수 만들기 (콘솔) #
콘솔 → Lambda → “함수 생성” → 직접 작성 → Python 3.13으로 진행합니다.
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": "Hello, Lambda"
}저장하고 “테스트"를 누르면 빈 이벤트로 호출됩니다. CloudWatch Logs에 자동으로 로그 그룹 (/aws/lambda/<함수이름>)이 생성됩니다.
CLI로 만들기 #
코드를 zip으로 묶습니다.
zip function.zip lambda_function.py
aws lambda create-function \
--function-name hello \
--runtime python3.13 \
--role arn:aws:iam::123456789012:role/lambda-basic-role \
--handler lambda_function.lambda_handler \
--zip-file fileb://function.zipIaC (Terraform) #
resource "aws_lambda_function" "hello" {
function_name = "hello"
runtime = "python3.13"
handler = "lambda_function.lambda_handler"
role = aws_iam_role.lambda.arn
filename = "function.zip"
source_code_hash = filebase64sha256("function.zip")
memory_size = 256
timeout = 10
}Terraform 자체는 25장 Terraform 입문에서 본격적으로 다룹니다.
모델 — Runtime / Handler / Event / Context #
Lambda의 모델을 네 키워드로 정리합니다.
Runtime #
언어와 버전입니다. 매니지드 runtime은 다음과 같습니다.
- Python (3.10~3.13)
- Node.js (18, 20, 22)
- Java (8, 11, 17, 21)
- .NET, Ruby, Go (
provided.al2023위) - Custom Runtime — Rust / Zig / Swift 등 직접 지원
또는 컨테이너 이미지 (ECR)로 배포합니다 — 16장 ECR과 자연스럽게 연결됩니다. 큰 의존성이 있을 때 유리합니다 (zip 한도 250MB, 컨테이너는 10GB).
Handler #
Lambda가 호출할 함수의 이름입니다. <파일이름>.<함수이름> 형식입니다.
def main(event, context):
...→ Handler 설정: myapp.handler.main
Event #
호출자가 보낸 데이터입니다. JSON 객체이며, 호출 소스마다 모양이 다릅니다.
{
"version": "2.0",
"routeKey": "GET /hello",
"headers": {...},
"queryStringParameters": {...},
"body": "..."
}{
"Records": [
{
"eventSource": "aws:s3",
"s3": {
"bucket": {"name": "my-bucket"},
"object": {"key": "uploads/photo.jpg"}
}
}
]
}Lambda Powertools (Python / TypeScript / Java) 같은 라이브러리가 이 event 들을 타입 안전하게 다루는 데 큰 도움이 됩니다 — 실전에서는 적극 활용합니다.
Context #
런타임 정보입니다. 함수 실행 시간 한도 (get_remaining_time_in_millis()), request id, 함수 이름 등이 들어 있습니다.
def handler(event, context):
print(context.aws_request_id)
print(context.function_name)
print(context.get_remaining_time_in_millis()) # 남은 시간 ms호출 방식 — 동기 vs 비동기 vs 스트림 #
호출 소스에 따라 Lambda의 동작이 다릅니다.
1) 동기 (Synchronous) #
호출자가 결과를 기다립니다. 응답을 받을 때까지 블록합니다.
| 호출 소스 |
|---|
| API Gateway |
| ALB |
| Cognito |
직접 Invoke API |
aws lambda invoke \
--function-name hello \
--payload '{"name": "world"}' \
--cli-binary-format raw-in-base64-out \
out.json2) 비동기 (Asynchronous) #
호출자가 큐에 넣고 끝납니다. Lambda가 백그라운드로 처리합니다. 실패 시 자동 재시도 (기본 2회)와 DLQ (Dead Letter Queue)로 이송합니다.
| 호출 소스 |
|---|
| S3 ObjectCreated |
| SNS |
| EventBridge |
InvocationType=Event의 Invoke |
aws lambda invoke \
--function-name hello \
--invocation-type Event \
--payload '{"key": "value"}' \
--cli-binary-format raw-in-base64-out \
out.json3) 스트림 / 폴링 #
Lambda가 큐 / 스트림을 자동 폴링합니다.
| 호출 소스 |
|---|
| SQS |
| DynamoDB Streams |
| Kinesis |
| MSK (Managed Kafka) |
설정만 해 두면 새 메시지가 오는 대로 Lambda가 batch로 받아 처리합니다. 실패하면 retry와 DLQ로 갑니다. SQS와의 연결은 19장 EventBridge / SQS / SNS에서 자세히 다룹니다.
동시성 (Concurrency)의 의미 #
Lambda의 가장 중요한 한 가지입니다. 함수 인스턴스 한 개가 동시 한 호출만 처리하니, 동시에 들어온 호출 수 = 띄워진 인스턴스 수입니다.
초당 10 개 호출, 각 호출 1 초 → 동시 ~10 개 인스턴스
초당 100 개 호출, 각 호출 100ms → 동시 ~10 개 인스턴스계정 한도 (Account Concurrency) #
리전별 기본 1,000입니다. 운영 워크로드는 종종 부족합니다 — Service Quotas 콘솔에서 증액을 요청합니다.
Reserved Concurrency #
특정 함수에 “최대 N 개"를 보장하고 제한합니다.
aws lambda put-function-concurrency \
--function-name hello \
--reserved-concurrent-executions 100용도는 다음과 같습니다.
- 위험한 함수의 폭주 차단 (예: 유료 외부 API 호출하는 함수)
- 다른 중요 함수에 동시성 여유 남기기 (이 함수가 1,000을 다 차지하지 못하게)
- DB connection 폭주 방지 (RDS connection pool 보호)
Provisioned Concurrency — 콜드 스타트 회피 #
미리 N 개 인스턴스를 데워 둡니다. 동시 호출이 N 이하면 콜드 스타트가 0입니다.
aws lambda put-provisioned-concurrency-config \
--function-name hello \
--qualifier prod \
--provisioned-concurrent-executions 10비용은 데워 둔 인스턴스 시간만큼 과금됩니다 (약간 저렴한 단가). API의 입구라 콜드 스타트가 사용자 경험에 직접 영향이라면 검토합니다.
콜드 스타트 — 가장 자주 만나는 함정 #
함수 인스턴스가 새로 만들어질 때의 초기 비용입니다. 두 단계로 나뉩니다.
[INIT 단계] — 한 번만
├─ 컨테이너 환경 준비
├─ runtime 시작
├─ 핸들러 모듈 import
└─ 핸들러 함수 외부의 코드 실행 (전역)
[INVOKE 단계] — 매 호출마다
└─ 핸들러 함수 실행INIT은 인스턴스의 첫 호출에만 비용입니다. 같은 인스턴스가 다음 호출을 받으면 INIT을 건너뜁니다 (warm).
콜드 스타트 시간 #
대략적인 구간입니다.
| 언어 / 형태 | INIT 시간 |
|---|---|
| Python (작은 의존성) | ~150ms |
| Python (큰 의존성, 예: boto3 + pandas) | ~1~2초 |
| Node.js (작은) | ~100ms |
| Java | ~500ms ~ 수 초 |
| 컨테이너 이미지 (큰) | ~수 초 |
콜드 스타트 줄이기 #
- 메모리 늘리기 — Lambda는 메모리에 비례해 vCPU도 늘어납니다. 256MB → 1024MB만 해도 INIT이 빨라집니다.
- 의존성 슬리밍 — 안 쓰는 패키지 빼기, tree-shaking
- 전역 변수 활용 — 핸들러 외부에서 한 번 만든 객체 (boto3 client, DB connection 등)를 warm 한 인스턴스가 재사용
- Provisioned Concurrency — 위에서 본 대로
- Lambda SnapStart — INIT 결과를 스냅샷 떠서 빠른 복구. 현재 Java / Python / .NET에서 지원
전역 변수 패턴 #
import boto3
# Lambda 인스턴스 한 번만 — INIT 단계
s3 = boto3.client("s3")
def handler(event, context):
# 핸들러는 깨끗하게 — 매 호출마다 client 안 만듦
return s3.list_buckets()def handler(event, context):
# 매 호출마다 boto3 client — 느림
s3 = boto3.client("s3")
return s3.list_buckets()메모리와 시간 한도 #
| 항목 | 한도 |
|---|---|
| 메모리 | 128MB~10,240MB (1MB 단위) |
| 시간 | 1초~900초 (15분) |
임시 디스크 (/tmp) | 512MB~10GB |
| 환경 변수 | 4KB |
| Payload (동기) | 6MB |
| Payload (비동기) | 256KB |
| zip (소스) | 50MB (압축) |
| zip (압축 해제) | 250MB |
| 컨테이너 이미지 | 10GB |
메모리는 vCPU와 묶여 있습니다. 1,769MB에서 vCPU 한 개 분량입니다. 메모리를 늘리면 CPU도 같이 늘어 함수가 빨라지는 경우가 흔합니다 — 메모리만 늘려도 비용이 줄 수 있습니다 (Lambda Power Tuning으로 최적값을 찾습니다).
로깅 — CloudWatch Logs #
Lambda의 모든 stdout / stderr는 자동으로 CloudWatch Logs의 /aws/lambda/<함수이름> 로그 그룹에 들어갑니다.
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handler(event, context):
logger.info("Hello %s", event.get("name"))
return "ok"운영에서는 구조화 로그를 권장합니다 — JSON으로 출력하면 CloudWatch Logs Insights에서 필드별 쿼리가 가능합니다.
import json, logging
logger = logging.getLogger()
def handler(event, context):
logger.info(json.dumps({
"request_id": context.aws_request_id,
"user_id": event.get("user_id"),
"action": "process",
"duration_ms": 42
}))Powertools의 Logger가 이것을 한 줄로 깔끔하게 처리합니다.
Layers — 코드 재사용 #
여러 함수가 같은 의존성 / 유틸리티를 쓸 때 Lambda Layer로 분리합니다.
# python 의존성
mkdir -p python
pip install requests -t python/
zip -r layer.zip python
aws lambda publish-layer-version \
--layer-name my-utils \
--zip-file fileb://layer.zip \
--compatible-runtimes python3.13aws lambda update-function-configuration \
--function-name hello \
--layers arn:aws:lambda:ap-northeast-2:123456789012:layer:my-utils:1장점은 함수 zip이 작아지고 의존성을 한 번만 업데이트한다는 점입니다. 단점은 너무 많아지면 추적이 어렵다는 점입니다 (5개 한도).
Lambda 비용 #
호출당 비용 = (호출 수 × $0.0000002)
+ (실행시간 GB-초 × $0.0000166667)월 100만 호출 + 호출당 100ms + 256MB의 예입니다.
- 호출 비용: 100만 × $0.0000002 = $0.20
- 시간 비용: 100만 × 0.1초 × 0.25GB × $0.0000166667 = $0.42
- 총: ~$0.62 / 월
매우 저렴합니다. 무료 티어로 월 100만 호출 + 400,000GB-초가 무료입니다. 작은 워크로드는 사실상 0입니다.
비용이 커지는 경우는 다음과 같습니다.
- 호출당 시간이 길고 메모리가 큰 함수 (예: 5분 × 3GB)
- 동시성이 항상 높은 함수 — ECS / Fargate가 더 쌀 수 있음
자주 만나는 함정 #
1) 콜드 스타트로 첫 호출 느림 #
API 입구의 Lambda가 0.5~2 초씩 걸립니다. 사용자가 못 견딥니다. 위의 “콜드 스타트 줄이기” 5 가지와 Provisioned Concurrency를 검토합니다.
2) 핸들러 안에서 매번 객체 생성 #
def handler(event, context):
db = create_db_connection()
boto = boto3.client("s3")
...매 호출마다 100ms의 낭비입니다. 전역으로 끌어냅니다.
3) RDS connection pool 폭주 #
Lambda 동시 100개 → DB 연결 100개 → DB가 connection 한도 초과입니다. 대안은 다음과 같습니다.
- RDS Proxy — Lambda 들이 공유하는 connection pool (11장 RDS)
- Reserved Concurrency로 함수 동시성 제한
- DynamoDB 같은 서버리스 DB 사용
4) 비동기 호출의 실수가 조용히 사라짐 #
S3 → Lambda가 실패해도 호출자는 모릅니다. DLQ (SQS) 또는 Lambda Destination으로 실패를 캡처합니다.
aws lambda put-function-event-invoke-config \
--function-name hello \
--maximum-retry-attempts 2 \
--destination-config '{"OnFailure":{"Destination":"arn:aws:sqs:...:dlq"}}'5) 시간 초과로 잘려 나감 #
15분 한도를 모르고 큰 처리를 한 함수에 넣으면 14분 59초에 강제 종료됩니다. 긴 처리는 21장 Step Functions 입문으로 분할하거나 ECS / Fargate로 옮깁니다.
6) Payload가 6MB 넘음 #
API Gateway → Lambda 동기 호출의 한도입니다. 큰 파일은 S3 presigned URL 패턴으로 우회합니다 — Lambda가 presigned URL만 발급하고, 클라이언트가 직접 S3에 업로드합니다.
7) 환경 변수에 비밀 #
평문 환경변수에 DB 비밀번호를 두면 로그 / 콘솔에서 노출됩니다. 20장 Secrets Manager / Parameter Store로 옮깁니다.
연습문제 #
- 본인이 만들려는 처리가 §“언제 Lambda가 어울리는가"의 어울리는 경우와 맞지 않는 경우 중 어디에 속하는지 한 줄로 판단하고, 만약 맞지 않는다면 15장 ECS와 Fargate 중 어느 모델로 가야 하는지 적어 보세요.
- §“콜드 스타트 줄이기"의 다섯 방법 중 코드 한 줄도 안 고치고 적용할 수 있는 것과 코드 구조를 바꿔야 하는 것을 나눠 보세요. 그리고 §“전역 변수 패턴"의 좋은 예와 나쁜 예가 콜드 스타트의 INIT / INVOKE 단계 중 어디에서 차이를 만드는지 연결해 두세요.
- §“Lambda 비용"의 계산을 따라, 월 500만 호출 + 호출당 200ms + 512MB 함수의 한 달 비용을 직접 계산해 보세요. 같은 워크로드를 15장 ECS와 Fargate의 Fargate로 항상 띄운다면 어느 쪽이 쌀지 한 줄로 판단해 두세요.
한 줄 요약: Lambda는 이벤트가 올 때만 깨어나는 서버리스 함수로, 트래픽이 0 이면 비용도 0이고 이벤트 주도·변동 큰 트래픽·짧은 처리에 어울린다. 모델은 runtime · handler · event · context 네 개이고 호출은 동기·비동기·스트림으로 나뉘며, 함수 인스턴스 한 개가 동시 한 호출만 처리해 호출이 늘면 인스턴스가 자동 확장된다. 가장 자주 만나는 함정은 콜드 스타트로 메모리 늘리기·전역 변수·Provisioned Concurrency로 완화하고, 15분 한도·6MB payload·환경변수 비밀 같은 한계는 Step Functions·S3 presigned URL·Secrets Manager로 우회한다.
다음 챕터 #
함수만 있으면 호출할 입구가 부족합니다. 다음 18장 API Gateway + Lambda에서는 HTTP 요청으로 Lambda를 부르는 가장 흔한 패턴을 다룹니다. REST API와 HTTP API의 차이, Lambda 통합, 라우트 / 메소드, 권한(IAM / Cognito / Lambda authorizer), 스테이지 / 배포까지 서버리스 API의 입구를 정리합니다.