모든 아티클

CORS 오류: 브라우저가 응답을 차단하는 이유

한국 기업·공공기관 백엔드에서 널리 쓰이는 Spring Boot에서는 @CrossOrigin(origins = "*") 애노테이션을 로컬 테스트용으로 붙였다가 그대로 배포하는 실수가 흔하다. 단순 GET 요청까지는 문제없이 동작하지만, 서브도메인에 분리된 API로 세션 쿠키를 보내기 위해 allowCredentials(true)를 추가하는 순간 브라우저가 응답을 거부하기 시작한다 — 와일드카드 오리진과 credentials는 함께 쓸 수 없기 때문이다.

동일 출처 정책

기본적으로 브라우저는 동일 출처 정책(Same-Origin Policy)을 적용한다: 한 출처(도메인+프로토콜+포트)의 페이지에 있는 JavaScript 코드는 다른 출처로 보낸 요청의 응답을 읽을 수 없다. 이는 한 사이트의 악성 코드가 사용자가 로그인한 다른 사이트의 데이터를 훔치는 것을 막는 기본적인 보안 메커니즘이다.

CORS가 이 정책을 완화하는 방식

CORS(Cross-Origin Resource Sharing)는 서버가 특정(또는 모든) 외부 출처에 자신의 응답을 읽도록 명시적으로 허용하는 HTTP 헤더 집합이다. 핵심 헤더는 Access-Control-Allow-Origin으로, 허용된 도메인을 지정한다.

와일드카드와 credentials 조합의 함정

다른 서브도메인의 API에 세션 쿠키를 보내는 데 필요한 credentials: 'include'를 붙여 요청을 보내면, 브라우저는 응답의 Access-Control-Allow-Origin: *를 절대 받아들이지 않는다. 명세는 와일드카드 오리진과 credentials의 조합을 명시적으로 금지하고 있으며, 서버는 대신 요청을 보낸 정확한 오리진을 돌려줘야 한다.

왜 필요한가

  • CORS 오류를 진단하고 서버에서 정확히 어떤 헤더가 빠져 있는지 파악한다.
  • Spring Boot에 테스트용으로 남아 있는 와일드카드 @CrossOrigin 설정을 배포 전에 찾아낸다.
  • "단순" 요청과 preflight(OPTIONS) 요청의 차이를 이해한다.

CORS는 서버를 공격으로부터 보호하지 않는다

CORS를 서버 측 보안 조치로 여기는 것은 흔한 오해다 — 실제로는 브라우저 내 JavaScript가 읽을 수 있는 것만 제한할 뿐이다. curl, Postman, 또는 백엔드 스크립트를 직접 사용하는 공격자는 이런 도구들이 동일 출처 정책을 적용하지 않기 때문에 CORS를 완전히 우회한다. CORS를 서버의 실제 인증 및 권한 부여를 대체하는 것으로 여겨서는 절대 안 된다.

도구 사용해보기