🚫 CORS 정책 완벽 이해하기: 교차 출처 요청 오류 해결법
🌐 웹 개발자가 꼭 알아야 할 CORS 정책과 헤더 설정 노하우
웹 개발을 하다 보면 API 호출 시 갑자기 등장하는 CORS 오류 메시지에 당황한 경험, 한 번쯤 있으셨을 거예요.
“Access to fetch at ‘URL’ from origin ‘도메인’ has been blocked by CORS policy”라는 에러 문구는 클라이언트와 서버의 도메인이 다를 때, 브라우저가 보안을 위해 요청을 차단하면서 발생합니다.
이럴 땐 코드가 틀린 게 아니라, 서버에서 CORS 정책에 맞게 허용 헤더를 설정하지 않았기 때문일 가능성이 높습니다.
단순한 개념 같지만, 보안과 직결된 부분이기 때문에 정확하게 이해하고 상황에 맞는 설정이 무엇보다 중요하답니다.
이번 글에서는 그런 CORS 정책의 원리부터, 오류를 방지하는 서버 측 헤더 설정 방법까지 차근차근 설명드릴게요.
CORS는 단순한 웹 설정 이상의 의미를 갖습니다.
웹 보안, API 설계, 서버 운영에 이르기까지 그 범위가 꽤 넓고, 실무에선 반드시 숙지해야 할 핵심 개념이죠.
특히 프론트엔드와 백엔드가 분리된 구조에서는 CORS 정책 설정이 잘못되면 기능 자체가 작동하지 않을 수 있어 필수적으로 다뤄야 합니다.
이 글에서는 CORS 정책이란 무엇인지부터, 실제로 어떤 오류를 일으키는지, 그리고 이를 해결하려면 서버 측에서 어떤 설정을 해야 하는지를 예제와 함께 설명해드릴게요.
📋 목차
🔍 CORS란 무엇인가요?
CORS(Cross-Origin Resource Sharing)는 ‘교차 출처 리소스 공유’라는 의미로, 브라우저에서 보안을 강화하기 위해 도입된 HTTP 기반의 정책입니다.
웹 페이지에서 자신이 로드된 도메인(출처)이 아닌 다른 도메인으로 요청을 보낼 경우, 브라우저는 보안상 이를 제한하게 되는데, 이때 사용되는 제어 장치가 바로 CORS입니다.
기본적으로 브라우저는 동일 출처 정책(Same-Origin Policy)을 따릅니다.
이는 클라이언트에서 로드된 웹 페이지가 다른 도메인의 리소스에 무분별하게 접근하지 못하게 막는 보안 메커니즘이죠.
하지만 실무에서는 프론트엔드와 백엔드가 서로 다른 도메인, 포트, 혹은 프로토콜을 사용하는 경우가 많기 때문에 이 제한이 문제가 될 수 있습니다.
이러한 상황에서 서버가 브라우저에게 ‘특정 출처의 요청은 허용한다’는 정보를 명시적으로 알려주는 역할을 하는 것이 바로 CORS입니다.
즉, 클라이언트가 아닌 서버 측에서 헤더를 통해 허용 도메인, 메서드, 헤더 등을 설정함으로써 교차 출처 요청을 안전하게 허용할 수 있게 됩니다.
💬 CORS는 브라우저 보안 정책의 일환으로, 백엔드 서버에서 명시적으로 허용한 출처만 외부 요청을 받을 수 있게 만들어줍니다.
예를 들어, https://frontend.com에서 https://api.server.com으로 API를 호출하는 경우, 서버 측에서 아래와 같은 헤더를 포함하지 않으면 브라우저는 요청을 차단하게 됩니다.
Access-Control-Allow-Origin: https://frontend.com
이처럼 CORS는 보안을 유지하면서도 다양한 출처 간 통신을 가능하게 하는 필수 기술입니다.
하지만 잘못된 설정은 오히려 보안 취약점을 초래할 수 있으니, 설정 시 주의가 필요합니다.
🔐 SOP와 CORS의 관계
웹 보안의 근간이 되는 정책 중 하나가 바로 SOP(Same-Origin Policy, 동일 출처 정책)입니다.
SOP는 웹 브라우저에서 로드된 리소스가 자신과 동일한 출처(origin)에서만 데이터를 주고받을 수 있도록 제한하는 보안 메커니즘입니다.
즉, 도메인, 프로토콜, 포트 중 하나라도 다르면 다른 출처로 간주되어 접근이 차단됩니다.
예를 들어 https://mydomain.com에서 로드된 웹 페이지는 기본적으로 https://api.otherdomain.com에 데이터를 요청할 수 없습니다.
이러한 보안 규칙은 사용자의 세션 쿠키나 토큰이 외부에 유출되는 것을 방지하기 위한 필수적인 장치입니다.
🔄 CORS는 SOP를 보완하기 위한 수단
하지만 최근 웹 개발 환경에서는 프론트엔드와 백엔드 서버가 분리된 구조가 일반화되었습니다.
따라서 서로 다른 출처 간 통신이 불가피하게 되었고, 이러한 흐름을 반영하여 SOP의 제한을 안전하게 완화할 수 있는 방법으로 CORS가 도입된 것입니다.
CORS는 SOP의 기본 보안 원칙은 유지하면서, 서버가 의도적으로 허용한 요청만 받아들이게 해 줍니다.
즉, 서버가 클라이언트에 “너는 내가 허락한 출처니까 요청을 허용할게”라는 신호를 보내는 방식입니다.
💬 SOP가 막고, CORS가 풀어주는 구조라고 보면 이해가 쉽습니다. 단, 모든 요청이 허용되는 건 아니며 서버의 명시적 설정이 필요합니다.
결국 CORS는 SOP의 보안 기준을 훼손하지 않으면서도 현대적인 웹 구조에 맞춰 필요한 데이터 통신을 가능하게 하는 절충안이라 할 수 있습니다.
단순히 브라우저의 오류 메시지를 없애는 게 아니라, 보안과 개발 편의성 사이의 균형을 맞춰주는 기술인 셈이죠.
⚠️ CORS 오류가 발생하는 원인
CORS 관련 오류는 프론트엔드 개발 중 API 요청을 보낼 때 자주 발생합니다.
브라우저 콘솔에 아래와 같은 경고 메시지를 본 적 있으신가요?
Access to XMLHttpRequest at 'https://api.example.com/data'
from origin 'https://myfrontend.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
이 오류는 서버가 클라이언트의 출처(origin)를 허용하지 않았기 때문에 발생합니다.
즉, 브라우저가 보안 상의 이유로 해당 요청을 차단한 것이며, 클라이언트 측에서 해결할 수 있는 문제가 아닙니다.
🚫 서버 설정 누락 또는 오인
CORS 오류는 대부분 서버의 HTTP 응답에 CORS 관련 헤더가 누락되었을 때 발생합니다.
아래는 자주 발생하는 설정 누락 사례입니다.
- ❌Access-Control-Allow-Origin 헤더가 아예 없음
- 🔁동일 출처만 허용(
Access-Control-Allow-Origin: same-origin)으로 설정됨 - ⚠️클라이언트가
Authorization또는Content-Type: application/json등을 포함했지만 서버가 Access-Control-Allow-Headers에 이를 명시하지 않음
또한 credentials 옵션(cookie 포함 요청)을 사용하는 경우에는 서버 설정이 더 복잡해집니다.
이때는 Access-Control-Allow-Origin에 *를 사용할 수 없고, 명시적으로 특정 출처를 지정해야만 오류 없이 동작합니다.
⚠️ 주의: 단순한 GET 요청이라도 클라이언트가 CORS 정책에 맞지 않는 헤더를 포함할 경우, 브라우저는 자동으로 preflight OPTIONS 요청을 보내며 이 과정에서 오류가 발생할 수 있습니다.
즉, CORS 오류는 대부분 서버 측 헤더 설정의 문제이며, 클라이언트 코드를 아무리 수정해도 해결되지 않는다는 점을 명심해야 합니다.
🛠️ 서버 측에서 해결하는 방법
CORS 오류는 브라우저에서 발생하지만, 그 해결 방법은 오직 서버의 응답 헤더 설정에 달려 있습니다.
서버가 클라이언트의 출처를 허용하도록 명시해야만, 브라우저가 요청을 통과시켜주기 때문입니다.
서버 언어나 프레임워크에 따라 설정 방식은 다르지만, 기본적으로 아래 3가지 헤더는 꼭 설정해야 합니다.
- 🌍Access-Control-Allow-Origin: 허용할 출처(origin)
- 📦Access-Control-Allow-Headers: 허용할 요청 헤더들
- 🧭Access-Control-Allow-Methods: 허용할 HTTP 메서드(GET, POST 등)
🚀 서버 언어별 CORS 설정 예시
각 서버 언어에서 CORS를 설정하는 방법은 다음과 같습니다.
💡 TIP: 개발 중이라면 *로 모든 출처를 허용할 수 있지만, 실제 서비스 환경에서는 보안상 반드시 특정 도메인만 지정하는 것이 좋습니다.
// Node.js (Express)
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'https://myfrontend.com',
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// PHP
header("Access-Control-Allow-Origin: https://myfrontend.com");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Allow-Methods: GET, POST");
서버 설정이 완료되면, 브라우저는 클라이언트의 요청을 허용하게 되며, 더 이상 CORS 오류 메시지가 발생하지 않습니다.
이처럼 클라이언트가 아닌 서버의 헤더 설정이 핵심이라는 점을 잊지 마세요.
💡 개발 시 주의할 점과 팁
CORS 오류는 초보자뿐 아니라 숙련된 개발자에게도 종종 혼란을 줍니다.
그만큼 조건이 까다롭고, 상황에 따라 해결 방식도 달라지기 때문입니다.
아래 팁들을 참고하면 CORS 이슈를 보다 빠르고 안전하게 처리할 수 있습니다.
🧩 Access-Control-Allow-Origin: *
모든 출처를 허용하는 * 설정은 개발 초기에는 편리하지만, 보안상 권장되지 않습니다.
특히 인증 정보를 포함하는 요청(fetch with credentials)에는 사용할 수 없으며, 특정 출처를 명확히 지정해야 합니다.
⚠️ 주의: credentials: true를 사용할 경우 Access-Control-Allow-Origin은 절대 *가 되어선 안 됩니다.
🛡️ OPTIONS Preflight 요청 이해하기
브라우저는 비표준 헤더나 메서드가 포함된 요청에 대해 먼저 OPTIONS 요청을 보냅니다.
이걸 preflight 요청이라고 하며, 이 응답에 필요한 CORS 헤더가 포함되지 않으면 본 요청은 차단됩니다.
서버에서는 OPTIONS 요청도 처리하도록 라우팅하거나 자동 응답 설정을 해주어야 합니다.
이 점을 놓치면 POST나 PUT 요청이 작동하지 않는 문제에 직면하게 됩니다.
🔄 캐시 이슈와의 연계
CORS는 브라우저 캐시나 프록시 서버 설정과도 밀접하게 연관됩니다.
CORS 관련 헤더가 변경되었음에도 오류가 계속될 경우, 캐시가 문제일 수 있습니다.
개발 중이라면 캐시를 비우거나 헤더에 Cache-Control: no-cache를 적용하는 것도 좋은 방법입니다.
💎 핵심 포인트:
CORS는 단순히 오류만 잡는 작업이 아니라, 웹 서비스의 구조와 보안을 조율하는 중요한 설정입니다. 실무에서 자주 발생하는 만큼, 원리부터 정확히 이해하는 것이 가장 빠른 해결책입니다.
❓ 자주 묻는 질문 (FAQ)
CORS 오류는 무조건 서버에서만 해결해야 하나요?
localhost 간 요청도 CORS 오류가 발생할 수 있나요?
서버에 Access-Control-Allow-Origin: * 설정하면 끝인가요?
OPTIONS preflight 요청을 꼭 처리해야 하나요?
브라우저에 따라 CORS 정책이 다를 수 있나요?
프록시 서버를 이용하면 CORS 우회가 가능한가요?
CORS 오류가 캐시로 인해 계속 발생할 수 있나요?
프론트엔드 프레임워크에서 CORS 해결 방법이 따로 있나요?
🧠 CORS 문제 해결을 위한 핵심 요약
CORS(Cross-Origin Resource Sharing)는 웹 보안을 위한 브라우저 정책이지만, 프론트엔드와 백엔드가 분리된 현대 웹 구조에서는 필수적으로 이해해야 할 개념입니다.
서로 다른 출처 간의 API 요청을 허용하려면 반드시 서버에서 Access-Control-Allow-Origin 등 관련 HTTP 응답 헤더를 명시적으로 설정해야 합니다.
SOP(동일 출처 정책)와 CORS는 보완 관계이며, 이를 통해 보안을 해치지 않으면서 유연한 데이터 통신이 가능해집니다.
개발 중에는 * 와 같은 전역 허용이 편리할 수 있지만, 실제 서비스에서는 반드시 특정 출처를 명시하여 보안을 강화해야 합니다.
OPTIONS preflight 요청, 인증 포함 요청, 커스텀 헤더 사용 등 CORS와 연관된 다양한 요소들을 함께 고려해야 하며, 클라이언트 측 코드로는 해결이 어렵다는 점을 기억하세요.
실제 서버 환경에 맞는 설정과 테스트를 통해 정확하게 대응하는 것이 CORS 오류를 방지하는 가장 확실한 방법입니다.
🏷️ 관련 태그 : CORS정책, 교차출처요청, 웹보안, Access-Control, preflight, SOP, 서버헤더설정, 프론트엔드백엔드, API보안, 웹개발팁