프로젝트를 하면서 수없이 만났던 CORS(Cross Origin Resource Sharing)!
정작 제대로 알고 있지는 못한 것 같아서 더 깊게 이해해보고자 한다.
CORS의 O는 Origin으로, URL의 프로토콜
+ 호스트
+ 포트
를 의미한다.
SOP는 동일 출처 정책을 말한다.
즉, 동일 출처(Same-Origin) 서버에 있는 리소스는 자유로이 가져올 수 있지만, 다른 출처(Cross-Origin) 서버에 있는 이미지나 영상 같은 리소스는 가져올 수 없다는 것이다.
동일 출처 정책은 CSRF나 XSS 공격에 대비하여 필요하다.
(이 부분에 대해서는 다음 포스트에 다루도록 하겠다!)
즉 SOP 정책은 프로토콜
, 호스트
, 포트
가 모두 같을 때만 리소스 사용을 허가한다.
그러나 웹이 발전하면서 어쩔 수 없이 다른 출처 간 상호작용을 해야 하는 경우가 많이 발생하기 시작했다.
그래서 다른 출처의 리소스를 사용하기 위한 해결책으로 CORS 정책이 나타나게 되었다.
Origin
을 담아 전달한다.Access-Control-Allow-Origin
을 담아 전달한다.Origin
이 서버에서 보내준 Access-Control-Allow-Origin
에 속해 있는지 확인한다.브라우저는 요청을 보낼 때 한 번에 보내지 않고, 예비 요청을 보내 서버와 잘 통신되는지 확인한 후 본 요청을 보낸다.
즉, 예비 요청의 역할은 본 요청을 보내기 전에 브라우저 스스로 안전한 요청인지 미리 확인하는 것이다.
이 때 브라우저가 예비 요청을 보내는 것을 Preflight
라고 부르며, 이 예비 요청의 HTTP 메소드는 GET
, POST
등이 아닌 OPTIONS
가 사용된다.
예비 요청의 취지는 좋지만 결국은 실제 요청에 걸리는 시간이 늘어나 성능에 영향을 미칠 수 있다.
수행하는 API 호출 수가 많으면 많을수록 서버 요청을 배로 보내기 때문이다.
따라서 브라우저 캐시를 이용해 응답 Header의 Access-Control-Max-Age
에 캐시될 시간을 명시해주면 최적화할 수 있다.
예비 요청을 생략하고 바로 본 요청을 보낸 후 그 응답을 토대로 CORS 정책 위반 여부를 판단하는 것이다.
단순 요청으로 요청을 보내기 위해서는 아래의 3가지 조건을 만족해야 한다.
GET
, POST
, HEAD
중 하나여야 한다.Accept
, Accept-Language
, Content-Language
, Content-Type
, DPR
, Downlink
, Save-Data
, Viewport-Width
, Width
일 경우 에만 적용된다.Content-Type
이 application/x-www-form-urlencoded
, multipart/form-data
, text/plain
중 하나여야한다. 대부분의 HTTP API 요청은 application/json
이나 text/xml
으로 통신하기 때문에 3번 조건을 만족하지 못한다.
위의 두 유형은 인증 정보가 없는 경우의 요청이고, 인증 정보를 함께 보내야 하는 요청이라면 조금 다른 형태로 통신하게 된다.
여기서 인증 정보란 세션 ID가 저장되어 있는쿠키
혹은 Header의 Authorization
에 설정하는 토큰 값 등을 말한다.
credentials
옵션을 지정해야 한다.Access-Control-Allow-Credentials
를 true로 설정해야 한다.Access-Control-Allow-Origin
의 값에 와일드카드 문자("")는 사용할 수 없다.Access-Control-Allow-Methods
의 값에 와일드카드 문자("")는 사용할 수 없다.Access-Control-Allow-Headers
의 값에 와일드카드 문자("*")는 사용할 수 없다.마지막으로, SOP와 CORS는 브라우저의 정책이다.
즉 서버는 정상적인 응답을 하고 있지만 브라우저에서 차단하는 것이다.
그렇다고 CORS 에러를 클라이언트 단에서 해결할 수 있느냐고 한다면 그것도 아니다.
서버에서 Access-Control-Allow-Origin
에 해당 도메인을 추가해야 한다!