'CORS error' 및 일반적인 Access-Control-Allow-Origin 문제 해결 방법: 완벽 가이드
다른 도메인의 API에 요청을 보내는 웹 애플리케이션을 구축해 본 적이 있다면, 거의 확실하게 공포의 "CORS 오류"를 만났을 것입니다. 보통 브라우저 콘솔에 다음과 같이 표시됩니다.
Access to fetch at 'https://api.example.com' from origin 'https://myapp.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
이 가이드에서는 CORS가 정확히 무엇인지, 왜 존재하는지, 그리고 이러한 오류를 완전히 해결하는 방법에 대해 설명합니다.
1. 일반적인 CORS 오류 메시지
주로 브라우저의 개발자 도구(콘솔 또는 네트워크 탭)에서 다음 오류를 볼 수 있습니다.
blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present ...The 'Access-Control-Allow-Origin' header contains multiple values ...Response to preflight request doesn't pass access control check ...Method NOT ALLOWED ...(POST 또는 DELETE와 같은 HTTP 메서드가 CORS에 의해 허용되지 않을 때)
2. CORS란 무엇인가요?
CORS (Cross-Origin Resource Sharing) 는 브라우저에서 구현하는 보안 기능입니다. 악성 웹사이트가 허가 없이 다른 도메인(은행이나 비공개 API 등)으로 요청을 보내는 것을 방지합니다.
기본적으로 브라우저는 **동일 출처 정책(Same-Origin Policy)**을 따릅니다. 즉, a.com의 스크립트는 a.com의 데이터만 요청할 수 있습니다. CORS는 b.com이 명시적으로 허용하는 경우에 한해 a.com이 b.com에서 안전하게 데이터를 요청할 수 있도록 하는 메커니즘입니다.
3. 주요 원인 및 해결 방법
3.1 "Access-Control-Allow-Origin" 헤더 누락
가장 흔한 오류입니다. 데이터를 요청하는 서버가 브라우저에 해당 도메인의 리소스 접근이 허용됨을 알리지 않았음을 의미합니다.
해결 방법:
수정은 반드시 서버 측에서 이루어져야 합니다. 응답에 Access-Control-Allow-Origin 헤더를 추가해야 합니다.
- 공공 API의 경우:
*(모두 허용)로 설정합니다. - 비공개 API의 경우: 특정 도메인(예:
https://myapp.com)으로 설정합니다.
3.2 CORS 사전 검사 (CORS preflight) 실패
"복잡한" 요청(사용자 정의 헤더가 있거나 PUT/DELETE와 같은 메서드를 사용하는 경우)에 대해 브라우저는 먼저 자동 OPTIONS 요청을 보냅니다. 이를 사전 검사(Preflight) 요청이라고 합니다.
오류 내용:
Response to preflight request doesn't pass access control check.
해결 방법:
서버가 OPTIONS 요청을 처리하고 올바른 CORS 헤더와 함께 200 OK 또는 204 No Content 상태를 반환하는지 확인하세요.
Access-Control-Allow-OriginAccess-Control-Allow-Methods(예:GET, POST, OPTIONS, PUT, DELETE)Access-Control-Allow-Headers(예:Content-Type, Authorization)
3.3 "Access-Control-Allow-Credentials"
요청과 함께 쿠키나 인증 헤더를 보내는 경우(withCredentials: true), CORS 규칙이 더 엄격해집니다.
오류 내용:
The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
해결 방법:
Access-Control-Allow-Origin에 와일드카드*를 사용할 수 없습니다. 정확한 출처(Origin)를 지정해야 합니다.Access-Control-Allow-Credentials: true헤더도 추가해야 합니다.
4. 다양한 환경에서 CORS를 해결하는 방법
4.1 Node.js (Express)
가장 쉬운 방법은 cors 미들웨어를 사용하는 것입니다.
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://myapp.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true
}));
4.2 Nginx
서버 또는 위치(location) 블록에 다음 줄을 추가하세요.
add_header 'Access-Control-Allow-Origin' 'https://myapp.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always;
if ($request_method = 'OPTIONS') {
return 204;
}
4.3 Python (Flask)
flask-cors 확장을 사용하세요.
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app, origins=["https://myapp.com"])
5. 자주 묻는 질문 (FAQ)
Q: 프론트엔드(JavaScript)에서 CORS 오류를 수정할 수 있나요?
A: 아니요. CORS는 브라우저에 의해 강제되는 서버 측 보안 정책입니다. JavaScript 코드에서 이를 "우회"할 수 없습니다. 서버를 제어할 수 없는 경우, 대신 데이터를 가져와 줄 **프록시 서버(Proxy Server)**를 사용해야 합니다.
Q: 왜 브라우저에서만 CORS 오류가 발생하고 Postman에서는 발생하지 않나요?
A: Postman은 웹 브라우저가 아닌 독립형 애플리케이션입니다. 동일 출처 정책이나 CORS 규칙을 강제하지 않습니다. 오직 웹 브라우저(Chrome, Firefox 등)만 CORS를 강제합니다.
Q: Access-Control-Allow-Origin을 "*"로 설정하면 어떤 위험이 있나요?
A: 공개 데이터의 경우 위험이 없습니다. 하지만 사용자 데이터를 다루거나 인증이 필요한 API의 경우 *로 설정하면 어떤 웹사이트든 로그인된 사용자를 대신하여 요청을 보낼 수 있게 되어 데이터 도난으로 이어질 수 있습니다.
6. CORS 헤더 요약표
| 헤더 | 설명 |
|---|---|
Access-Control-Allow-Origin |
리소스 접근이 허용된 도메인을 지정합니다. |
Access-Control-Allow-Methods |
허용된 HTTP 메서드(GET, POST 등)를 지정합니다. |
Access-Control-Allow-Headers |
보낼 수 있는 사용자 정의 헤더를 지정합니다. |
Access-Control-Allow-Credentials |
credentials 플래그가 true일 때 응답을 공유할 수 있는지 나타냅니다. |
Access-Control-Max-Age |
사전 검사 요청 결과를 캐시할 수 있는 시간을 지정합니다. |
관련 오류
- JSON의 'Unexpected token' 오류 해결 방법
- JavaScript에서 'URIError: URI malformed' 해결 방법
- 'JWT expired' 및 일반적인 JWT 오류 해결 방법