Same-Origin Policy(SOP)란 무엇인가
Same-Origin Policy(동일 출처 정책)는 브라우저가 보안을 위해 기본적으로 적용하는 규칙으로, ‘다른 출처(origin)’에서 로드된 스크립트가 현재 페이지의 데이터에 접근하지 못하도록 막는 정책이다. 여기서 출처란 프로토콜, 호스트(도메인), 포트 세 가지가 모두 같아야 동일하다고 판단한다. 예를 들어 ‘https://example.com:443’과 ‘http://example.com:443’은 프로토콜이 다르므로 다른 출처이고, ‘https://example.com’과 ‘https://api.example.com’도 서브도메인이 다르므로 다른 출처로 취급된다.
이 정책이 없다면 어떤 일이 벌어질까. 예를 들어 사용자가 은행 사이트에 로그인한 상태에서 악성 사이트를 방문했다고 가정하자. 만약 브라우저가 출처를 구분하지 않는다면, 악성 사이트의 자바스크립트가 사용자의 쿠키를 이용해 은행 사이트에 몰래 요청을 보내고 계좌 정보를 읽어올 수 있다. 마치 옆집 우편함 열쇠를 아무나 복제해서 쓸 수 있는 것과 같은 상황이다. SOP는 바로 이런 상황을 막기 위해 브라우저에 기본적으로 내장된 방어벽이다.
그렇다면 CORS는 왜 필요한가
문제는 실무에서 서로 다른 출처 간의 통신이 반드시 필요한 경우가 매우 많다는 점이다. 프론트엔드는 ‘https://myapp.com’에서 서비스되고, 백엔드 API는 ‘https://api.myapp.com’에서 서비스되는 구조는 요즘 흔하다. 이때 SOP를 그대로 적용하면 정상적인 요청까지 모두 차단되어 버린다. 그래서 등장한 것이 CORS(Cross-Origin Resource Sharing)다.
CORS는 SOP를 없애는 기술이 아니라, 서버가 ‘이 출처는 허용한다’고 명시적으로 브라우저에 알려주는 예외 규칙이다. 서버가 응답 헤더에 ‘Access-Control-Allow-Origin: https://myapp.com’과 같은 값을 담아 보내면, 브라우저는 이를 확인하고 해당 출처의 스크립트가 응답 데이터를 사용할 수 있도록 허용한다. 즉 결정 권한은 여전히 서버에 있고, 브라우저는 그 결정을 검증하는 역할을 한다.
실무에서 CORS를 다루는 방식
실제 개발 환경에서는 다음과 같은 CORS 관련 상황을 자주 마주친다.
- 단순 요청(GET 등)은 바로 서버에 전달되지만, 응답에 허용 헤더가 없으면 브라우저가 결과를 화면에서 차단한다.
- PUT, DELETE처럼 상태를 변경하는 요청이나 커스텀 헤더가 포함된 요청은 본 요청 전에 ‘OPTIONS’ 메서드로 사전 요청(preflight)을 보내 서버의 허용 여부를 먼저 확인한다.
- 서버 개발자는 Spring이나 Express 같은 프레임워크에서 CORS 설정을 통해 허용할 출처, 메서드, 헤더 목록을 지정해야 한다.
결국 CORS 에러를 만났을 때 당황하지 않으려면, 이것이 클라이언트 코드의 버그가 아니라 서버 쪽에서 해당 출처를 허용하도록 설정해야 하는 문제라는 점을 이해하는 것이 중요하다. SOP는 보안을 위한 기본 원칙이고, CORS는 그 원칙 안에서 안전하게 예외를 허용하는 공식적인 통로인 셈이다.