이전에 진행했던 프로젝트에서 URL을 이용해 HTML 소스를 긁어오는 기능을 브라우저 상에서 구현하다가 위 이미지와 같이 CORS 정책 관련 오류를 마주쳤었고, 프록시 서버를 별도로 구축하여 문제를 해결할 수 있었습니다.
해결 과정에서 배운 CORS 개념과 관련 문제를 상황에 맞게 해결하는 방법에 대해 블로그를 통해 공유합니다.
MDN의 CORS에 관한 글에서는 CORS를 아래와 같이 설명합니다.
교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다.
쉽게 이해하기 어려운 내용이니 차근차근 이해해보겠습니다.
우선 리소스를 자신의 출처(도메인, 프로토콜, 포트)와 다른 출처에 요청을 보내는 것을 Cross-Origin Request라고 합니다. (예를 들어, https://a.com
에서 https://b.com/data.json
을 요청)
그리고 이러한 Cross-Origin Request에 대한 Response를 받으려면, 즉 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여받으려면 Remote Origin(요청에 대한 응답을 보내는 쪽)에서 추가 HTTP 헤더를 포함하여 Response를 전송하여 브라우저에 알려주어야 합니다.
이러한 체제를 교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)라고 합니다.
만약 해당 HTTP 헤더를 포함한 Response를 받지 못한 경우, 브라우저는 보안 상의 이유로 Cross-Origin Request를 차단하는데, 이러한 브라우저 상의 정책을 CORS(Cross-Origin Resource Sharing) 정책이라고 합니다.
위에서 "다른 출처"를 언급하였습니다. 그렇다면 "다르다"의 기준이 무엇일까요?
이는 Cross-Origin이라는 단어에도 나와있지만, 오리진(origin)이 다른 경우를 말합니다. 오리진은 프로토콜(protocol), 도메인(domain), 포트(port)로 구성되어 있으며, 세 가지 중에 하나라도 다른 두 출처 간의 요청은 Cross-Origin Request인 것입니다.
(URL의 path, search, hash 부분이 다른 것은 "다른 출처"와 관련이 없습니다.)
- 도메인(domain): naver.com
- 오리진(origin): https://naver.com:80
- URL: https://naver.com:80/search?q=hello#1
이제 상황에 따라 CORS 이슈를 해결하는 방법들을 알아보겠습니다.
정확히는 Cross-Origin Request에 대한 Response를 전송하는 서버를 컨트롤 할 수 있는 경우 사용할 수 있는 방법입니다.
응답 서버의 HTTP Response의 Access-Control-Allow-Origin
Header 값에 해당 요청을 수락할 출처(Origin)들을 지정하여 CORS 정책 관련 문제를 해결할 수 있습니다.
Access-Control-Allow-Origin: <origin> | *
"*
" 와일드 카드는 어떤 출처든 상관없이 리소스에 접근 가능하도록 허용합니다. (편리한 대신, 보안에 취약해집니다.)
이 방법을 사용한 경우, 브라우저는 서로 다른 출처들 간의 HTTP Request/Response가 사전에 상호합의된 것이므로 보안상 문제가 없다고 인지하고 CORS 정책 오류를 발생시키지 않게 됩니다.
다만, 인증 정보를 포함한 요청(Credentialed Request)인 경우 HTTP 헤더에 Access-Control-Allow-Credentials: true
를 추가해야 하며, Access-Control-Allow-Origin
헤더 값에 "*
" 와일드카드 대신 출처를 직접 지정해야 합니다.
Credentialed Request
: HTTP cookies 또는 HTTP Authentication 정보가 포함된 요청
다음은 Cross-Origin Request에 대한 Response를 전송하는 서버를 컨트롤 할 수 없는 경우입니다.
이런 경우에는 프록시 서버(Proxy Server)를 구축하여 요청을 우회시켜 CORS 정책 이슈를 해결할 수 있습니다.
위에서 이야기했던 것처럼, CORS는 브라우저 정책입니다. 그렇다면 브라우저를 끼지 않는 서버 간의 요청 및 응답에 대해서는 CORS 정책 문제가 당연히 발생하지 않습니다.
예를 들어, 요청을 보내는 서버를 A
, 이에 따른 응답을 보내는 서버를 B
로 가정하겠습니다.
프록시 서버를 두 서버 사이에 놓는다면, 이제 A
서버에서 B
서버로 직접 요청을 보내는 것이 아니라, A
서버는 프록시 서버로 요청을 보내고, 프록시 서버는 요청을 받으면 B
서버로 요청을 보내 데이터를 가져온 후, A
서버에서 보낸 요청에 대한 응답으로 해당 데이터를 보내는 과정이 이루어지는 것 입니다.
이제 프록시 서버는 "컨트롤이 가능한 서버"이므로, 위에서 작성한 1번 해결 방법을 통해 A
서버와 프록시 서버 간의 CORS 정책 문제를 해결할 수 있습니다.
위 작업을 위한 프록시 서버는 직접 구현 및 구축할 수도 있지만, cors-anywhere 라는 오픈 소스를 이용해 비교적 쉽게 만들 수 있습니다.