
CORS는 Cross Origin Resource Sharing에 약자다.
Origin은 url 주소상에서 프로토콜, Domain 이름, 포트까지 포함한 개념이다.
예시로, 로컬에 nextjs 프론트엔드 서버를 3000번으로 띄웠다면
http://localhost:3000 이 Origin에 해당한다.
Same Origin이란 같은 Origin을 뜻하고
Cross Origin 이란 서로 다른 Origin을 뜻하는 말이다.
여기에 붙은 Resrouce Sharing 의 단어까지 포함하면
CORS는 서로 다른 Origin간의 리소스 교환을 뜻한다.

URL 구조
출처가 무엇인지 알아보기 위해서는 URL 의 구조를 살펴보아야 한다.
서버의 위치를 의미하는 https://google.com 과 같은 URL 은 사실 여러 요소로 이루어져 있다.

포트번호가 생략이 가능한 이유는 http, https 프로토콜의 기본 포트번호가 정해져 있기 때문이다.
http://velog.com:870 과 같이 포트번호가 명시적으로 표기 된게 아니라면
http는 80번 https는 443번 포트가 디폴트 값이다.
출처는 Protocol, Host, 포트번호를 의미한다.
즉 서버의 위치를 찾아가기 위해 필요한 가장 기본적인 것들을 합쳐놓은 것이다.
-> 콘솔창에 location.origin 실행하면 출처 확인할 수 있다.
http / https -> 프로토콜
naver.com / google.com -> 호스트
:80 / :81 / :443 -> 포트번호
⚒️ 나의 경우 프론트엔드와 백엔드가 같은 포트 8077에서 실행 중이라도, 호스트가 달라서 CORS 에러가 발생했다.
다른 출처의 리소스 공유에 대한 허용/비허용 정책
클라이언트가 HTTP 요청을 보낼 때, HTTP 요청의 헤더에 Origin을 담아 전달한다.
서버는 요청을 받고, Origin 정보를 기반으로 해당 요청이 허용되는지 검사한다.
서버는 요청을 받고, Origin 정보를 기반으로 해당 요청이 허용되는지 검사한다. 허용된 Origin일 경우, 응답헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달한다. 이를 통해 해당 Origin이 서버에 접근할 수 있는 권한이 있는지 알려준다.
브라우저 내에서: 클라이언트가 서버로부터의 응답을 받으면, 브라우저는 해당 응답의 Access-Control-Allow-Origin 값을 검사하여 요청한 Origin과 일치하는지 확인한다.
일치하는 경우: 브라우저는 응답을 클라이언트에게 전달한다.
일치하지 않는 경우: 브라우저는 보안 상의 이유로 응답을 무시하고 JavaScript 등에서 접근이 차단되는 CORS 에러가 발생한다.

브라우저는 요청을 보낼때 한번에 바로 보내지않고, 먼저 예비 요청을 보내 서버와 잘 통신되는지 확인한 후 본 요청을 보낸다.
즉, 예비 요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 미리 확인하는 것이다.
이때 브라우저가 예비요청을 보내는 것을 Preflight라고 부르며, 이 예비요청의 HTTP 메소드를 GET이나 POST가 아닌 OPTIONS라는 독립적인 요청이 사용된다는 것이 특징이다.
API 호출 수가 많으면 많을 수록 예비 요청으로 인해 서버 요청을 배로 보내게 되니 비용 적인 측면에서 폐가 될 수 있다.
따라서 브라우저 캐시(Cache)Visit Website 를 이용해 Access-Control-Max-Age 헤더에 캐시될 시간을 명시해 주면, 이 Preflight 요청을 캐싱 시켜 최적화를 시켜줄 수 있다.
대부분의 경우 preflight 방식을 사용하지만,
특정 조건들이 전부 만족된 경우 예비 요청 없이 본 요청만으로
CORS 정책 위반 여부를 검사할 수도 있다.

📍특정 조건이란?
1. 요청 메소드가 GET, POST, HEAD 중 하나여야 함.
2. Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안됨
3. 만약 Content-Type를 사용하는 경우에는 application/x-www-form-urlencoded, multipart/form-data, text/plain만 허용됨.
📍이러한 조건을 모두 만족시키기 어렵기 때문에 (특히나 2,3번이 까다로움)
본 요청을 바로 보내는 Simple Request보다
예비 요청을 먼저 보내는 Preflight Request를 더 자주 접할 수 있다.


