웹 개발을 하다 보면 자주 마주치는 문제 중 하나가 바로 CORS 에러다. 이 글에서는 CORS가 무엇인지, 왜 필요한지, 그리고 실제로 어떻게 작동하는지를 쉽게 정리한다.

CORS는 교차 출처 리소스 공유를 의미한다. 말 그대로 출처가 다른 서버 간 리소스를 공유할 수 있도록 허용하는 보안 메커니즘이다.
❗ 출처(origin)는 프로토콜 + 도메인 + 포트의 조합이다.
예를 들어, 다음 두 출처는 서로 다르다:
https://example.com (443 포트)http://example.com (80 포트)이처럼 서로 다른 출처 간에 요청이 발생하면 브라우저는 보안 상의 이유로 요청을 차단한다. 이 제한을 풀어주는 것이 CORS다.
과거에는 CSRF(Cross Site Request Forgery) 공격이 자주 발생했다. 이는 사용자의 브라우저를 이용해 의도치 않은 요청을 다른 서비스로 전송하게 만드는 방식이다.
이러한 공격을 막기 위해 브라우저는 SOP(Same-Origin Policy, 동일 출처 정책)을 도입했다. 이는 동일한 출처에서만 요청을 허용하는 정책이다.
하지만, 현대 웹 환경에서는 프론트엔드와 백엔드가 서로 다른 도메인이나 포트에서 동작하는 경우가 많기 때문에 SOP만으로는 부족하다. 그래서 이를 보완하기 위해 CORS가 사용된다.
브라우저는 요청을 보낼 때 Origin 헤더를 자동으로 포함한다. 서버는 Access-Control-Allow-Origin 응답 헤더를 통해 이 요청을 허용할지를 판단한다.
GET /api/data HTTP/1.1
Origin: http://localhost:3000
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:3000
만약 응답에 해당 헤더가 없다면 브라우저는 응답을 차단하고 CORS 오류를 발생시킨다.
단순 요청으로 분류되기 위해서는 다음 조건을 만족해야 한다:
GET, POST, HEADapplication/x-www-form-urlencoded, multipart/form-data, text/plain이 경우 브라우저는 별도의 사전 요청 없이 바로 요청을 전송한다.
Simple Request 조건을 만족하지 않는 경우, 브라우저는 실제 요청 전에 사전 요청(OPTIONS)을 보낸다. 이를 Preflight Request라고 한다.
OPTIONS /api/data HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
서버는 다음과 같이 응답해야 한다:
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
쿠키나 인증 토큰 같은 민감한 정보를 포함하는 요청의 경우 자격 요청(Credential Request)이 발생한다.
withCredentials: true로 설정한다.Access-Control-Allow-Credentials: true를 설정해야 하며, Access-Control-Allow-Origin에 와일드카드는 사용할 수 없다.Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Credentials: true
axios.get('https://api.example.com/user', {
withCredentials: true
});
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));
| 요청 유형 | 설명 | 조건 |
|---|---|---|
| Simple Request | 바로 요청, 사전 요청 없음 | 메서드/헤더 제한 있음 |
| Preflight Request | OPTIONS로 사전 요청 | 커스텀 헤더나 메서드 사용 시 |
| Credential Request | 인증 정보 포함, 보안 강화 필요 | Allow-Credentials: true 필요 |
CORS는 웹 보안을 지키면서도 유연한 리소스 공유를 가능하게 해주는 중요한 기능이다. 백엔드 개발자는 서버에서 적절한 CORS 헤더 설정을 통해 클라이언트가 다른 출처에서 안전하게 리소스를 요청할 수 있도록 해야 한다.
브라우저가 요청을 막는다고 해도, 서버가 명확한 허용 정책을 지정하면 원활하게 통신할 수 있다.