어느덧 벌써 항해 5주차다... 이번 주는 처음으로 프론트엔드분들과의 협업을 통해 미니프로젝트를 진행하는 주차다. 그래서 이와 관련한 라이브 세션을 진행했는데 이 때 CORS 라는 것에 대해 알려줬다.
우리 조는 아직 백엔드 서버 구축이 완료가 안 돼서 CORS 를 만나보지는 못 했지만 프론트엔드와의 협업을 하다보면 매우 쉽게 만나볼 수 있는 문제기에 한 번 정리를 해보고자 한다.
CORS(Cross Origin Resource Sharing)
이름 그대로 해석해보면 서로 다른 Origin 끼리 교차해서 자원을 공유하는 것과 관련된 정책을 의미하는데 Origin은 무엇일까?
Origin
Origin은 URL에서 프로토콜, 도메인, 포트 번호를 합친 부분을 의미한다. 내 블로그 URL은 https://w-hand.tistory.com/ 이렇게 생겼는데
https -> 프로토콜
w-hand.tistory.com -> 도메인
이고 포트 번호는 우리 눈에는 보이지 않지만 https는 기본 포트로 443 번을 사용하고 있다.
그래서 https://w-hand.tistory.com:443 이 바로 Origin이 되는 것이다.
CORS
CORS는 서로 다른 Origin 으로 요청을 보내기 위해서 지켜야 하는 '브라우저'의 정책이다. 서버는 늘 요청에 따라 응답을 해주기만 할 뿐이고 브라우저 쪽에서 내가 보낸 요청과 서버의 응답이 CORS 정책을 잘 지키고 있는지 검사하는 방식이다.
그렇다면 어떤 원리를 통해 요청과 응답이 CORS 정책을 잘 지키고 있는지 검사하는 걸까?
CORS 동작 원리
1. 요청 헤더에 자신의 Origin 을 설정
2. 서버로 부터 받은 응답 헤더에 설정된 Origin 목록에 자기 Origin이 포함되는지 검사
이게 끝이다. 그냥 서버의 허락한 Origin 중에 자기도 포함되는지 검사하는 것이다.
CORS 요청 유형
CORS 요청 유형은 크게 3가지로 나뉜다.
1. 프리플라이트 요청
2. 단순 요청
3. 인증 정보를 포함한 요청
프리플라이트 요청
CORS 정책에서는 어떤 조건에 따라서 안전한 요청인지 아닌지를 판단하는데 안전하지 않은 요청의 경우 서버에 실제 요청을 보내기 전 예비 요청인 '프리플라이트 요청'을 보내서 실제 요청을 보내기에 안전한 상황인지를 확인한다.
그림을 보면 프리플라이트 요청에 OPTIONS, Origin, Access-Control-Request-Method, Access-Control-Request-Headers 가 있는데 각각 무엇인지 살펴보자.
OPTIONS : 프리플라이트 요청의 HTTP 메서드
Origin : 요청을 보내는 Origin
Access-Control-Request-Method : 실제 요청의 HTTP 메서드
Access-Control-Request-Headers : 실제 요청에 사용할 헤더들
반대로 프리플라이트 요청에 대한 서버의 응답 헤더에 들어가는 내용들은 아래와 같다.
Access-Control-Allow-Origin : 허용되는 Origin 목록
Access-Control-Allow-Methods : 허용되는 메소드 목록
Access-Control-Allow-Headers : 허용되는 헤더 목록
Access-Control-Max-Age : 프리플라이트 요청에 브라우저에 캐시 될 수 있는 시간(초 단위)
이렇게 프리플라이트 요청을 보내고 서버로부터 응답을 받으면 브라우저는 CORS 정책에 따라 서버에서 보내 준 허용 목록들에 본인의 Origin 및 보내려고 하는 메서드나 헤더들이 포함되어 있는지를 확인하고 이상이 없으면 그 때 실제 요청을 서버에 보내게 된다.
요즘은 일반적으로 json 타입으로 데이터를 주고 받는데 json 데이터는 안전하지 않은 요청이라 프리플라이트 요청을 보내게 되므로 잘 알아두어야 한다.
단순 요청
단순 요청은 안전한 요청으로 프리플라이트 요청이 필요가 없다. 그렇기에 여러 조건들을 만족하는 경우에만 단순 요청이 될 수 있다. 관련된 정보들은 여기서 확인이 가능하다.
단순 요청은 이미 안전하다고 보장이 된 요청이기에 서버와 요청/응답을 주고 받을 때 헤더에 Origin 정보만을 담아 서버의 허가 목록 중 자신의 Origin 이 포함됐는지를 확인하는 것으로 검사를 마친다.
인증 정보를 포함한 요청
인증 정보가 필요한 서비스들(로그인 시에만 이용가능한 것들)에서는 클라이언트 쪽에서 쿠키나 혹은 Authorization 헤더에 토큰 값을 담아서 서버에 요청을 보내게 된다. 이런 요청들은 위 2가지 요청과 달리 별도로 따라줘야 하는 CORS 정책이 존재한다.
클라이언트 단에서는 axios 를 사용할 경우 withCredentials 옵션을 true로 설정해줘야 하고
서버 쪽에서는 응답의 Access-Control-Allow-Origin 헤더에 모든 것을 다 허용한다는 와일드 카드(*) 가 아니라 정확히 어떤 Origin 들을 허용할 것인지 명시해주어야 하며 Access-Control-Allow-Credentials 헤더는 true 로 설정이 되어야 한다.
좀 더 알아볼 점
과거에 서버사이드렌더링 방식을 통해서 웹서비스를 제공할 때는 같은 Origin 끼리 데이터를 주고 받았는데 점점 클라이언트 사이드 렌더링이 등장하면서 CORS 정책도 등장한 것 같다.
그런데 궁금한 것은 결국 실질적으로 서로 다른 Origin 간의 요청은 '클라이언트 서버'와 '백엔드 서버' 사이에서 일어나는 일인데 왜 이에 대한 검사를 브라우저 쪽에서 하는 것으로 했을까? 좀 더 찾아보면서 답을 얻을 수 있도록 노력해봐야겠다.
'항해99' 카테고리의 다른 글
[TIL] DAY 43 (0) | 2023.05.15 |
---|---|
[WIL] Week 6 (0) | 2023.05.14 |
[TIL] DAY 34 (1) | 2023.05.07 |
[TIL] DAY31 (0) | 2023.05.03 |
[TIL] DAY 29 (0) | 2023.05.01 |