CORS는 웹 브라우저에서 실행되는 스크립트가 다른 도메인(출처)의 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다.
기본적으로 웹 브라우저는 보안상의 이유로 SOP (Same-Origin Policy, 동일 출처 정책)를 따릅니다. 이는 자바스크립트가 자신이 로드된 도메인과 다른 도메인으로 HTTP 요청을 보내는 것을 제한하는 규칙입니다. 하지만 현대 웹 개발에서는 API 서버와 프론트엔드 서버가 분리된 경우가 많아, 이를 안전하게 허용하기 위해 CORS가 사용됩니다.
브라우저는 다음 세 가지 요소가 모두 일치해야 동일한 출처로 판단합니다.
예를 들어, https://naver.com에서 https://api.naver.com으로 요청을 보내면 호스트가 다르기 때문에 CORS 정책의 적용을 받게 됩니다.
CORS는 HTTP 헤더를 통해 작동하며, 크게 두 가지 요청 방식으로 나뉩니다.
다음 조건을 만족하는 경우, 브라우저는 바로 본 요청을 보냅니다.
GET, POST, HEAD 중 하나일 것Accept, Content-Language 등 기본적인 것만 포함될 것대부분의 현대적인 API 요청(JSON 데이터 전송 등)은 이 방식에 해당합니다.
Access-Control-Allow-Origin) 등을 담아 보냅니다.서버 측에서 CORS 문제를 해결하기 위해 설정해야 하는 핵심 헤더들입니다.
| 헤더 | 설명 |
|---|---|
| Access-Control-Allow-Origin | 자원 접근이 허용된 출처를 지정합니다. (예: * 또는 https://my-site.com) |
| Access-Control-Allow-Methods | 허용되는 HTTP 메서드 목록을 지정합니다. (GET, POST, PUT 등) |
| Access-Control-Allow-Headers | 실제 요청 시 사용할 수 있는 HTTP 헤더를 지정합니다. |
| Access-Control-Allow-Credentials | 쿠키나 인증 정보를 포함한 요청을 허용할지 여부를 결정합니다. |
SOP(Same-Origin Policy, 동일 출처 정책)가 보안의 핵심인 이유는 간단합니다. "내가 로그인한 A 사이트의 권한을 B 사이트가 마음대로 가로채지 못하게 하기 위해서"입니다.
만약 SOP가 없다면, 여러분이 브라우저에서 탭을 여러 개 띄워놓고 사용하는 것 자체가 매우 위험한 일이 됩니다.
가장 대표적인 시나리오는 다음과 같습니다.
bank.com(은행 사이트)에 로그인되어 있고, 브라우저에는 해당 사이트의 로그인 세션(쿠키)이 남아 있습니다.evil.com에 접속하게 됩니다.evil.com의 자바스크립트가 배경에서 몰래 bank.com/transfer?to=attacker&amount=10000이라는 요청을 보냅니다.bank.com으로 요청을 보낼 때 저장된 쿠키(인증 정보)를 함께 실어 보냅니다. SOP가 없다면 은행 서버는 "오, 정당한 사용자가 돈을 보내라고 했네?"라고 판단하여 송금을 처리해 버립니다.SOP는 단순히 '요청'을 막는 것뿐만 아니라, '응답 데이터에 접근하는 것'을 막습니다.
naver.com에 접속 중인 상태에서 다른 탭으로 들어간 malicious-site.com이 자바스크립트를 이용해 naver.com/my-profile의 내용을 읽어오려 한다고 가정해 봅시다.malicious-site.com이 네이버 서버로부터 온 응답 데이터를 읽지 못하도록 차단합니다.웹 브라우저는 수많은 서로 다른 서비스들이 한 공간(브라우저)에서 실행되는 환경입니다. SOP는 각 웹사이트가 자신만의 독립된 보안 구역(Sandbox) 안에서만 놀 수 있도록 벽을 세우는 역할을 합니다.
SOP는 브라우저 차원의 최소한의 방어선입니다.
CORS는 바로 이 엄격한 보안 벽(SOP)에 "이 사이트는 믿을 만하니까 우리가 정한 규칙 하에 벽을 살짝 열어주자"라고 예외를 두는 허가증 같은 개념인 셈입니다.
정확하게 이해하셨습니다. 다만, 주체와 방향을 조금 더 명확히 정리하면 완벽합니다. CORS 설정은 자원을 가진 서버(Giver)가 누구에게 허용할지(Taker)를 결정하는 보안 정책입니다.
말씀하신 예시를 바탕으로 구조를 다시 정리해 드릴게요.
CORS는 '데이터를 주는 서버'가 설정하는 것입니다.
edward_taker.com(클라이언트/프론트엔드)이 edward_giver.com(API 서버)에 데이터를 요청합니다.edward_giver 서버가 설정을 가지고 있어야 합니다.edward_giver 서버 설정 파일에 "우리 데이터를 edward_taker.com 출처에서 가져가는 것을 허용한다"라는 화이트리스트를 등록하는 방식입니다.질문하신 내용처럼 등록 여부에 따라 결과가 달라집니다.
| 구분 | edward_giver 서버의 상태 | 결과 |
|---|---|---|
| 허용 등록 시 | Access-Control-Allow-Origin: edward_taker.com 헤더를 응답에 포함 | 브라우저가 응답을 확인하고 edward_taker의 스크립트에 데이터를 전달함 (성공) |
| 미등록 시 | 해당 헤더가 없거나 다른 도메인만 허용됨 | 브라우저가 응답을 가로채서 에러를 발생시킴 (실패) |
여기서 재미있는 점은 "요청 자체는 서버에 도달할 수도 있다"는 것입니다.
edward_giver에게 데이터를 달라고 요청합니다. 서버는 평소처럼 데이터를 보내주지만, 응답 헤더에 "허용" 표시가 없으면 브라우저가 그 데이터를 파기하고 콘솔에 빨간색 CORS 에러를 띄웁니다.OPTIONS 메서드). 이때 서버가 "안 돼"라고 하면 본 요청은 시작조차 하지 않습니다.CORS 에러는 '내 소중한 데이터를 아무나 가져가지 못하게 보호'하려는 서버의 방어 기제이기 때문입니다.
edward_taker 서버는 그저 요청을 보내는 입장일 뿐입니다.edward_taker가 보낸 요청이 실패한다면, 그것은 edward_taker가 잘못해서가 아니라 edward_giver가 문을 열어주지 않았기 때문입니다.edward_taker.com만 믿어. 얘네한테는 데이터 보내줄게." (설정 필요)edward_taker인데 데이터 좀 줘!" (설정 불필요, 요청만 보냄)