- CORS란?
- CORS ( Cross-Origin Resource Sharing )는 HTTP 헤더 기반 메커니즘으로 , 브라우저가 리소스 로드를 허용해야 하는 자체 출처 이외의 모든 출처 (도메인, 스키마 또는 포트)를 서버가 표시할 수 있도록 합니다. CORS는 또한 서버가 실제 요청을 허용하는지 확인하기 위해 브라우저가 원본 간 리소스를 호스팅하는 서버에 "실행 전" 요청을 하는 메커니즘에 의존합니다. 해당 프리플라이트에서 브라우저는 HTTP 메서드를 나타내는 헤더와 실제 요청에 사용될 헤더를 보냅니다. - MDN - 자료의 출처에 대해서 확인하는 작업인거 같은 느낌이 든다!
- 출처(origin) 뭐임?!
- URL은 여러가지 정보로 이루어져 있다. 아래의 사진에는 없지만 :80, :443과 같은 포트번호 까지 합친것을 의미한다.
- 포트 번호까지 명시되어 있다면 포트 번호 까지 같아야 같은 출처로 인정된다. 그러나 해당 사항이 표준으로 정의되어 있는 것은 아니기 때문에 그때 그때 ‘같은 출처’로 인정되지 않을 수 도 있다(…..-_-). 그래도 대부분 ‘scheme’, ‘host’, ‘port’가 같다면 같은 출처라고 인식한다.
- 이때 출처를 판단하는 동작은 브라우저에서 일어난다. 서버에서 ‘같은 출처에서 오는 리소스 요청만 받겠다’ 라는 처리를 하지 않는 이상 정상적으로 리소스 요청을 할 수 있고 응답을 받을 수 있다. 이때 같은 출처인지를 브라우저가 판단하고 같은 출처가 아니라고 판단하면 응답받은 응답을 버리는 것이다.





- SOP(Same-Origin Policy)
- 다른 출처로의 리소스 요청을 제한하는 또 다른 방법이다.
- 같은 출처로의 리소스 요청만 가능하다 라는 정책이다. 그러나 웹이라는 오픈스페이스 환경에서 다른 출처에 있는 리소스를 가져와서 사용하는 일은 굉장히 흔한 일이라 몇 가지 예외 조항을 두고 이 조항에 해당하는 리소스 요청은 출처가 다르더라도 허용하기로한다. 그 중 하나가 ‘CORS’정책을 지킨 리소스 요청이다. SOP 정책을 위반하고 CORS 정책 까지 위반하면 다른 출처의 리소스를 사용할 수 없게 되는 것이다.
- 만약 SOP나 CORS같은 정책이 없다면 CSRF(Cross stie request Forgery)나 XSS(Cross site scripting)과 공격에 취약할 수 있다.
- CORS는 어떻게 동작함?
- 기본적으로 웹 클라이언트 어플리케이션이 다른 출처의 리소스를 요청할 때는 HTTP 프로토콜을 사용하여 요청을 보내게 되는데, 이때 브라우저는 요청 헤더에 Origin이라는 필드에 요청을 보내는 출처를 함께 담아보낸다.
- 이후 서버가 이 요청에 대한 응답을 할 때 응답 헤더의 Access-Control-Allow-Origin 이라는 값에 ‘이 리소스를 접근하는 것이 허용된 출처’를 내려주고 이후 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 응답이 유요한 응답인지 아닌지 판단한다.
- Preflight Request
- 일단적으로 웹 개발을 할 때 마주치는 시나리오다. 이 시나리오에 해당하는 상황일 때 브라우저는 요청을 한 번에 보내지않고 예비 요청과 본 요청으로 나누어서 서버로 전송한다.
- 이 때 CORS 정책을 위반하더라도 응답 자체는 정상적으로 200으로 떨어질 수 있다. 정상적으로 떨어진 200 이라는 응답안에 접근 가능한 출처가 들어있는 것이고 브라우저에서 이를 바탕으로 판단하는 것이기 때문이다.
- Simple Request
- 이 시나리오에 대한 정식 명칭은 없지만 MDN의 CORS문서에서 이 시나리오를 Simple Request라고 부른다.
- 단순 요청은 예비 요청을 보내지 않고 서버에게 본 요청을 하고, 서버가 이에 대한 응답의 헤더에 Access-Control-Allow-Origin과 같은 값을 보내주면 그 때 브라우저가 CORS 정책 위반 여부를 검사하는 방식이다.
- 그럼 Preflight Requset 안쓰고 단순 요청 하면 되는거아님?
- 아무 때나 단순 요청을 할 순 없다.
- 대부분의 HTTP API는 text/xml 이나 application/json 컨텐츠 타입을 가지도록 설계되기 때문에 사실상 이 조건들을 만족시키는 상황은 흔치 않다.
- Credentaled Requset
- 인증된 요청을 사용하는 방법이다. 이 시나리오는 CORS의 기본적인 방식이라기 보다는 다른 출처 간 통신에서 좀 더 보안을 강화하고 싶을 때 사용하는 방법이다.
- 기본적으로 브라우저가 제공하는 비동기 리소스 요청 API인 XMLHttpRequset 객체나 fetch API는 별도의 옵션 없이 브라우저의 쿠키 정보나 인증과 관련된 헤더를 함부로 요청에 담지 않는다. 이때 요청에 인증과 관련된 정보르 담을 수 있게 해주는 옵션이 credentials 옵션이다.



- CORS를 해결해보자.
- Access-Control-Allow-Origin 세팅하기
- 정석대로 서버에서 Access-Control-Allow-Origin 헤더에 알맞은 값을 세팅해주는 것이다.
- Webpack Dev Server로 리버스 프록싱하기 (흠……)
- 로컬 환경에서 /api 로 시작하는 URL로 보내는 요청에 대해 브라우저는 localhost:8000/api 로 요청을 보낸 것으로 알고 있지만, 사실 뒤에서 웹팩이 https://api.evan.com 으로 요청을 프록싱해주기 때문에 마치 CORS 정책을 지킨 것처럼 브라우저를 속이면서도 우리는 원하는 서버와 자유롭게 통신할 수 있다.
- 다만 이 방법은 실제 프로덕션 환경에서도 클라이언트 어플리케이션의 소스를 서빙하는 출처와 API서버의 출처가 같은 경우에 사용하는것이 좋다. 어플리케이션을 빌드 하고 서버에 올리고 나면 더 이상 webpack-dev-server가 구동하는 환경이 아니기 때문에 이상한 곳으로 API 요청을 보낸다.

CORS는 문제를 겪는 사람은 대부분 프론트엔드 개발자이지만, 문제를 해결을 위해서는 백엔드 개발자의 도움이 필요한 부분이다.