cors debugging web-development javascript security api

'CORS error' 및 일반적인 Access-Control-Allow-Origin 문제 해결 방법

'blocked by CORS policy', 'Access-Control-Allow-Origin 헤더 누락', 'CORS preflight' 실패와 같은 CORS 오류를 수정하기 위한 종합 가이드입니다. 서버를 올바르게 구성하는 방법을 알아보세요.

2026-04-11

'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.comb.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-Origin
  • Access-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'.

해결 방법:

  1. Access-Control-Allow-Origin에 와일드카드 *를 사용할 수 없습니다. 정확한 출처(Origin)를 지정해야 합니다.
  2. 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 오류 해결 방법