allow CORS / cors unblock
브라우저 오른쪽 상단에서 확장 프로그램을 활성화 시킬 수 있다. 해당 프로그램을 활성화 시키게 되면, 로컬(localhost) 환경에서 API를 테스트 시, CORS 문제를 해결할 수 있다.
처음에 CORS 에러가 생겼을때 cors unblock 확장 프로그램으로 우선 개발을 한 뒤에 cors 에러를 마지막으로 해결했다. cors 에러로 머리가 아플때는 개발 시점에서는 사용해도 좋다고 생각한다.
서버에 따로 설정을 안한 상태에서,
클라이언트가 서버에 리로스 요청을 한다면 CORS 에러가 발생할 것이다.
이런 경우, 모든 출처를 허용한 서버 대리점인 프록시(Proxy) 서버를 통해 요청을 할 수 있다.
현재 무료 프록시 서버 대여 서비스들은 모두 악용 사례 때문에 api 요청 횟수 제한을 두어 실전에서는 사용하기 무리이다. 따라서 실전에서는 직접 프록시(Proxy) 서버를 구축하여 사용해야 한다.
heroku 프록시 서버
http://cors-anywhere.herokuapp.com
서비스를 가장 간단하게 사용할 수 있었으나 2021.02부터는 한 번 들어가서 허용 버튼을 눌러야 가능하다고 하며, cors-anywhere 프로젝트를 fork하여 heroku에서 프록시서버를 만들 수도 있다.
직접 서버에서 HTTP 헤더 설정을 통해 출처를 허용하게 설정하는 가장 정석적인 해결책이다.
서버의 종류도 노드 서버, 스프링 서버, 아파치 서버 등 여러가지가 있으니, 이에 대한 각각 해결책을 나열해본다.
각 서버의 문법에 맞게 위의 HTTP 헤더를 추가해 주면 된다.
참고로 CORS에 연관된 HTTP 헤더 값으로는 다음 종류가 있다. (이들을 모두 설정할 필요는 없다.)
# 헤더에 작성된 출처만 브라우저가 리소스를 접근할 수 있도록 허용함.
# * 이면 모든 곳에 공개되어 있음을 의미한다.
Access-Control-Allow-Origin : https://naver.com
# 리소스 접근을 허용하는 HTTP 메서드를 지정해 주는 헤더
Access-Control-Request-Methods : GET, POST, PUT, DELETE
# 요청을 허용하는 해더.
Access-Control-Allow-Headers : Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization
# 클라이언트에서 preflight 의 요청 결과를 저장할 기간을 지정
# 60초 동안 preflight 요청을 캐시하는 설정으로, 첫 요청 이후 60초 동안은 OPTIONS 메소드를 사용하는 예비 요청을 보내지 않는다.
Access-Control-Max-Age : 60
# 클라이언트 요청이 쿠키를 통해서 자격 증명을 해야 하는 경우에 true.
# 자바스크립트 요청에서 credentials가 include일 때 요청에 대한 응답을 할 수 있는지를 나타낸다.
Access-Control-Allow-Credentials : true
# 기본적으로 브라우저에게 노출이 되지 않지만, 브라우저 측에서 접근할 수 있게 허용해주는 헤더를 지정
Access-Control-Expose-Headers : Content-Length
⭐ CORS에러는 브라우저에서만 발생⭐ 한다는 것.. 꼭 기억하자.
😥 나는 만든 페이지를 개발 + https에 배포까지 해야하는 상황이었다. 3가지 이외의 방법인 http-proxy-middleware는 로컬 환경일 경우 한정해서 해결할 수 있는 등 개발 + 배포 두가지 상황에서 cors를 만족스럽게 해결할 수 있는 방법은 서버에서 헤더를 세팅하는 방법 뿐이었다.
chrome extension으로 cors 에러를 임시 해결해서 먼저 페이지 개발부터 진행했다. 그 후 에러에 대한 설명과 함께 백엔드 분께 헤더에 코드 추가를 부탁드렸다. 놀랍게도 백엔드분께서 코드를 추가하시자마자 바로 해결되었다.
개발하면서 꼭 만나야하는 cors 그래도 만나서 반갑다 😂
참고자료
악명 높은 CORS 개념 & 해결법 - 정리 끝판왕
CORS 에러를 프론트에서 해결? 무시? 아무튼? 해보자!
어쩌다 마주친 CORS 에러
지긋지긋한 CORS error 이제 제대로 공부해보자
CORS란? CORS 에러란?