위 글은 발표 자료를 제작하며 알게된 내용을 정리한 글입니다.
개발자를 꿈꾸는 이들이 처음 웹 협업 프로젝트를 경험하다 보면 한번씩 겪게 되는 상황이 있다.
바로 CORS ERROR
이다. 이 에러에 대해 알아보자.
CORS를 알기 위해선 SOP의 개념을 함께 이해하는 것이 좋다.
SOP (Same Origin Policy)
동일 출처 정책
CORS (Cross Origin Resource Sharing)
교차 출처 자원 공유
두 개념의 직역을 나열해보아도 무슨 뜻 한번에 이해하기 어렵다.
두 단어에 공통적으로 있는 Origin
을 먼저 알아보자.
Origin(출처)란 서버의 위치를 식별하기 위해 필요한 최소한의 요소들이다.
위 그림과 같이 스킴, 호스트명, 포트 3 가지 요소를 묶어 Origin이라 정의한다.
이를 기반으로 SOP와 CORS에 대해 깊이 알아보자.
동일 출처 정책이란 동일한 출처 간의 자원 공유만 허용하는 브라우저의 정책이다.
예를 들어, 위와 같은 상황이 존재한다면 웹 서버가 데이터 처리를 API 서버 위임했다면 브라우저는 두 출처가 다름을 인식하고 Error를 발생시키게 된다.
이러한 브라우저 정책은 보안적인 이슈(CSRF, XSS 등) 예방하기 위해 적용되었다.
웹이 발전하면서 Web 서버와 API 서버가 분리되는 것이 자연스러워졌고 이를 위해 SOP의 예외 조항인 CORS가 대응법으로 사용하게 되었다.
교차 출처 자원 공유란 서버에 명시되어 있는 출처들에 한해 자원 공유를 허용하는 정책입니다.
위와 같이 API Server 에 Web Server에 해당하는 출처를 명시하면 브라우저가 예외적으로 자원 공유를 허용하는 것이다.
개념을 알았으니 동작하는 방식을 한번 알아보자.
브라우저가 CORS 여부를 판단하는 방식은 다음과 같다.
GET /profile HTTP/1.1
Origin: http://daon-world.com
먼저 Client 측에서 자신의 출처를 Origin 헤더에 담아 HTTP Request를 보낸다.
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://daon-world.com
Server는 자신의 허용 목록에 담긴 출처들을Access-Control-Allow-Origin
헤더에 담아 응답한다.
브라우저는 이 두 헤더의 값이 다른 경우 CORS 에러를 반환한다.
브라우저가 CORS를 동작하는 여러 매커니즘 중 대표적인 3가지만 다뤄보려 한다.
우선 Simple Request이다. 해당 용어는 CORS spec에서만 쓰이는 용어이나 편의를 위해 사용하겠다.
Simple Request는 위에서 설명한 기본 동작 원리를 바탕으로 동작한다.
단, 아래 조건을 모두 만족해야 가능하게 설정되어 있다.
- 3가지 메서드만 허용된다. (GET, POST, HEAD)
- 자동으로 설정되는 헤더를 제외하고 수동으로 설정할 수 있는 헤더는 Fetch 사양에서 CORS 허용 목록에 있는 요청 헤더로 정의된 헤더만 허용된다
- Content-Type 헤더에 지정된 미디어 유형은 지정된 것만 허용된다.
(application/x-www-form-urlencoded, multipart/form-data, text/plain)
...
- Cross Origin Resource Sharing - MDN
Simple Request
는 브라우저가 서버의 응답 헤더를 확인 후 출처가 같은지 판단한다. 이 과정에서 서버는 응답을 반환해야 하기 때문에 CORS ERROR가 발생하기 전 실제 데이터를 처리하게 된다.
이를 방지하기 위해 대부분의 요청은Preflight Request
시나리오를 따르게 된다.
이 사니라오는 사전에 서버에 어떠한 영향도 주지 않는 안전한 HTTP 메서드인 OPTIONS
를 이용하여 미리 요청/응답을 통해 출처가 같은지 판단한다.
이후 출처가 같다면 본 요청을 다시 보내어 실제 요청을 처리한다.
*
란 와일드 카드를 쓸 수 있다.기본적으로 브라우저는 쿠키와 같은 인증정보를 헤더를 담지 않는다. 이때 쿠키를 요청에 보내기 위해 client 측 fetch()
내부에 credentials: "include"
설정을 추가하게 된다면 Credentialed Request
시나리오를 따르게 된다.
const request = new Request(url, { credentials: "include" });
const fetchPromise = fetch(request);
fetchPromise.then((response) => console.log(response));
Access-Control-Allow-Credentials: true
설정을 추가해 주어야 한다.MDN에 정의되어 있는 CORS를 알아야 하는 대상들을 찾아보면 웹 프로젝트 관련 모든 이해관계자들이라 설명한다.
따라서 위에서 설명한 방식 여러번 숙지하며 언제든 빠르게 대처할 수 있는 개발자로 성장하길 바란다.