프론트엔드 개발을 하다보면 서버로 API를 요청시 크로스 도메인 이슈를 한번씩은 마주하게 되는것 같다. 그래서 CORS에 대해서 보고자 한다.
XMLHttpRequest
XMLHttpRequest는 비동기로 서버와 통신한다. 그런데 XMLHttpRequest는 이름으로 오해의 소지가 있다. 마치 XML 데이터만 주고 받아야 할 것 같고, HTTP 프로토콜만 이용해야 하는 느낌을 준다. 하지만 실제로는 JSON, HTML로도 결과로 받을 수도 있고, HTTP 이외 다른 프로토콜(file, ftp)도 지원한다.
XMLHttpRequest 객체는 기본적으로 SOP(Same Origin Policy: 동일 출처 정책)의 제약을 받는다. 한 도메인에서 데이터를 요청하면 다른 도메인의 데이터를 요청할 수 없다는 것을 의미한다. 이를 크로스 도메인 이슈라고 한다.
CORS는 크로스 도메인 이슈를 해결하는 표준이라고 할수 있겠다. CORS를 사용하기 위해서는 클라이언트와 서버사이에 몇가지 정보를 주고 받아야 한다. 클라이언트는 CORS 요청을 위해 새로운 HTTP 헤더를 추가하고, 서버는 클라이언트가 전송한 헤더를 확인하고 요청을 허용 여부를 결정한다.
CORS header 규격
Request Headers (클라이언트 요청 헤더)
Origin: (도메인) 요청 보내는 페이지
Access-Control-Request-Method : 실제 요청 때 사용할 메소드
Access-Control-Request-Headers : 실제 요청때 헤더에 추가할 커스텀 속성
Response Header (서버 응답 헤더)
Access-Control-Allow-Origin : 허용하는 Origin ( * 이면 모든 곳에 공개함을 의미)
Access-Control-Allow-Credentials : 요청이 쿠키를 통해 자격 증명을 해야 하는 경우 true. true를 응답 받은 클라이언트는 실제 요청시 서버에 정의된 인증값이 담긴 쿠키를 같이 보내야 함.
Access-Control-Expose-Headers: 클라이언트 요청에 포함 가능한 사용자 정의
Access-Control-Allow-Headers : 요청을 허용하는 헤더 속성
Access-Control-Request-Methods : 허용하는 요청 메소드
Access-Control-Max-Age : preflight 요청 결과를 저장 할 시간(초 단위)
Simple Request
사이드 이펙트를 일으키지 않는 GET, HEAD, POST 요청을 말한다. POST 요청의 경우에는 서버로 전송하는 Content-Type이 application/x-www-form-urlencoded, multipart/form-data, text/plain 중에 하나여야 한다. 이 때는 HTTP 요청에 커스텀 헤더를 지정하지 않는다.
Preflight Request
GET, HEAD, POST 외 요청은 데이터 사이드 이펙트를 만들 수 있다. CORS 스펙에 따라 클라이언트에서 preflight 요청을 먼저 서버로 전송해야 한다. POST 요청이지만 Content-Type이 application/x-www-form-urlencoded, multipart/form-data, text/plain이 아닌 경우도 여기에 해당한다.
Credential Request
표준 CORS는 기본적으로 요청을 보낼 때 쿠키를 전송하지 않는다. 쿠키를 요청에 포함하고 싶다면 XMLHttpRequest 객체의 withCredentials 속성 값을 true로 설정한다.
withCredentials = true
Non-Credential Request
withCredentials 플래그는 디폴트 값이 false 이다. 위 Credential Request와 같이 처리하지 않는다면 모든 요청은 Non-Credential에 해당된다.
모든 외부 도메인 요청을 허용할 경우
preflight 요청을 받기 위해 OPTIONS 메서드 요청을 받아서 컨트롤 해야 한다. 모든 요청의 응답을 아래 header를 추가한다.
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: Origin,Accept, X-Requested-With, Content-Type,Access-Control-Request-Method, Access-Control-Request-Headers, Authorization
preflight 요청 응답으로 Access-Control-Allow-Origin에 값이 * 이면 모든 도메인의 요청을 허용하는 것이며, 실패하면 Access-Control-Allow-Origin 헤더가 없어서 요청이 허용되지 않는 것이다.