CORS(Cross-origin resource sharing) 이슈는 웹 개발 초기에 누구나 한 번쯤 겪을 수 있는 상황입니다. 필자의 경우 기존 개발 환경에서 vue.js를 도입 하였을 때 아래와 같은 경고 문구를 보며 당황했던 기억이 있습니다. 이번 기회에 경고 문구를 뜯어보며 CORS에 대해 한 발짝 다가가 볼까 합니다.
Access to XMLHttpRequest at ‘http://192.168.xxx.xxx:8080/api/sign/login’ from origin ‘http://localhost:8080’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. |
흔하게 발생할 수 있는 전체 경고 문구는 위와 같습니다. 하나씩 구분해서 살펴 보면
Access to XMLHttpRequest at ‘http://192.168.xxx.xxx:8080/api/sign/login’ |
현재 웹 페이지에서 XMLHttpRequest를 사용해 ‘http://192.168.xxx.xxx:8080/api/sign/login’에 정보를 요청하고자 접근(access) 했습니다.
from origin ‘http://localhost:8080’ has been blocked by CORS policy |
XMLHttpRequest를 작성한 파일의 출처(origin)는 ‘http://localhost:8080’이고, XMLHttpRequest의 사용은 CORS 정책에 의해 막혔습니다.
Response to preflight request doesn’t pass access control check |
사전 요청(preflight request)이 접근 제어 확인을 통과하지 못했고,
No ‘Access-Control-Allow-Origin’ header is present on the requested resource |
Access-Control-Allow-Origin’이란 헤더가 요청하고 있는 자원(resource)에 없기 때문입니다.
1. 자원(resource)
자원은 HTTP 요청 대상을 말하며 문서, 사진등 어떠한 형태든 될 수 있습니다. 자세한 내용은 잘 정리된 곳이 있으니 참고 하시기 바랍니다. (참조 : MDN – 웹 리소스 식별 )
2. 출처(origin)
출처는 CORS 정책의 적용 기준이 됩니다. A서버 자원이 B서버 자원을 요청하는 상황에서 A, B서버가 서로 다른 서버이면 자원의 출처가 다르다고 판단하여 CORS 정책을 적용합니다. 웹은 프로토콜, 호스트, 포트가 같아야 동일한 출처로 간주하는데 이를 SOP(Same-Origin Policy) – 동일 출처 정책 – 이라 하며, CORS 정책의 근간이 됩니다. CORS 정책은 SOP에 예외를 두는 셈입니다.
http://blog.sinsiway.com/trend/post.html과 출처(origin) 비교
http://blog.sinsiway.com/trend/ittrend/post.html | 요청 성공 | 경로 |
http://blog.sinsiway.com/inside/post.html | 요청 성공 | 경로 |
https://blog.sinsiway.com/trend/post.html | 요청 실패 | protocol |
http://blog.sinsiway.com:81/trend/post.html | 요청 실패 | port |
http://rnd.sinsiway.com/trend/post.html | 요청 실패 | host |
3. 사전 요청(Preflight request)
사전 요청은 CORS 정책의 확인 시점입니다. 사전 요청은 먼저 Simple Request 에 해당하지 않는 요청을 말합니다. Simple Request의 조건은 이렇습니다.
1) HTTP Method – GET / HEAD / POST
2) 사용자 정의 헤더(자동 설정 헤더 제외) – Accept / Accept-Language / Content-Language / Content-Type
3) 헤더 Content-Type의 값 – application/x-www-form-urlencoded, multipart/form-data, text/plain
사전 요청은 CORS 확인을 위해 실제 요청 전에 자동 수행하는 요청으로 OPTIONS 메소드를 사용합니다. ‘Access-Control-Request-Method’, ‘Access-Control-Request-Headers’ 등의 헤더에 CORS 조건에서 사용할 메소드 등을 서버에 알려주고 어떻게 응답하는지 먼저 확인하는 것입니다. 서버의 응답에 따라 실제 요청의 차단 여부가 결정됩니다. Simple Request에 해당하더라도 CORS와 무관한 것은 아닙니다. 단지 사전 요청(Preflight request)이 발생하지 않을 뿐입니다.
예를 들어 HTTP Method – POST에 Content-Type – ‘application/json’로 요청하였다면, Simple request에 해당하지 않기 때문에 사전 요청이 발생합니다.
요청 받은 서버에 CORS 조치가 없다면, 실제 요청(POST)까지 가지 않고 끝납니다.
4. ‘Access-Control-Allow-Origin’ 헤더
CORS 정책에 허용할 출처를 명시하는 헤더입니다. 3번에서 언급한 “서버의 응답”에 해당합니다. 서버는 응답 헤더에 ‘Access-Control-Allow-Origin’와 허용할 출처(origin) 정보를 입력하여 CORS 정책을 허용합니다.
출처는 ‘Access-Control-Allow-Credentials: true’가 아니라면 와일드 카드(*)도 가능 합니다.
끝으로 조치 방안입니다. 경고 문구 기반 원리 이해가 목적이었기 때문에 해결책은 간단하게 요약 하고자 합니다. 우선 해결책의 기본 방안은 서버 측에서 CORS 작업을 하는 것입니다. 익스프레스, 스프링 등 프레임워크 차원에서 기능이 제공되고 있습니다.
express – https://expressjs.com/en/resources/middleware/cors.html
Spring framework – https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/cors.html
당장 개발에 영향을 받는 프론트엔드에서 조치할 방안으로는 크롬 브라우저에 옵션을 달아서 실행하거나, 개발 환경 Local Proxy (https://webpack.js.org/configuration/dev-server/) 를 세우는 방법 등이 있습니다.
“C:\Program Files (x86)\Google\Chrome\Application\chrome.exe” –disable-web-security –user-data-dir=”C:\chrome” |
참고 자료 : 모질로 공식 홈페이지(교차 출처 리소스 공유)
-
NEXT 행복한 중기경영대상 산업통상자원부장관상 수상
2020-08-31