Lambda 프록시 통합 vs 표준 통합: API Gateway 이벤트 객체 구조 완전 분석
API Gateway를 처음 설정할 때 'Lambda 프록시 통합' 체크박스 하나가 이벤트 객체 구조 전체를 바꾼다는 사실을 모르고 넘어가면, Lambda 핸들러에서 event.body가 undefined로 찍히는 상황을 마주하게 된다. 이 글은 두 통합 방식의 내부 동작 차이와 그로 인한 이벤트 포맷 변화를 실제 운영 관점에서 정리한다.
TL;DR — Lambda 프록시 통합 핵심 요약
| 항목 | Lambda 프록시 통합 | Lambda 표준 통합 |
|---|---|---|
| 이벤트 구조 | API Gateway가 고정 포맷으로 전달 | 매핑 템플릿으로 개발자가 직접 정의 |
| 요청 매핑 템플릿 | 불필요 (자동 처리) | 필수 작성 |
| 응답 매핑 템플릿 | 불필요 (Lambda 반환값 그대로) | 필수 작성 |
| HTTP 상태코드 제어 | Lambda 반환 객체 내 statusCode 필드 | API Gateway 매핑 규칙 |
| 헤더 접근 | event.headers로 직접 접근 | 매핑 템플릿에서 명시적으로 추출 필요 |
| 설정 복잡도 | 낮음 | 높음 (VTL 템플릿 작성) |
| 유연성 | 포맷 고정 | 완전한 변환 제어 가능 |
Lambda 프록시 통합이란 무엇인가
API Gateway에서 Lambda를 백엔드로 사용할 때 두 가지 통합 방식을 선택할 수 있다. Lambda 프록시 통합(Lambda Proxy Integration)은 API Gateway가 HTTP 요청 전체 — 메서드, 경로, 쿼리스트링, 헤더, 바디 — 를 하나의 표준화된 JSON 이벤트 객체로 패키징해서 Lambda에 그대로 전달하는 방식이다. 반대로 Lambda 표준 통합(Lambda Non-Proxy Integration)은 개발자가 Velocity Template Language(VTL)로 작성한 매핑 템플릿을 통해 요청과 응답을 변환하는 방식이다.
프록시 통합에서 'Proxy'라는 단어는 API Gateway가 요청을 가공하지 않고 투명하게 통과시킨다는 의미다. 단, 실제로는 완전한 투과가 아니라 AWS가 정의한 고정 포맷으로 재구성해서 전달한다는 점을 정확히 이해해야 한다.
매핑 템플릿 없음"| Lambda1["Lambda 함수"] StdPath -->|"요청 매핑 템플릿(VTL)"| Lambda2["Lambda 함수"] Lambda1 -->|"statusCode + body 반환"| ProxyResp["API Gateway
응답 변환 없음"] Lambda2 -->|"임의 객체 반환"| StdResp["응답 매핑 템플릿(VTL)"] ProxyResp --> Client StdResp --> Client
- 클라이언트 요청: HTTP 요청이 API Gateway 엔드포인트에 도달한다.
- 프록시 통합 경로: API Gateway가 요청 전체를 고정 포맷 이벤트 객체로 변환해 Lambda를 직접 호출한다. 매핑 템플릿 단계가 없다.
- 표준 통합 경로: 요청 매핑 템플릿(VTL)이 먼저 실행되어 이벤트 구조를 변환한 뒤 Lambda를 호출한다. 응답도 응답 매핑 템플릿을 거친다.
- Lambda 응답 처리: 프록시 통합은 Lambda 반환 객체를 그대로 HTTP 응답으로 변환한다. 표준 통합은 응답 매핑 템플릿이 HTTP 응답을 구성한다.
Lambda 프록시 통합 이벤트 객체 구조
프록시 통합을 활성화하면 Lambda 핸들러가 수신하는 이벤트 객체는 AWS가 정의한 고정 스키마를 따른다. 이 구조를 모르면 event.body를 파싱하지 않고 객체로 접근하려다 런타임 오류가 발생한다.
🔽 프록시 통합 이벤트 객체 전체 예시 (클릭하여 펼치기)
{
"resource": "/users/{userId}",
"path": "/users/42",
"httpMethod": "POST",
"headers": {
"Content-Type": "application/json",
"Authorization": "Bearer eyJ...",
"Host": "abc123.execute-api.us-east-1.amazonaws.com"
},
"multiValueHeaders": {
"Accept": ["text/html", "application/json"]
},
"queryStringParameters": {
"page": "1"
},
"multiValueQueryStringParameters": {
"tag": ["aws", "lambda"]
},
"pathParameters": {
"userId": "42"
},
"stageVariables": {
"env": "prod"
},
"requestContext": {
"resourceId": "abc123",
"resourcePath": "/users/{userId}",
"httpMethod": "POST",
"extendedRequestId": "Xyz123=",
"requestTime": "01/Jan/2024:00:00:00 +0000",
"path": "/prod/users/42",
"accountId": "123456789012",
"protocol": "HTTP/1.1",
"stage": "prod",
"requestId": "abc-def-ghi",
"identity": {
"sourceIp": "203.0.113.10",
"userAgent": "Mozilla/5.0"
},
"domainName": "abc123.execute-api.us-east-1.amazonaws.com",
"apiId": "abc123"
},
"body": "{\"name\": \"Alice\", \"email\": \"alice@example.com\"}",
"isBase64Encoded": false
}
여기서 반드시 짚고 넘어가야 할 점이 있다. body 필드는 문자열이다. JSON 객체가 아니다. 클라이언트가 application/json으로 요청을 보냈더라도, Lambda 핸들러에서는 반드시 JSON.parse(event.body) 또는 json.loads(event.body)로 파싱해야 한다. 이걸 놓치면 event.body.name이 undefined로 나오는 이유를 한참 찾게 된다.
주요 필드 설명
| 필드 | 타입 | 설명 |
|---|---|---|
httpMethod | string | HTTP 메서드 (GET, POST 등) |
path | string | 실제 요청 경로 (경로 파라미터 값 포함) |
resource | string | API Gateway에 정의된 리소스 패턴 |
headers | object | 단일 값 헤더 맵 |
multiValueHeaders | object | 동일 키에 여러 값이 있는 헤더 맵 |
queryStringParameters | object | null | 단일 값 쿼리스트링 맵 |
pathParameters | object | null | 경로 파라미터 맵 |
body | string | null | 요청 바디 (항상 문자열, Base64 인코딩 가능) |
isBase64Encoded | boolean | 바디가 Base64 인코딩된 경우 true |
requestContext | object | API Gateway 컨텍스트 (스테이지, 요청 ID 등) |
stageVariables | object | null | API Gateway 스테이지 변수 |
Lambda 프록시 통합 응답 포맷
프록시 통합에서는 Lambda가 반환하는 객체 구조가 곧 HTTP 응답이 된다. API Gateway가 응답을 변환하지 않기 때문에, Lambda가 잘못된 구조를 반환하면 클라이언트는 502 Bad Gateway를 받는다.
// 올바른 프록시 통합 응답 구조 (Node.js)
exports.handler = async (event) => {
const body = event.body ? JSON.parse(event.body) : {};
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
// body는 반드시 문자열이어야 한다
body: JSON.stringify({
message: 'success',
userId: event.pathParameters?.userId
}),
isBase64Encoded: false
};
};
응답 객체에서 statusCode는 필수 필드다. headers와 body는 선택적이지만, body를 포함할 경우 반드시 문자열이어야 한다. 객체를 그대로 반환하면 API Gateway가 직렬화하지 않고 오류를 낸다.
표준 통합에서 이벤트 객체가 달라지는 이유
표준 통합(비프록시)에서는 Lambda가 수신하는 이벤트 구조를 개발자가 완전히 제어한다. API Gateway 콘솔에서 '통합 요청' 섹션의 매핑 템플릿에 VTL을 작성하면, 그 템플릿의 출력이 Lambda 이벤트 객체가 된다.
## 표준 통합 요청 매핑 템플릿 예시 (VTL)
{
"userId": "$input.params('userId')",
"name": $input.json('$.name'),
"sourceIp": "$context.identity.sourceIp",
"stage": "$context.stage"
}
이 템플릿을 사용하면 Lambda가 받는 이벤트는 다음과 같이 단순해진다:
{
"userId": "42",
"name": "Alice",
"sourceIp": "203.0.113.10",
"stage": "prod"
}
프록시 통합의 거대한 이벤트 객체 대신 Lambda가 필요한 데이터만 정제해서 받는다. 레거시 시스템 연동이나 Lambda 코드를 API Gateway 구조로부터 완전히 분리하고 싶을 때 표준 통합이 유리한 이유다.
body: JSON 객체"] --> APIGW["API Gateway"] APIGW --> ProxyEvt["프록시 통합 이벤트"] APIGW --> StdEvt["표준 통합 이벤트"] ProxyEvt --> P1["event.httpMethod: POST"] ProxyEvt --> P2["event.pathParameters.userId: 42"] ProxyEvt --> P3["event.body: 문자열 (JSON.parse 필요)"] ProxyEvt --> P4["event.headers: 전체 헤더"] ProxyEvt --> P5["event.requestContext: API GW 메타데이터"] StdEvt --> S1["event.userId: 42 (VTL 추출)"] StdEvt --> S2["event.name: Alice (VTL 파싱)"] StdEvt --> S3["개발자 정의 필드만 포함"]
- 프록시 통합 이벤트:
headers,queryStringParameters,pathParameters,body(문자열),requestContext등 HTTP 요청 전체가 포함된 고정 스키마 객체가 전달된다. - 표준 통합 이벤트: VTL 매핑 템플릿이 정의한 필드만 포함된 커스텀 객체가 전달된다. 구조는 개발자가 완전히 결정한다.
- body 처리 차이: 프록시 통합에서는 Lambda가 직접
JSON.parse(event.body)를 수행해야 한다. 표준 통합에서는 VTL의$input.json()이 이미 파싱된 값을 이벤트에 포함시킬 수 있다.
실제 장애 패턴: 잘못된 가정이 만드는 502 오류
프로덕션에서 자주 보이는 패턴이 있다. 로컬 테스트는 잘 되는데 API Gateway를 통하면 502 Bad Gateway가 간헐적으로 발생한다. CloudWatch 로그를 보면 Lambda 자체는 정상 실행됐다. 이 경우 십중팔구 응답 구조 문제다.
Lambda가 다음처럼 반환하면 프록시 통합에서 502가 발생한다:
// 잘못된 응답 — 502 유발
return {
message: 'success',
data: { userId: 42 }
};
// 올바른 응답
return {
statusCode: 200,
body: JSON.stringify({ message: 'success', data: { userId: 42 } })
};
처음에는 Lambda 코드 버그나 타임아웃을 의심하게 된다. 하지만 실제 원인은 API Gateway가 statusCode 필드를 찾지 못해서 응답을 구성할 수 없는 것이다. Lambda 실행 자체는 성공했으니 Lambda 오류 로그는 없고, API Gateway 액세스 로그에서 통합 응답 오류를 확인해야 원인을 찾을 수 있다.
프록시 통합에서 API Gateway는 Lambda 응답을 신뢰한다. Lambda가 올바른 HTTP 응답 구조를 반환할 책임이 완전히 Lambda 코드로 이동한다는 뜻이다. 표준 통합에서는 이 책임이 매핑 템플릿에 있다.
CLI로 통합 방식 확인 및 설정
기존 API의 통합 방식을 확인하거나 변경할 때 AWS CLI를 사용할 수 있다. REST API 기준으로 설명한다.
현재 통합 방식 확인
# 특정 리소스의 메서드 통합 정보 조회
aws apigateway get-integration \
--rest-api-id abc123defg \
--resource-id xyz789 \
--http-method POST \
--region us-east-1
응답의 type 필드가 AWS_PROXY이면 Lambda 프록시 통합, AWS이면 표준 통합이다.
프록시 통합으로 설정
# Lambda 프록시 통합 설정
aws apigateway put-integration \
--rest-api-id abc123defg \
--resource-id xyz789 \
--http-method POST \
--type AWS_PROXY \
--integration-http-method POST \
--uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:MyFunction/invocations \
--region us-east-1
Lambda 호출 권한 부여
통합 방식과 무관하게, API Gateway가 Lambda를 호출하려면 리소스 기반 정책이 필요하다.
aws lambda add-permission \
--function-name MyFunction \
--statement-id apigateway-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn arn:aws:execute-api:us-east-1:123456789012:abc123defg/*/POST/users \
--region us-east-1
언제 어떤 통합 방식을 선택해야 하는가
구조를 알아도 되는가?"} Q1 -->|"예"| Q2{"VTL 작성/유지 리소스가
있는가?"} Q1 -->|"아니오 (포터블 로직 필요)"| Std["표준 통합 선택
(VTL로 이벤트 정제)"] Q2 -->|"아니오 / 빠른 개발 우선"| Proxy["프록시 통합 선택
(AWS_PROXY)"] Q2 -->|"예 + 레거시 스키마 호환 필요"| Std Proxy --> Note1["body는 JSON.parse 필수
응답에 statusCode 필수"] Std --> Note2["요청/응답 매핑 템플릿
모두 작성 필요"]
실무에서 대부분의 신규 프로젝트는 Lambda 프록시 통합을 선택한다. 설정이 단순하고, Lambda 코드 안에서 요청 처리 로직을 모두 제어할 수 있기 때문이다. VTL 매핑 템플릿은 학습 곡선이 있고, 디버깅이 어렵다.
표준 통합이 유리한 경우는 다음과 같다:
- Lambda 코드가 API Gateway 구조를 전혀 몰라야 하는 경우 (포터블한 비즈니스 로직)
- 요청 데이터를 API Gateway 레이어에서 사전 검증하거나 변환해야 하는 경우
- 레거시 Lambda 함수가 특정 이벤트 스키마를 기대하는 경우
Lambda 프록시 통합 관련 IAM 고려사항
통합 방식 자체는 IAM 실행 역할과 직접적인 관계가 없다. 그러나 API Gateway가 Lambda를 호출하는 권한 모델은 두 방식 모두 동일하게 적용된다.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:MyFunction",
"Condition": {
"ArnLike": {
"AWS:SourceArn": "arn:aws:execute-api:us-east-1:123456789012:abc123defg/*/*/*"
}
}
}
]
}
Condition의 AWS:SourceArn을 특정 API와 스테이지로 제한하면 다른 API Gateway 인스턴스에서 동일 Lambda를 호출하는 것을 방지할 수 있다. 최소 권한 원칙 적용 시 와일드카드 *보다 구체적인 ARN을 사용하는 것이 권장된다.
Lambda 프록시 통합 마무리 및 다음 단계
Lambda 프록시 통합은 API Gateway와 Lambda를 연결하는 가장 직관적인 방법이다. 이벤트 객체의 고정 스키마를 이해하고, body가 문자열이라는 점, 응답에 statusCode가 필수라는 점만 정확히 파악하면 대부분의 운영 이슈를 예방할 수 있다.
HTTP API(API Gateway v2)를 사용하는 경우 이벤트 포맷이 REST API와 다르다. HTTP API의 Lambda 통합은 payload format version 1.0과 2.0을 지원하며, 2.0 포맷은 구조가 단순화되어 있다. 새 프로젝트라면 HTTP API와 payload format 2.0 조합도 검토할 가치가 있다.
핵심 용어 정리
| 용어 | 설명 |
|---|---|
| Lambda 프록시 통합 (AWS_PROXY) | API Gateway가 HTTP 요청을 고정 스키마 이벤트 객체로 변환해 Lambda에 전달하는 통합 방식. 매핑 템플릿 불필요. |
| Lambda 표준 통합 (AWS) | VTL 매핑 템플릿으로 요청/응답을 변환하는 통합 방식. 이벤트 구조를 개발자가 정의. |
| VTL (Velocity Template Language) | API Gateway 표준 통합에서 요청/응답 변환에 사용하는 템플릿 언어. |
| isBase64Encoded | 프록시 통합 이벤트/응답에서 바디가 Base64 인코딩되었는지 나타내는 플래그. 바이너리 데이터 처리 시 사용. |
| requestContext | 프록시 통합 이벤트 내 API Gateway 메타데이터 객체. 스테이지, 요청 ID, 인증 컨텍스트 등 포함. |
댓글
댓글 쓰기