브라우저에서는 보안적인 이유로 cross-origin HTTP요청을 제한하기 때문에 요청을 위해 서버의 동의가 필요하다. 이러한 메커니즘을 HTTP-heade를 이용해서 가능하게 하는것이 CORS(Cross-Origin Resource Sharing)이다.
CORS가 없이 모든 곳에서 데이터를 요청할수 있게 되다면, 사이트를 흉내낼 수 있고 사이트 공격을 당해 사용자에게 피해가 생실 수 있다. CORS를 알아가기 앞서 SOP를 알아보자.
<img>, <script>, <frame>, <video>, <audio> 등이 웹에 등장하면서, 페이지 로딩 이후에 브라우저에서 이러한 하위 자원들을 가져올 수 있게 되었습니다. 그러므로 동일 출처, 다른 출처 모두 호출이 가능하게 되었습니다
CORS 정책이 없고 모든 다른 출처 요청이 가능한 브라우저를 생각해봅시다.

홈페이지를 서핑하고 있는데, <script>가 심어진 evil.com 페이지를 열었다고 생각해봅시다. 굉장히 유용한 정보를 담고 있는 사이트이지만, 페이지를 열면서 <script>가 실행되어 은행에 'Delete /account'를 요청하도록 되어 있습니다. AJAX 호출로 은행 API를 호출하여 나의 은행 계좌를 삭제해버리는 사고가 발생합니다.
따라서, 다른 출처의 접근을 막기 위해서 SOP(동일 출처 정책)가 등장했습니다.
: 다른 출처의 리소스를 사용하는 것에 제한하는 보안 방식

URL의 Protocol, Host, Port를 통해 같은 출처인지 다른 출처인지 판단할 수 있다.
다른 출처의 리소스가 필요한 경우는 CORS정책을 이용한다.
: 교차 출처 리소스 공유(CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 어플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제
Preflight 요청 없이 바로 실제 요청을 한다.

- GET, HEAD, POST 요청만 가능하다.
- Accept, Accept-Language, Contet-Language, Content-Type과 같은 CORS 안전 리스트 헤더 혹은 User-Agent 헤더만 허용
- Contet-Type 헤더는 application/x-www-form-urlencoded, multipart/form-data and text/plain만 가능
- ReadableStream 객체가 사용되지 않습니다.
- XMLHttpRequest 객체를 사용하여 요청하면, 요청에서 사용된 XMLHttpRequest.upload에 의해 반환되는 객체에 어떠한 이벤트 리스너도 등록되지 않는다.
: 인증 관련 헤더를 포함할 때 사용하는 요청
- 클라이언트는 credentials를 include로 설정을 해야한다.
- 서버는 Access-Control-Allow-Credentials를 true로 설정을 해야한다.
(Access-Control-Allow-Origin: * 으로 설정할 수 없다.)
OPTIONS 메서드를 통해 다른 도메인의 리소스에 요청이 가능한 지 확인하는 작업 (사전 요청) 요청이 가능하다면 실제 요청(Actual Request)을 보낸다.

- PREFLIGHT REQUEST HEADER
Origin : 요청 출처
Access-Control-Request-Method : 실제 요청의 메서드
Access-Control-Request-Headers : 실제 요청의 추가 헤더- PREFLIGHT RESPONSE HEADER
Access-Control-Allow-Origin : 서버 측 허가 출처
Access-Control-Allow-Method : 서버 측 허가 메서드
Access-Control-Allow-Headers : 서버 측 허가 헤더
Access-Control-Max-Age : preflight 응답 케시 기간
👇Access-Control-Max-Age
Preflight Request(사전 요청)과 Actual Request(실제 요청) 총 두 번의 요청을 두 번이 보내지게 된다. 이렇게 CORS 요청에 대한 두 번의 요청이 보내지게 되는데 이것이 리소스 낭비가 된다. 그렇기 때문에 PreFlight 응답에 대해서 브라우저는 캐싱을 해두고 다음 똑같은 요청 시 Preflight Request(사전 요청)의 캐싱을 확인 후 바로 Actual Request(실제 요청)을 보내게 되는 것 이다.
CORS를 모르는 서버를 위해서다.
위 예시는 Preflight Request(사전 요청) 없이 Actual Request(실제 요청)이 진행되었을 경우이다.
클라이언트는 자신의 출처를 브라우저에게 보내고 서버는 CORS에 대해 모르고 있는 상태이끼 때문에 실행을 하고 응답을 해준다. 하지만 서버는 CORS에 대한 정보가 없기 때문에 Access-Control-Allow-Origin 즉 서버측 허가 출처가 없는 상태이고 브라우저는 그것을 확인한 후 CORS Error가 발생한다. 만약 클라이언측 측에서의 요청이 심플한 GET요청이 아닌 DELETE요청인 경우에는 이미 데이터는 삭제가 된 후가 될 것이다.
만약 Preflight Request(사전 요청)이 있을 경우에는 클라이언트가 브러우저에게 Preflight Request을 보내고 브러우저는 그 요청을 서버에게 전달한다. 서버의 입장에서는 CORS의 설정이 없는 상태이기에 Access-Control-Allow-Origin이 없는 상태가 되고 브라우저는 클라이언트에게 CORS Error를 보여주게 된다. 그 후 CORS Error을 통해 클라이언트는 Actual Request(실제 요청)을 보내지 않게 되는 것이다.
참고 자료
https://escapefromcoding.tistory.com/724
https://www.youtube.com/watch?v=-2TgkKYmJt4