CORS(Cross-Origin Resource Sharing)는 브라우저가 다른 출처(origin)의 리소스에 자바스크립트로 접근할 수 있도록 서버가 명시적으로 허용하는 표준 메커니즘입니다. 이걸 이해하려면 먼저 동일 출처 정책(Same-Origin Policy)을 알아야 합니다.
동일 출처 정책
출처는 스킴 + 호스트 + 포트의 조합입니다. 예를 들어 https://app.example.com과 https://api.example.com은 호스트가 달라 서로 다른 출처이고, http://와 https://도, 포트가 다른 것도 모두 다른 출처입니다. 브라우저는 보안을 위해 스크립트가 자신의 출처와 다른 출처의 응답을 읽는 것을 기본적으로 차단합니다. 이것이 동일 출처 정책이며, 사용자의 쿠키를 악용한 공격을 막는 핵심 방어선입니다.
CORS는 이 정책에 '구멍'을 내는 게 아니라, 서버가 특정 출처의 접근을 허용한다고 응답 헤더로 선언하게 해서 안전하게 교차 출처 통신을 여는 방식입니다. 중요한 점: CORS는 브라우저가 강제합니다. curl이나 서버 간 통신에는 CORS가 적용되지 않습니다.
단순 요청 vs 프리플라이트 요청
브라우저는 교차 출처 요청을 두 가지로 나눕니다. 단순 요청(simple request)은 GET/HEAD/POST 메서드에, 헤더가 제한된 안전한 집합(예: Content-Type이 text/plain·application/x-www-form-urlencoded·multipart/form-data)일 때입니다. 이 경우 브라우저는 바로 요청을 보내고, 응답의 Access-Control-Allow-Origin 헤더를 확인해 스크립트가 읽어도 될지 판단합니다.
그 밖의 요청(예: Content-Type: application/json을 쓰거나, PUT/DELETE, Authorization 외 커스텀 헤더 사용)은 프리플라이트(preflight) 대상입니다. 실제 요청을 보내기 전에 브라우저가 먼저 OPTIONS 요청을 보내 '이런 요청을 보내도 되냐'고 서버에 묻습니다.
OPTIONS /v1/users HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
서버가 이 프리플라이트에 허용 헤더로 응답하면 브라우저는 그제야 실제 PUT 요청을 보냅니다. Access-Control-Max-Age는 이 허용 결과를 캐시할 시간이라, 매 요청마다 프리플라이트가 반복되는 것을 줄여 줍니다.
자격 증명(credentials)
쿠키나 Authorization 헤더 같은 자격 증명을 교차 출처 요청에 포함하려면 양쪽 모두 동의해야 합니다. 클라이언트는 fetch(url, { credentials: 'include' })로 요청하고, 서버는 응답에 Access-Control-Allow-Credentials: true를 실어야 합니다. 중요한 제약: 자격 증명을 허용할 때는 Access-Control-Allow-Origin에 와일드카드 *를 쓸 수 없고 반드시 구체적인 출처를 지정해야 합니다.
자주 겪는 오류
Access-Control-Allow-Origin헤더 없음 → 서버가 CORS 응답 헤더를 아예 안 보냄. 서버 설정을 고쳐야 하며, 프런트엔드로는 해결 불가.- **와일드카드
*+ 자격 증명** →credentials: 'include'인데 서버가*를 보내면 차단됨. 구체적 출처를 반사(reflect)해서 응답하세요. - 프리플라이트가 2xx가 아닌 4xx/5xx →
OPTIONS를 인증 미들웨어가 가로채 401을 주는 경우가 흔함.OPTIONS는 인증 없이 통과시켜야 합니다. - CORS 오류인 줄 알았는데 사실은 네트워크·HTTPS 문제 → 브라우저 콘솔 메시지를 정확히 읽으세요.
- 오해: CORS는 서버를 보호하는 게 아니라 사용자의 브라우저를 보호합니다. 서버 접근 제어는 별도의 인증·인가로 해야 합니다.