
웹 개발을 하다 보면 흔히 마주치는 에러 중 하나가 바로 CORS 오류입니다.
“Access to fetch at 'http://example.com/api' from origin 'http://localhost:3000' has been blocked by CORS policy...”
CORS를 이해하려면 먼저 동일 출처 정책(Same-Origin Policy)을 알아야 합니다. 이는 웹 브라우저의 핵심 보안 기능 중 하나입니다.
출처는 다음 세 가지 요소로 구성됩니다:
https://example.com:443/page를 기준으로 살펴보겠습니다:
| URL동일 | 출처 | 여부이유 |
|---|---|---|
| https://example.com:443/api | ✅ 동일 | 모든 요소 일치 |
| https://example.com/api | ✅ 동일 | HTTPS 기본 포트는 443 |
| http://example.com/api | ❌ 다른 출처 | 프로토콜 다름 (http vs https) |
| https://api.example.com/data | ❌ 다른 출처 | 도메인 다름 |
| https://example.com:8080/api | ❌ 다른 출처 | 포트 다름 |
하지만 현대 웹 애플리케이션은 점점 복잡해지고 있습니다:
이처럼 마이크로서비스 아키텍처와 SPA(Single Page Application)가 보편화되면서, 서로 다른 출처 간의 통신이 필수가 되었습니다. CORS는 이런 필요를 안전하게 충족시키기 위해 등장했습니다.
CORS는 브라우저가 출처가 다른 서버에 요청을 허용할 수 있도록 서버에서 명시적으로 허락해주는 메커니즘입니다.
즉, 백엔드에서 헤더를 통해 "이 도메인의 요청은 괜찮아!"라고 알려주는 방식입니다.
모든 CORS 요청이 바로 API로 가는 건 아닙니다.
"안전하지 않은 요청"(예: Content-Type: application/json, PUT, DELETE 등)이 오면
브라우저는 본 요청 전에 먼저 OPTIONS 메서드로 사전 요청(Preflight)을 보냅니다.
OPTIONS /api/resource HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type
이 요청에 대한 서버 응답은 대략 아래와 같습니다.
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type
이건 CORS의 핵심 키입니다.
Access-Control-Allow-Origin: http://localhost:3000
이 헤더는 "어떤 출처(origin)의 요청을 허용할 것인지" 명시합니다.
특정 도메인만 허용: http://localhost:3000
모든 도메인 허용: * (주의: 인증정보 포함 요청은 사용 불가)
서버가 허용하는 HTTP 메서드 목록입니다.
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Preflight 응답에 포함되어야 합니다.
허용할 요청 헤더를 지정합니다.
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
쿠키나 인증 정보를 포함한 요청을 허용할지 지정합니다.
Access-Control-Allow-Credentials: true
Preflight 응답을 캐시할 시간을 초 단위로 지정합니다.
Access-Control-Max-Age: 86400 # 24시간
개념 설명