CORS는 Cross-Origin Resource Sharing의 약자로 교차 출처 리소스 공유라고 불린다.
CORS를 이해하기 위해선 SOP(Same Origin Policy)를 먼저 이해해야 한다. SOP는 말 그대로 같은 출처에서만 리소스를 공유할 수 있는 정책을 말한다. 하지만 웹에서 다른 출처의 리소스를 가지고 오는 상황은 굉장히 흔한 상황이라 다른 출처의 리소스를 무작정 막아버리면 큰 불편함을 야기하게 된다. 그래서 몇가지 예외를 두고 리소스의 출처가 다르더라도 요청을 허가해주도록 했는데 그 예외가 바로 CORS 정책을 지킨 요청을 말한다.
즉, 다른 출처의 리소스를 요청하려면 반드시 CORS 규칙에 맞게 요청을 해야 응답을 받을 수 있다.
그렇다면 자료의 출처(ORIGIN)는 무엇일까?
위 그림에서 보듯이 URL은 여러가지 구성요소로 이루어져 있다. 같은 출처라 함은 여기서 PROTOCOL, HOST 그리고 HOST뒤에 붙는 포트번호가 같은걸 말한다.
예시
1. HTTP와 HTTPS 는 서로다른 출처이다.
2. HOST의 주소가 다르면 다른 출처이다.
기본적으로 웹 클라이언트 어플리케이션이 다른 출처의 리소스를 요청할 때는 HTTP 프로토콜을 사용하여 요청을 보내게 된다. 이 때 브라우저는 요청 헤더에 ORIGIN 이라는 필드에 요청을 보내는 출처를 함께 담아 보낸다. 이후 서버가 이 요청에 대한 응답을 할 때 응답 헤더의 Access-Control-Allow-Origin이라는 값에 허용된 출처를 내려주고 응답을 받은 브라우저는 자신이 보낸 요청의 ORIGIN과 서버가 보내준 응답을 비교해보고 응답이 유효한지 확인한다.
Preflight Request
이 방식은 브라우저가 요청을 보낼 때 한번에 보내지 않고 미리 요청을 한번 보내는 작업을 거친뒤 본 요청을 보내는 방식을 말한다. 예비 요청을 보내기 때문에 preflight라고 불리며 HTTP 메소드 명은 OPTINS를 사용한다.
위와같이 예비요청을 보낸뒤 본요청을 보내는 방식이다.
Simple request
단순 요청이라 불리는 이 방식은 예비 요청 없이 한번에 요청을 보내는 방식이다. 예비 요청만 없을 뿐 전체적인 로직은 같다.
단순 요청은 아무때나 할 수 있는게 아니고 특정 조건을 만족시킬 경우에만 사용할 수 있는데 그 조건은 다음과 같다.
1. 요청의 메소드는 GET, HEAD, POST 중 하나여야 한다.
2. Accept, Accept-Language, Content-Language, Content-Type, DPR, Downlink, Save-Data, Viewport-Width, Width를 제외한 헤더를 사용하면 안된다.
3. 만약 Content-Type를 사용하는 경우에는 application/x-www-form-urlencoded, multipart/form-data, text/plain만 허용된다.
조건을 살펴보면 굉장히 까다로운 것을 알 수 있다. 당장 사용자 인증에 해당하는 Authoriztion 헤더도 포함되지 않고, 우리가 흔하게 사용하는 application/json방식도 포함되지 않는다. 따라서 우리는 예비요청 방식을 가장 흔하게 사용하게 된다.
위에서 말한 것 처럼 Access-Control-Allow-Origin 헤더에 알맞은 값을 세팅해 주면 된다. 물론 여기엔 모든 걸 허용하겠다는 * 와일드카드를 사용할 수도 있다. 하지만 이렇게 되면 모든 요청을 받아들이겠다는 뜻이 되므로 권장하지 않는다.(의도치 않은 공공api가 되버린다.) 따라서 Origin뒤에 출처를 정확히 적는 방법을 사용해야 한다.
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
// - (3)
configuration.addAllowedOrigin("*");
configuration.addAllowedMethod("*");
configuration.addAllowedHeader("*");
configuration.addExposedHeader(HttpHeaders.AUTHORIZATION);
//configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
! 위와 같이 와일드카드 사용은 안된다.
CORS는 결국 브라우저의 구현 스펙이기 때문에 이 문제를 겪는 사람들은 대부분 프론트엔드 개발자들이다. 그러나 이 문제를 해결하기 위해서는 백엔드 개발자가 서버 어플리케이션의 응답헤더에 올바른 출처를 명시해 줘야 한다. 따라서 개발자 간의 소통을 잘해야 위의 문제를 잘 해결 할 수 있다. 스프링부트의 경우 몇줄의 코드로 CORS를 해결 할 수 있으니 좀더 찾아보고 문제를 잘 해결해야 겠다.