가장 간단한 예시로는 서로 다른 두 로컬서버에서
한 서버에서 다른 서버로 GET, POST 같은 요청을 보냈을 때 응답을 핸들링하려고하면
Access to fetch at 'url' from origin 'url2' has been blocked by CORS policy : No 'Access-Control-Allow-Origin' header is present on the requested rescource. If an opaque respose serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
이와 같은 에러를 볼 수 있다.
이는 HTTP 요청에 대해서 어떤 요청을 하느냐에 따라 다른 특징을 가지고 있기 때문인데
HTML 은 기본적으로 Cross-Origin 정책을 따르고 link 태그에서 다른 origin의 소스에 접근이 가능하다.
하지만 XMLHttpRequest, Fetch API 등 script 태그 내에서는 Same-Origin 정책을 따르므로 자바스크립트는 서로 다른 도메인에 대한 요청을 보안상 제한하고 있어서 오류가 나는 것이다.
출처란 서버의 위치를 의미하는 url을 보면
http://www.sample.com:3001/login?query=test#me
순서대로
protocol, host, port, path, query string, fragment 를 의미하고
출처는 이중에서 protocol + host + port 까지 합친 것을 의미한다.
console.log(location.origin); // http://www.sample.com:3001
클라이언트에서 HTTP 요청의 헤더에 Origin을 담아 전달.
서버는 응답헤더에 Access-Control-Allow-Origin을 담아 전달.
클라이언트에서, 자신이 보냈던 요청의 Origin과 서버가 보낸 Access-Control-Allow-Origin을 비교.
3가지의 시나리오가 있지만 아래 사이트를 참조하여 작동방식을 확인하고 이 글에서는 내가 가장 많이 사용한 단순 요청만 다뤄보겠다.
먼저 요청 메소느는 GET, HEAD, POST 중 하나여야 하고 유저 에이전트가 자동으로 설정한 헤더외에 수동으로 설정 가능한 헤더는 Fetch 명세에 CORS-safelisted request-header로 정의한 것만 사용가능하다.
크롬에서는 CORS 문제를 해결하기 위한 확장 프로그램을 제공하는데
'Allow CORS: Access-Control-Allow-Origin을 설치하고 활성화하면, 로컬환경에서 API를 사용할때 CORS 문제를 해결할 수 있다.
하지만! 모든 사용자가 해당 브라우저에서 다음과 같은 작업을 하고 사용하는 경우는 없기 때문에 서버쪽에서 작업을 해주는 것이 낫다고 생각한다.
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', 'true'); //쿠기 주고 받기 허용
이와 같이 *을 사용하면 모든 Origin에서 오는 요청을 허용하므로 보안상으로는 취약하다.
res.setHeader('Access-Control-Allow-Origin', 'http://www.sample.com');
이렇게 출처를 명시해주면 좋다.
이 헤더는 Nginx나 Apache 같은 서버 엔진의 설정에서도 추가 가능하지만 소스 코드 내에서 응답 미들웨어 등을 사용하여 세팅하는 것이 비교적 간단하다.
//헤더에 작성된 출처만 브라우저가 리소스 접근 허용
Access-Control-Allow-Origin : url
//리소스 접근을 허용하는 http 메서드 지정
Access-Control-Request-Methods: GET, POST, PUT, DELETE
//서버에서 응답 헤더에 추가해 줘야 브라우저의 자바스크립트에서 헤더에 접근 허용
Access-Control-Expose-Headers: Authorization
//60초 동안 preflight 요청을 캐시하는 설정
Access-Control-Max-Age : 60
// js 요청에서 credentials 가 inclue일때 요청에 대한 응답을 할 수 있는지
Access-Control-Allow=Credentials: true