CORS가 뭔지, 언제 예외가 생기는지 분석해보자
동일 출처 정책의 원리, CORS 헤더 동작 방식, 그리고 Beacon·JSONP·서버 프록시 같은 예외 경로를 정리했다
CORS가 뭔지, 언제 예외가 생기는지 분석해보자
CORS가 뭔지, 언제 예외가 생기는지 분석해보자
북마크릿으로 다른 도메인 API에 요청하려다 CORS 오류에 막혔다. 정책이 어디서 동작하는지 파악했더니 예외 경로가 보였다.
1. 서론 (Introduction)
- 문제/상황 (Problem): 브라우저에서 JavaScript로 다른 도메인에 fetch 요청을 보내면 CORS 오류가 났다. 서버에서 직접 요청하면 문제가 없는데 브라우저 안에서만 막혔다.
- 목적 (Purpose): CORS 정책이 어디서 어떻게 동작하는지 이해하고, 브라우저 제약 안에서 가능한 예외 경로를 정리한다.
- 대상 (Target Audience): 브라우저 수집이나 북마크릿 개발 중 CORS에 막힌 개발자.
2. 방법 및 과정 (Methods & Process)
- 배경 조사 및 데이터 (Data Collection): CORS는 브라우저가 강제하는 보안 정책이지 서버가 강제하는 정책이 아니다. 같은 프로토콜·도메인·포트를 공유해야 “동일 출처”로 인정된다. 파이썬
requests,curl, 서버 측 프록시는 모두 이 제약을 받지 않는다. 제약은 브라우저 내 JavaScript 실행 컨텍스트에만 적용된다.
| 요청 주체 | CORS 적용 여부 |
|---|---|
| 브라우저 JavaScript(fetch/XHR) | 적용됨 |
| 서버 간 HTTP 요청(requests·curl) | 적용 안 됨 |
<script src> 태그 로드 | 적용 안 됨 |
| Beacon API | Preflight 없이 적용 |
- 접근 방법 (Approach Methods):
- [방법 1]: CORS 헤더 확인. 서버가
Access-Control-Allow-Origin: *헤더를 응답에 포함하면 브라우저가 다른 출처에서의 요청을 허용한다. 이 헤더가 없으면 브라우저가 응답을 차단한다. Preflight 요청(OPTIONS)은 실제 요청 전에 브라우저가 자동으로 서버에 보내는 사전 확인이며, 서버가 허용하지 않으면 실제 요청이 전달되지 않는다. - [방법 2]: 예외 경로 세 가지 확인. Beacon API(
navigator.sendBeacon)는 POST 데이터를 전송하는 단방향 API로 CORS Preflight 없이 전송된다. JSONP는<script src="...">태그가 CORS 제약을 받지 않는 점을 이용해 서버가 콜백 함수로 응답을 내려주는 레거시 패턴이다. 서버 프록시는 브라우저 → 내 서버 → 타깃 서버 경로를 만들어, 브라우저와 내 서버는 동일 출처로 CORS가 없고 서버 간 통신도 CORS 제약이 없는 점을 이용한다.
- [방법 1]: CORS 헤더 확인. 서버가
- 분석 및 해결 프로세스 (Analysis Flow):
- 도구/기술: Flask 데모 서버,
requests클라이언트. - 주요 단계: CORS 헤더 없는 엔드포인트 준비 $\rightarrow$ CORS 헤더 포함 엔드포인트 준비 $\rightarrow$ Beacon 수신 라우트 준비 $\rightarrow$ 서버 쪽에서 세 엔드포인트에 요청을 보내 도달 여부 확인.
- 결과 도출 및 검증:
/no-cors는Access-Control-Allow-Origin헤더 없이 status 200을 반환했고,/with-cors는 헤더 값*와 함께 status 200을 반환했다./beacon은 헤더 없이도 POST 데이터를 status 204로 정상 수신했다. 세 요청 모두 서버에는 헤더 유무와 무관하게 도달했다.
- 도구/기술: Flask 데모 서버,
3. 결과 (Results)
- 분석 결과 요약: CORS가 서버가 아닌 브라우저 정책임을 확인했다. 서버 관점에서는 헤더 유무와 무관하게 요청이 항상 도달하며, 차단은 브라우저가 응답을 읽는 단계에서만 일어난다. Beacon API로 헤더 없는 엔드포인트에도 데이터가 도달함을 확인했다.
4. 인사이트 및 액션 (Insights & Action)
- 인사이트 (Insight): CORS는 사용자 보호를 위한 브라우저 정책이다. “서버가 막는다”가 아니라 “브라우저가 응답 읽기를 막는다”는 이해가 우회 경로를 찾는 출발점이다.
- 실행 방안 (Action Plan): 브라우저에서 데이터를 내 서버로 보내야 한다면 Beacon API가 가장 간단하다. 응답이 필요 없는 전송에는 CORS Preflight 없이 작동한다.
- 한 줄 결론 (Key Takeaway): CORS는 브라우저가 응답을 읽는 단계를 막는 정책이라 서버 프록시나 Beacon으로 우회하면 된다. 샘플 코드
- 다음 스텝 (Next Step): 이번 데모는 서버 쪽 도달 여부만 확인했다. 실제 브라우저에서
/no-cors응답을 fetch로 읽으려 할 때 콘솔에 찍히는 차단 메시지까지 캡처해 비교하면 정책의 경계가 더 뚜렷해질 것이다.
This post is licensed under CC BY 4.0 by the author.