AWS 고급 #4 API Gateway + Lambda
#3 Lambda 기초의 함수를 HTTP로 부르려면 입구가 필요합니다. AWS의 표준은 API Gateway입니다. Lambda (또는 ECS, EC2, 외부 백엔드) 앞에 두는 매니지드 게이트웨이입니다.
이번 글은 API Gateway의 두 형태 (REST / HTTP), Lambda 통합 패턴, 권한 옵션, 스테이지 / 배포, 사용량 플랜까지 한 번에 정리하겠습니다.
API Gateway가 하는 일 #
클라이언트 (브라우저, 앱)
│ HTTPS
▼
API Gateway
├─ TLS 종단
├─ 라우팅 (path → 백엔드)
├─ 인증 / 권한
├─ Rate limiting / Throttling
├─ 요청 변환
└─ 캐싱
│
▼
백엔드 (Lambda / ECS / 외부 HTTP)ALB (중급 #6)와 기능이 비슷하지만 강조점이 다릅니다.
| ALB | API Gateway | |
|---|---|---|
| 가격 모델 | 시간 + LCU | 요청 + 데이터 |
| 0 트래픽 비용 | 시간당 (~$20/월) | 0 |
| Lambda 직접 통합 | 가능 | 자연스러움 |
| API key / Usage Plan | 없음 | 있음 |
| 캐싱 | 없음 | 있음 (REST API) |
| Cognito 통합 | 별도 | 자연스러움 |
| 적합 | 항상 도는 큰 ECS | 변동 큰 / 서버리스 / API key 필요 |
REST API vs HTTP API #
API Gateway는 두 가지 형태로 제공됩니다.
HTTP API (v2): 신형 #
- 70% 더 저렴, 더 빠름
- JWT 토큰 (Cognito / Auth0 / 자체 OIDC) 직접 검증
- CORS 설정 단순
- Lambda proxy 통합 기본
- WebSocket 미지원, API key / Usage Plan 미지원, 요청 변환 / 캐싱 없음
새 프로젝트의 기본 후보. 단순한 REST API + JWT면 충분.
REST API (v1): 구형 + 풍부한 기능 #
- 캐싱 (스테이지 단위)
- 요청 / 응답 매핑 변환
- WAF 직접 통합
- API Key + Usage Plan
- WebSocket 지원
복잡한 변환 / 사용량 분리 / WebSocket이 필요할 때.
어떻게 고를까 #
WebSocket 필요? ─Yes→ REST API (또는 WebSocket API)
│ No
▼
API Key / Usage Plan 필요? ─Yes→ REST API
│ No
▼
복잡한 변환 / 캐싱 필요? ─Yes→ REST API
│ No
▼
HTTP API (저렴 + 빠름)이 글은 HTTP API 기준으로 진행하고, REST API만의 기능은 별도로 표시합니다.
첫 API: Hello, World #
Lambda 함수 준비 #
import json
def handler(event, context):
name = event.get("queryStringParameters", {}).get("name", "world")
return {
"statusCode": 200,
"headers": {"Content-Type": "application/json"},
"body": json.dumps({"message": f"Hello, {name}!"})
}이미 #3에서 다룬 함수입니다. 이 함수를 hello-fn으로 배포해 둠.
HTTP API 만들기 (콘솔) #
콘솔 → API Gateway → “API 생성” → HTTP API의 “구축”.
- 통합 추가: Lambda → 리전 / 함수 (
hello-fn) - API 이름:
hello-api - 라우트 설정:
GET /hello→hello-fn - 스테이지:
$default(자동 배포) - 생성
완료하면 URL: https://abc123.execute-api.ap-northeast-2.amazonaws.com/hello?name=AWS
CLI로 만들기 #
# API 생성
API_ID=$(aws apigatewayv2 create-api \
--name hello-api \
--protocol-type HTTP \
--target arn:aws:lambda:ap-northeast-2:123456789012:function:hello-fn \
--query ApiId --output text)
# Lambda가 API Gateway에서 호출되는 권한
aws lambda add-permission \
--function-name hello-fn \
--statement-id apigw-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:ap-northeast-2:123456789012:$API_ID/*/*"
echo "https://$API_ID.execute-api.ap-northeast-2.amazonaws.com/"--target 옵션이 라우트 + 통합을 자동으로 만들어 주는 단축 방법입니다. 정밀 제어가 필요하면 create-route, create-integration을 따로 실행하세요.
Lambda 통합: Proxy vs Non-proxy #
API Gateway → Lambda 호출의 두 모드.
Proxy 통합 (HTTP API의 기본) #
요청을 거의 그대로 Lambda에 전달. Lambda가 statusCode / headers / body를 표준 응답 형태로 반환.
# Event
{
"version": "2.0",
"routeKey": "POST /users",
"rawPath": "/users",
"queryStringParameters": {"limit": "10"},
"headers": {...},
"body": "{\"name\":\"Alice\"}",
"isBase64Encoded": false
}
# Lambda 응답
{
"statusCode": 201,
"headers": {"Content-Type": "application/json"},
"body": "{\"id\":42}"
}운영의 표준입니다. 코드가 자체 라우팅 / 응답 형성을 책임지므로, 변경이 생겨도 API Gateway 설정을 건드릴 필요가 없습니다.
Non-proxy (REST API만) #
API Gateway가 요청을 변환해서 Lambda가 원하는 형태로 보내고, 응답도 변환합니다. 매핑 템플릿 (Velocity Template Language)으로 정의.
{
"name": "$input.path('$.name')",
"user_id": "$context.authorizer.claims.sub"
}기존 Lambda가 raw event를 안 받고 깔끔한 입력만 받도록 할 때 씁니다. 학습 곡선이 있고 모니터링이 까다로워 새 프로젝트는 거의 항상 Proxy를 씁니다.
라우트와 메소드 #
라우트 = path + method 조합.
# Catch-all
aws apigatewayv2 create-route \
--api-id $API_ID --route-key 'ANY /'
# 특정 메소드
aws apigatewayv2 create-route \
--api-id $API_ID --route-key 'GET /users'
aws apigatewayv2 create-route \
--api-id $API_ID --route-key 'POST /users'
aws apigatewayv2 create-route \
--api-id $API_ID --route-key 'GET /users/{userId}'{userId} 같은 파라미터는 event의 pathParameters에 들어옵니다.
한 Lambda가 여러 라우트 #
작은 API면 한 Lambda가 모든 라우트 처리. Lambda 안에서 FastAPI / Flask 같은 프레임워크 + adapter (예: Mangum)로 라우팅.
from fastapi import FastAPI
from mangum import Mangum
app = FastAPI()
@app.get("/users")
def list_users():
return [{"id": 1, "name": "Alice"}]
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {"id": user_id, "name": "Alice"}
handler = Mangum(app) # Lambda handlerAPI Gateway 라우트는 ANY /{proxy+} 한 줄로 끝.
라우트 별 다른 Lambda #
큰 API는 라우트별로 Lambda를 분리합니다. 권한 / 자원 / 배포 단위가 명확해집니다.
GET /users → list-users-fn
GET /users/{id} → get-user-fn
POST /users → create-user-fn분리하면 콜드 스타트도 분산됩니다. 한 함수당 워밍 / 동시성을 따로 관리할 수 있습니다.
권한: 누가 호출할 수 있나 #
API Gateway에서 가장 자주 만나는 설정입니다. 네 옵션.
1) Open (인증 없음) #
테스트 / 공개 API. 운영에는 거의 안 쓰임.
2) IAM 인증 #
요청에 SigV4 서명 (AWS SDK가 자동) 필요. AWS 계정 안에서 서비스 간 호출에 적합.
import boto3
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
import requests
session = boto3.Session()
creds = session.get_credentials()
url = "https://abc.execute-api.ap-northeast-2.amazonaws.com/hello"
req = AWSRequest(method="GET", url=url)
SigV4Auth(creds, "execute-api", "ap-northeast-2").add_auth(req)
resp = requests.get(url, headers=dict(req.headers))브라우저에서 직접 호출은 어려움 (서명 노출). 백엔드 ↔ 백엔드.
3) JWT Authorizer (HTTP API) #
Cognito User Pool / Auth0 / 자체 OIDC의 ID 토큰을 API Gateway가 직접 검증. HTTP API의 강점.
aws apigatewayv2 create-authorizer \
--api-id $API_ID \
--authorizer-type JWT \
--identity-source '$request.header.Authorization' \
--jwt-configuration '{
"Audience": ["my-app"],
"Issuer": "https://cognito-idp.ap-northeast-2.amazonaws.com/<UserPoolId>"
}' \
--name jwt-auth라우트에 붙이면 Authorization: Bearer eyJ... 헤더가 자동 검증되고, claims가 Lambda event의 requestContext.authorizer.jwt.claims에 들어옴.
4) Lambda Authorizer (Custom) #
검증 로직 자체를 내가 짠 Lambda가 책임. 최대 유연성.
def handler(event, context):
token = event["headers"]["authorization"].replace("Bearer ", "")
# 자체 검증 로직 (DB 조회 / 외부 호출 등)
user = verify_token_somehow(token)
if not user:
return {"isAuthorized": False}
return {
"isAuthorized": True,
"context": {"user_id": user.id, "role": user.role}
}캐싱 가능 (TTL 동안 같은 토큰은 검증 건너뜀). 외부 인증 시스템 연동에 자연스러움.
5) Cognito User Pool Authorizer (REST API 전용) #
REST API에서 Cognito 직접 통합. HTTP API의 JWT Authorizer와 비슷한 기능이지만 REST API에서.
CORS #
브라우저에서 호출하려면 CORS 설정이 필수입니다. HTTP API는 콘솔 한 번으로 끝납니다.
aws apigatewayv2 update-api \
--api-id $API_ID \
--cors-configuration "AllowOrigins=https://myapp.com,AllowMethods=GET,POST,AllowHeaders=*"REST API는 각 라우트에 OPTIONS 응답을 직접 만들어야 합니다. 콘솔에 “CORS 활성화” 버튼이 있어 자동으로 생성됩니다.
스테이지와 배포 #
HTTP API #
자동 배포 옵션이 기본입니다. 라우트 변경이 즉시 반영되며, 스테이지는 $default 한 개입니다.
다른 환경 (dev / staging / prod) 분리는 API 자체를 따로 만드는 방향.
REST API #
명시적 배포 + 스테이지. dev, staging, prod 같은 스테이지에 동시에 다른 버전을 운영 가능.
aws apigateway create-deployment --rest-api-id $REST_ID --stage-name prod스테이지마다 캐싱, 로깅, throttling, 변수를 다르게.
Throttling: 트래픽 제한 #
기본 한도:
- 계정 단위: 10,000 req/s, burst 5,000
- 라우트 단위: 별도 설정 가능
aws apigatewayv2 update-stage \
--api-id $API_ID --stage-name '$default' \
--route-settings '{
"GET /heavy": {
"ThrottlingRateLimit": 100,
"ThrottlingBurstLimit": 50
}
}'악성 / 무거운 라우트만 강하게. 다른 라우트는 영향 없음.
API Key + Usage Plan (REST API) #
외부 사용자 / 파트너에 API를 노출하는 용도.
API Key (사용자별 고유 문자열)
│
▼
Usage Plan (한도, e.g. "월 10만 req, 초당 50 req")
│
▼
Stage (실제 API)사용자 등록 시 API Key 발급 → Usage Plan에 연결 → 그 사용자의 요청에 자동으로 한도 적용.
KEY_ID=$(aws apigateway create-api-key \
--name customer-A --enabled --query id --output text)
PLAN_ID=$(aws apigateway create-usage-plan \
--name basic-tier \
--throttle 'rateLimit=50,burstLimit=100' \
--quota 'limit=100000,period=MONTH' \
--query id --output text)
aws apigateway create-usage-plan-key \
--usage-plan-id $PLAN_ID --key-id $KEY_ID --key-type API_KEY
aws apigateway update-usage-plan \
--usage-plan-id $PLAN_ID \
--patch-operations 'op=add,path=/apiStages,value=<rest-api-id>:prod'호출 측은 헤더 x-api-key: <키>를 같이 보냄.
HTTP API는 API Key / Usage Plan 미지원. 필요하면 REST API.
캐싱 (REST API) #
스테이지 단위로 응답을 캐싱합니다. GET 요청에 자연스럽게 어울립니다.
aws apigateway update-stage \
--rest-api-id $REST_ID --stage-name prod \
--patch-operations 'op=replace,path=/cacheClusterEnabled,value=true' \
--patch-operations 'op=replace,path=/cacheClusterSize,value=0.5'- 캐시 크기: 0.5GB ~ 237GB
- TTL: 라우트별 설정
- 무효화: 헤더
Cache-Control: max-age=0(필요 권한 있는 호출자만)
비용은 시간당 과금입니다. 트래픽이 정말 많은 경우에만 도입할 가치가 있습니다.
로깅: Access Log + Execution Log #
Access Log #
요청 단위로 기록됩니다. 운영 / 비즈니스 분석에 활용합니다.
aws apigatewayv2 update-stage \
--api-id $API_ID --stage-name '$default' \
--access-log-settings '{
"DestinationArn": "arn:aws:logs:ap-northeast-2:123456789012:log-group:/aws/apigateway/hello-api",
"Format": "{ \"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"requestTime\":\"$context.requestTime\",\"routeKey\":\"$context.routeKey\",\"status\":\"$context.status\" }"
}'JSON으로 받아 CloudWatch Logs Insights에서 쿼리.
Execution Log (REST API) #
내부 단계별 로그. 디버깅용. 운영에선 비용 / 노이즈로 잘 안 켬.
사용자 도메인: Custom Domain #
https://api.myapp.com/...으로 노출. ACM 인증서 (중급 #6) + Route 53 (중급 #5)와 연결.
aws apigatewayv2 create-domain-name \
--domain-name api.myapp.com \
--domain-name-configurations \
"CertificateArn=arn:aws:acm:ap-northeast-2:...,EndpointType=REGIONAL"
# API → Domain 매핑
aws apigatewayv2 create-api-mapping \
--api-id $API_ID \
--domain-name api.myapp.com \
--stage '$default'
# Route 53 : A 레코드 alias
aws route53 change-resource-record-sets ...비용 #
HTTP API #
- 요청당 $1.00 / 100만 (~$0.000001)
- Data Transfer 별도
REST API #
- 요청당 $3.50 / 100만 (3.5배)
- 캐싱 활성화 시 시간당 추가
월 100만 요청:
- HTTP API: $1
- REST API: $3.50 (+ 캐시 / Usage Plan 사용 시 추가)
Lambda만큼은 아니어도 매우 저렴.
자주 만나는 함정 #
1) Lambda가 호출 안 됨 / 권한 거부 #
lambda:InvokeFunction 권한이 API Gateway에 부여 안 됨. 콘솔 통합은 자동, CLI는 aws lambda add-permission 직접.
2) CORS가 안 풀림 #
브라우저 콘솔에 CORS 오류. 두 가지를 점검:
- API Gateway CORS 설정 (위 참고)
- Lambda 응답에
Access-Control-Allow-Origin헤더 (proxy 통합은 Lambda가 책임)
3) 타임아웃 #
API Gateway의 통합 타임아웃은 최대 30초 (REST), 30초 (HTTP). Lambda는 15분이지만 API Gateway가 잘라냄. 30초 넘는 처리는 비동기 (Lambda Destination, Step Functions, SQS)로 우회.
4) Body 크기 한도 #
API Gateway의 payload 한도 6MB (양쪽). 큰 파일은 S3 presigned URL 패턴.
5)의도치 않은 자동 배포 (HTTP API) #
라우트 / 통합 변경이 즉시 운영 반영. CI에서 IaC (Terraform / CDK)로 관리하면 의도된 변경이지만, 콘솔 직접 수정은 위험. 콘솔은 dev / staging API만.
6) JWT Authorizer의 audience / issuer 오타 #
aud / iss가 한 글자라도 다르면 401. Cognito User Pool ID의 정확한 issuer URL (https://cognito-idp.<리전>.amazonaws.com/<UserPoolId>) 확인.
7) API Key가 모든 보안의 끝이 아님 #
API Key는 클라이언트에 평문으로 남아 있음. 결제 정보 같은 용도에는 단독 인증으로 쓰지 않습니다. 사용량 분리 + Throttling 용도입니다. 진짜 인증은 JWT / IAM.
정리 #
이번 글에서 잡은 것:
- API Gateway의 역할. Lambda / ECS / 외부 백엔드 앞의 매니지드 게이트웨이입니다. 0 트래픽 비용 0
- HTTP API vs REST API. HTTP가 70% 저렴 + 빠름. WebSocket / API Key / 캐싱이 필요하면 REST
- Lambda 통합. Proxy (표준) vs Non-proxy (변환). 새 프로젝트는 Proxy
- 라우트 / 메소드.
GET /users/{id}같은 패턴. 한 Lambda 다 라우팅 (Mangum 등) vs 라우트별 분산 - 권한 4 옵션. Open / IAM / JWT / Lambda Authorizer / (REST의) Cognito User Pool
- JWT Authorizer (HTTP API). Cognito / Auth0 / OIDC ID 토큰 직접 검증
- CORS. HTTP API는 한 줄
- 스테이지. HTTP API는 자동 배포 / REST는 명시적 배포 + 환경 분리
- Throttling. 라우트별 한도
- API Key + Usage Plan (REST만). 외부 사용자별 한도
- 캐싱 (REST만). 스테이지 단위
- Access Log. JSON으로 CloudWatch Logs
- Custom Domain. ACM + Route 53
- 비용. HTTP API 요청당 $0.000001, 매우 저렴
- 함정. Lambda 권한, CORS, 30초 타임아웃, 6MB Payload, HTTP API 자동배포, JWT issuer 오타, API Key의 역할
다음: 메시지 인프라 #
API Gateway / Lambda의 동기 호출은 여기까지입니다. 이제 비동기 / 이벤트 주도 통신을 정리할 차례입니다.
#5 EventBridge / SQS / SNS에서는 셋의 차이 비교, fan-out 패턴, DLQ, 멱등성 (idempotency) 까지, AWS의 메시지 인프라를 한 번에 정리하겠습니다.