프론트엔드 개발을 하다 보면 백엔드 서버에 요청을 보냈을 때, 콘솔창에 빨갛게
CORS
에러가 뜨는 것을 자주 목격하게 된다.CORS
를 대응하는 방법을 알지 못한다면, 클라이언트가 에러를 해결하기 위해 여러가지 다양한 시도를 해봐도 원하는대로 동작하지 않아 난감하기 짝이 없을 것이다. 클라이언트에서도 작업을 해줘야 되지만, 근본적인 문제 해결은 서버에서 해결 해야한다.
CORS
를 알기 전에 SOP
에 대해 알아야 한다. SOP
는 Same Origin Policy
의 약자로, 해석하면 동일 출처 정책이다. 여기서 말하는 출처란 url
의 scheme, host, port 까지를 말한다. 따라서, 동일 출처라는 것은 요청을 보내는 url
의 scheme, host, port
과 요청을 보내는 url
의 scheme, host, port
가 같은 것을 말한다.
http://localhost:80
http://localhost:80/login
위의 두 url
은 path
만 다르므로 same origin
이다.
이전에 서버에 요청을 보내서 html 정적파일
을 받았을 때는, 클라이언트와 서버의 url이 당연히 같을 수 밖에 없었다. 그렇기 때문에, 클라이언트와 서버의 출처가 다르면 외부의 해킹일 가능성이 높다고 생각했다.
다른 출처에 대한 제재가 없다면 원래 클라이언트 url
의 쿠키 정보를 통해 원래 요청하려던 서버의 중간에 다른 서버를 거쳐서 클라이언트의 정보를 악용할 수 있는 가능성이 있다. 그래서 두 url의 출처가 다른 경우에 제재를 가할 필요가 있었고, 그 결과 동일 출처 원칙인 SOP
가 생겨난 것이다.
이후 웹에서 html
파일을 정적으로만 받지 않고, 웹 애플리케이션을 만들기 시작하면서 백엔드 서버 말고 다른 url에도 요청을 보낼 일이 생기기 시작했다. 다른 누군가 만든 서버의 url은 내가 만든 클라이언트의 url과 당연히 다르므로 응답을 받을 수 없었다.
그래서 지금은 잘 사용되지 않지만 한동안 JSONP
라는 script태그를 사용하여 우회하는 방법으로 응답을 받는 처리를 했다. 이후에 결국 등장하여 우회하는 방법이 아닌 공식적인 방법으로 다른 출처의 요청을 허용하게 해주는 것이 CORS(Cross Origin Resource Sharing
이다. CORS
에러 메시지를 보고 CORS
를 에러라고 생각할 수 있지만, 에러를 유발하는 것은 SOP
이고 CORS
는 에러가 아니라 에러를 해결하는 방법이다.
프론트엔드에서 CORS
문제를 해결하기 위해 프록시 서버를 통해 우회하는 방법이 있긴 하지만, 근본적인 해결책은 아니다. 백엔드 서버에서 다른 출처의 접근도 허용하게 하기 위해서는 접근을 허용해줄 다른 출처에 대해 명시해주면 된다.
서버가 보내는 response
에는 Access-Control-Allow-Origin
이라는 값이 있는데, *
를 값으로 주면 모든 url
에서 접근이 가능하고, 아니면 프론트엔드
와 사전에 협의한 url
을 값으로 주면 된다. 백엔드 서버를 위해 사용하는 대부분의 라이브러리에서는 CORS
문제에 대응하기 위한 방법들이 이미 마련되어 있기 때문에, 공식문서를 보고 비교적 손쉽게 문제를 해결할 수 있다.
브라우저는 서버에 request
를 보낼 때, 다른 출처의 요청일 경우에 header
에 origin
이라는 항목을 추가한다. 서버는 response
에 브라우저로 부터 받은 origin
과 위에서 말한 Access-Control-Allow-Origin
을 같이 헤더에 보낸다. 서버는 동일 출처를 검사하지 않고 응답을 보낸다.
origin
과 Access-Control-Allow-Origin
을 검사하는 것은 서버가 아닌 브라우저의 역할이다. 두 출처를 검사하여 SOP
에 위배되면 브라우저는 response
의 body
의 값을 공개하지 않고, CORS
에러를 발생시킨다.
동일한 origin
의 경우에는 http통신을 하면 쿠키
가 요청 header
에 자동으로 포함된다. 그런데 origin
이 다른 경우에는, 자동으로 포함되지 않기 때문에 전송하기 위해서는 직접 별도로 넣어줘야 한다. 프론트엔드는 header
에 credentials: true
를 추가해주고, 백엔드 서버는 response
의 header
에 Access-Control-Allow-Credentials: true
를 추가해주면 된다.
일반적인 요청의 경우에는 위와 같이 요청을 보내고 이를 simple request
라고 한다. 일반적이지 않은 요청
이라고 간주되는 것은 꽤 복잡한데, 주로 요청 메서드가 GET, POST, HEAD
가 아닌 요청들이다. 이런 요청들은 보내기 전에 요청을 보낼 수 있는지 확인하는 사전 요청을 보내는데 이를 preflight request
라고 한다. preflight
요청은 브라우저가 자동적으로 생성하기 때문에 별도로 생성할 필요가 없고 사전 요청을 통해 요청을 보낼 수 있는지 서버에 확인하고, 원래 요청을 보낸다.