AWS 고급 #4 API Gateway + Lambda

8 분 소요

#3 Lambda 기초의 함수를 HTTP로 부르려면 입구가 필요합니다. AWS의 표준은 API Gateway입니다. Lambda (또는 ECS, EC2, 외부 백엔드) 앞에 두는 매니지드 게이트웨이입니다.

이번 글은 API Gateway의 두 형태 (REST / HTTP), Lambda 통합 패턴, 권한 옵션, 스테이지 / 배포, 사용량 플랜까지 한 번에 정리하겠습니다.

API Gateway가 하는 일 #

API Gateway가 하는 일
클라이언트 (브라우저, 앱)
   │ HTTPS
API Gateway
   ├─ TLS 종단
   ├─ 라우팅 (path → 백엔드)
   ├─ 인증 / 권한
   ├─ Rate limiting / Throttling
   ├─ 요청 변환
   └─ 캐싱
백엔드 (Lambda / ECS / 외부 HTTP)

ALB (중급 #6)와 기능이 비슷하지만 강조점이 다릅니다.

ALBAPI 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 함수 준비 #

lambda_function.py
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의 “구축”.

  1. 통합 추가: Lambda → 리전 / 함수 (hello-fn)
  2. API 이름: hello-api
  3. 라우트 설정: GET /hellohello-fn
  4. 스테이지: $default (자동 배포)
  5. 생성

완료하면 URL: https://abc123.execute-api.ap-northeast-2.amazonaws.com/hello?name=AWS

CLI로 만들기 #

HTTP API 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를 표준 응답 형태로 반환.

proxy 통합의 입출력
# 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)으로 정의.

VTL 매핑
{
  "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)로 라우팅.

FastAPI on Lambda
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 handler

API 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 계정 안에서 서비스 간 호출에 적합.

IAM 인증된 호출 (Python)
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의 강점.

JWT Authorizer 만들기
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가 책임. 최대 유연성.

lambda authorizer
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는 콘솔 한 번으로 끝납니다.

HTTP API CORS
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
  • 라우트 단위: 별도 설정 가능
라우트당 100 req/s로 제한
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에 연결 → 그 사용자의 요청에 자동으로 한도 적용.

API Key + 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 #

요청 단위로 기록됩니다. 운영 / 비즈니스 분석에 활용합니다.

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)와 연결.

Custom Domain
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의 메시지 인프라를 한 번에 정리하겠습니다.

X