API Gateway CORS 오류 완전 해결 가이드: 콘솔 설정부터 Lambda 응답 헤더까지
프론트엔드에서 API Gateway를 호출했을 때 CORS 오류가 발생하면, 대부분의 엔지니어는 콘솔에서 'Enable CORS' 버튼 하나만 누르면 해결된다고 생각한다. 실제로는 그 버튼이 절반의 작업만 처리하고, 나머지 절반은 Lambda 함수 응답에서 직접 헤더를 반환해야 한다 — 이 사실을 모르면 콘솔 설정 후에도 동일한 오류가 반복된다.
TL;DR — API Gateway CORS 오류 핵심 요약
| 구분 | 내용 |
|---|---|
| 문제 원인 | 브라우저 preflight OPTIONS 요청에 CORS 헤더 누락, 또는 실제 응답에 헤더 없음 |
| 콘솔 설정 역할 | OPTIONS 메서드 Mock 응답에 CORS 헤더 추가 + 배포 필요 |
| Lambda 역할 | GET/POST 등 실제 메서드 응답에 직접 CORS 헤더 포함해야 함 |
| 필수 헤더 | Access-Control-Allow-Origin, Access-Control-Allow-Headers, Access-Control-Allow-Methods |
| 자주 놓치는 것 | 콘솔 설정 후 Stage 재배포 누락 |
CORS가 API Gateway에서 동작하는 방식
브라우저는 크로스 오리진 요청을 보내기 전에 먼저 preflight 요청을 OPTIONS 메서드로 전송한다. 서버가 이 OPTIONS 요청에 올바른 CORS 헤더로 응답해야 브라우저가 실제 요청을 허용한다. API Gateway에서 이 흐름을 이해하지 못하면 설정이 반쪽짜리가 된다.
API Gateway REST API에서 CORS는 두 개의 독립된 레이어에서 처리된다. 첫 번째는 OPTIONS preflight — API Gateway가 Lambda를 호출하지 않고 Mock 통합으로 직접 응답한다. 두 번째는 실제 메서드(GET, POST 등) 응답 — Lambda 함수가 응답 본문과 함께 CORS 헤더를 직접 반환해야 한다. 콘솔의 'Enable CORS'는 첫 번째 레이어만 구성한다.
- 브라우저 → OPTIONS 요청: 브라우저가 실제 요청 전 preflight를 전송한다.
- API Gateway Mock 응답: Lambda 호출 없이 API Gateway가 직접 CORS 헤더를 반환한다. 콘솔 'Enable CORS'가 이 부분을 설정한다.
- 브라우저 → 실제 요청: preflight 통과 후 GET/POST 등 실제 요청을 전송한다.
- Lambda 응답 헤더 필수: 실제 메서드 응답에도 CORS 헤더가 없으면 브라우저가 응답을 차단한다.
API Gateway CORS 오류 진단 — 어느 레이어가 문제인가
브라우저 DevTools Network 탭에서 OPTIONS 요청을 먼저 확인한다. OPTIONS가 200이 아니거나 응답 헤더에 Access-Control-Allow-Origin이 없으면 콘솔 설정 문제다. OPTIONS는 정상인데 실제 요청(GET/POST)에서 CORS 오류가 나면 Lambda 응답 헤더 문제다.
OPTIONS 요청 확인"} B --> C{"OPTIONS 상태 코드?"} C -->|"200 아님"| D["콘솔 Enable CORS 재적용
+ Stage 재배포"] C -->|"200"| E{"OPTIONS 응답에
CORS 헤더 있음?"} E -->|"없음"| D E -->|"있음"| F{"실제 GET/POST 응답에
CORS 헤더 있음?"} F -->|"없음"| G["Lambda 응답에
CORS 헤더 추가"] F -->|"있음"| H{"인증 요청인가?"} H -->|"Yes"| I["Allow-Origin에 *
사용 불가 — 특정 오리진 명시"] H -->|"No"| J["Lambda 호출 권한 확인
5xx 오류 여부 점검"]
- OPTIONS 응답 확인: 상태 코드 200 여부와 CORS 헤더 존재 여부로 콘솔 설정 문제를 분리한다.
- 실제 메서드 응답 확인: OPTIONS는 정상인데 GET/POST에서 오류가 나면 Lambda 코드 수정이 필요하다.
- 배포 여부 확인: 콘솔 설정 후 Stage 재배포를 하지 않으면 변경사항이 적용되지 않는다.
Step 1: 콘솔에서 CORS 활성화 (REST API)
콘솔의 'Enable CORS'는 OPTIONS 메서드를 Mock 통합으로 생성하고, 해당 메서드 응답에 CORS 헤더를 추가한다. 이 설정만으로는 GET/POST 응답의 CORS 헤더가 처리되지 않는다는 점을 명심해야 한다.
콘솔 절차:
- API Gateway 콘솔 → 해당 REST API 선택
- Resources 패널에서 CORS를 적용할 리소스(경로) 선택
- 상단 메뉴 Actions → Enable CORS 클릭
- 설정 화면에서
Access-Control-Allow-Origin값 입력 (예:'*'또는 특정 오리진) - Enable CORS and replace existing CORS headers 클릭
- 확인 다이얼로그에서 Yes, replace existing values 클릭
- Actions → Deploy API로 변경사항을 Stage에 배포
배포를 빠뜨리는 경우가 생각보다 많다. 콘솔에서 설정을 완료해도 Stage에 배포하지 않으면 기존 설정이 그대로 서비스된다. 설정 변경 후 반드시 배포 단계를 확인한다.
CLI로 현재 OPTIONS 메서드 응답 헤더를 확인하는 방법:
aws apigateway get-method-response \
--rest-api-id YOUR_API_ID \
--resource-id YOUR_RESOURCE_ID \
--http-method OPTIONS \
--status-code 200 \
--region us-east-1
응답의 responseParameters에 method.response.header.Access-Control-Allow-Origin 등이 포함되어 있어야 한다.
배포 상태 확인:
aws apigateway get-deployments \
--rest-api-id YOUR_API_ID \
--region us-east-1
Step 2: Lambda 함수 응답에 CORS 헤더 포함
OPTIONS preflight가 정상이어도, 실제 GET/POST 응답에 CORS 헤더가 없으면 브라우저는 응답을 차단한다. Lambda 프록시 통합(Lambda Proxy Integration)을 사용하는 경우 — 대부분의 현대적 API 구성이 이에 해당한다 — Lambda 함수가 응답 객체에 직접 헤더를 포함해야 한다.
Lambda 프록시 통합에서 응답 형식은 다음 구조를 따라야 한다:
🔽 Python Lambda 응답 예시 (클릭하여 펼치기)
import json
def lambda_handler(event, context):
return {
'statusCode': 200,
'headers': {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
'Access-Control-Allow-Methods': 'GET,POST,OPTIONS'
},
'body': json.dumps({'message': 'success'})
}
🔽 Node.js Lambda 응답 예시 (클릭하여 펼치기)
exports.handler = async (event) => {
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
'Access-Control-Allow-Methods': 'GET,POST,OPTIONS'
},
body: JSON.stringify({ message: 'success' })
};
};
필수 CORS 헤더 설명:
| 헤더 | 역할 | 일반적인 값 |
|---|---|---|
| Access-Control-Allow-Origin | 허용할 오리진 지정 | * 또는 https://example.com |
| Access-Control-Allow-Headers | 허용할 요청 헤더 목록 | Content-Type, Authorization 등 |
| Access-Control-Allow-Methods | 허용할 HTTP 메서드 목록 | GET, POST, OPTIONS 등 |
인증이 포함된 요청(쿠키, Authorization 헤더)을 사용하는 경우 Access-Control-Allow-Origin에 *를 사용할 수 없다. 이 경우 특정 오리진을 명시해야 하며, Access-Control-Allow-Credentials: 'true' 헤더도 함께 반환해야 한다.
실제 운영에서 마주친 패턴 — 증상, 오진, 실제 원인
콘솔에서 'Enable CORS'를 적용하고 배포까지 완료했는데도 프론트엔드에서 여전히 CORS 오류가 발생하는 상황이 있었다. 브라우저 콘솔 메시지는 Access-Control-Allow-Origin header is missing이었고, OPTIONS 요청은 200을 반환하고 있었다.
처음에는 콘솔 설정이 잘못됐다고 판단해 'Enable CORS'를 반복해서 재적용했다. OPTIONS 응답은 계속 정상이었다. 실제 원인은 Lambda 함수 응답에 CORS 헤더가 없었던 것이다 — Lambda 프록시 통합에서는 OPTIONS preflight와 실제 메서드 응답이 완전히 분리된 경로를 타기 때문이다.
Lambda 함수 응답 헤더를 직접 확인하는 방법:
aws lambda invoke \
--function-name YOUR_FUNCTION_NAME \
--payload '{"httpMethod": "GET", "path": "/your-path", "headers": {}, "queryStringParameters": null, "body": null}' \
--cli-binary-format raw-in-base64-out \
--region us-east-1 \
response.json && cat response.json
출력된 JSON에서 headers 필드에 Access-Control-Allow-Origin이 포함되어 있는지 확인한다. 없으면 Lambda 코드를 수정해야 한다.
이 패턴이 반복되는 이유는 'Enable CORS' 버튼의 이름이 오해를 유발하기 때문이다. 버튼이 하는 일은 OPTIONS Mock 응답 구성이지, API 전체의 CORS 활성화가 아니다.
HTTP API (v2)에서의 CORS 설정
REST API가 아닌 HTTP API(API Gateway v2)를 사용하는 경우 CORS 설정 방식이 다르다. HTTP API는 API 레벨에서 CORS를 구성하며, 이 경우 API Gateway가 OPTIONS 응답과 실제 응답 모두에 CORS 헤더를 자동으로 추가한다.
HTTP API CORS 설정 CLI:
aws apigatewayv2 update-api \
--api-id YOUR_HTTP_API_ID \
--cors-configuration AllowOrigins='["https://example.com"]',AllowMethods='["GET","POST","OPTIONS"]',AllowHeaders='["Content-Type","Authorization"]' \
--region us-east-1
HTTP API에서 API 레벨 CORS를 구성하면 Lambda 응답에 별도로 CORS 헤더를 추가할 필요가 없다. 단, Lambda 응답에 CORS 헤더를 직접 포함하면 헤더가 중복될 수 있으므로 둘 중 하나만 사용해야 한다.
현재 HTTP API의 CORS 설정 확인:
aws apigatewayv2 get-api \
--api-id YOUR_HTTP_API_ID \
--region us-east-1 \
--query 'CorsConfiguration'
IAM 권한 — Lambda 호출에 필요한 최소 권한
API Gateway가 Lambda를 호출하려면 Lambda 리소스 기반 정책에 API Gateway의 호출 권한이 있어야 한다. CORS 오류와 직접 관련은 없지만, 권한 문제로 Lambda 호출 자체가 실패하면 CORS 헤더 없는 5xx 응답이 반환되어 CORS 오류처럼 보일 수 있다.
aws lambda add-permission \
--function-name YOUR_FUNCTION_NAME \
--statement-id apigateway-invoke \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn 'arn:aws:execute-api:us-east-1:123456789012:YOUR_API_ID/*/GET/your-path' \
--region us-east-1
API Gateway CORS 오류 해결 체크리스트
- 브라우저 DevTools에서 OPTIONS 요청 상태 코드와 응답 헤더 확인
- OPTIONS가 실패하면 → 콘솔 'Enable CORS' 재적용 후 Stage 재배포
- OPTIONS는 성공인데 실제 요청에서 오류 → Lambda 응답 헤더 확인 및 추가
- 인증 요청이면 →
Access-Control-Allow-Origin에 특정 오리진 명시,*사용 불가 - HTTP API 사용 중이면 → API 레벨 CORS 설정 확인, Lambda 헤더 중복 여부 점검
- Lambda 호출 권한 확인 → 5xx 응답이 CORS 오류처럼 보이는 경우 배제
마무리 및 다음 단계 — API Gateway CORS 오류 완전 해결
API Gateway CORS 오류는 두 레이어를 동시에 처리해야 완전히 해결된다. OPTIONS preflight는 콘솔 설정으로, 실제 메서드 응답 헤더는 Lambda 코드로 각각 처리한다. HTTP API를 사용 중이라면 API 레벨 CORS 설정으로 두 레이어를 한 번에 처리할 수 있다.
추가로 확인할 공식 문서:
핵심 용어 정리
| 용어 | 설명 |
|---|---|
| CORS (Cross-Origin Resource Sharing) | 브라우저가 다른 오리진의 리소스 접근을 제어하는 보안 메커니즘 |
| Preflight 요청 | 브라우저가 실제 요청 전 OPTIONS 메서드로 서버 허용 여부를 확인하는 사전 요청 |
| Lambda 프록시 통합 | API Gateway가 요청 전체를 Lambda에 전달하고, Lambda가 응답 객체 전체를 직접 구성하는 통합 방식 |
| Mock 통합 | Lambda 호출 없이 API Gateway가 직접 응답을 반환하는 통합 방식. OPTIONS preflight에 사용 |
| HTTP API (v2) | REST API보다 단순하고 저렴한 API Gateway 버전. API 레벨 CORS 설정 지원 |
댓글
댓글 쓰기