본문 바로가기

트러블슈팅

Access to XMLHttpRequest at '요청 url' has been blocked by CORS policy 에러.

문제

로컬 환경에 Vue 프로젝트와 Spring 프로젝트를 모두 실행시키고, Vue에서 Spring에 API를 호출하려 했다.

- Vue 프로젝트의 URL: http://localhost:8080

- Spring 프로젝트의 URL: http://localhost:20101

 

API를 호출할 때 아래와 같은 CORS 문제가 났다.

CORS(Corss-Origin Resource Sharing) 란?

CORS는 한국어로 교차-출처 리소스 공유이다. 쉽게 말하면 다른 출처로부터 가져온 리소스를 공유하는 것이다.

출처(Origin) 란?

아래는 URL 구조이다. 출처는 URL 구조에서 Protocol, Host, Port 를 의미한다. 즉, 서버의 위치를 찾아가기 위한 가장 기본적인 구성이다.

 

브라우저 콘솔에서 'location.origin'을 실행하면 출처를 확인할 수 있다.

출처 동일 여부

웹페이지 주소: https://one.example.com/ 

참고로, 웹 페이지 주소의 포트 번호는 프로토콜이 https 이므로 기본적으로 443이다. http 프로토콜은 80이다.

 

Post, Host, Port 중 하나라도 다르면 다른 출처이다.

URL 결과 이유
https://one.example.com/list 같은 출처 (Same Origin) Protocol, Host, Port 동일
https://one.example.com/list?q=test 같은 출처 (Same Origin) Protocol, Host, Port 동일
http://one.example.com 다른 출처 (Cross Origin) Protocol 다름
https://two.example.com 다른 출처 (Cross Origin) Host 다름
https://one.example.com:80 다른 출처 (Cross Origin) Port 다름

 

CORS의 동작 원리

출처를 비교하는 로직은 서버 단이 아니라 브라우저 단에서 이루어진다.

브라우저가 CORS 정책을 위반하는 리소스를 요청하더라도, 서버 단에서 같은 출처에서 온 요청만 받겠다는 설정을 따로 해둔 것이 아니라면 일단 정상적으로 응답한다. 그 후 브라우저가 응답을 분석해 CORS 위반이라 생각하면 그 응답을 버린다.

 

브라우저에서 서버로 리소스 요청을 HTTP 프로토콜을 이용해 보낼 때, 요청 헤더origin을 넣어서 보낸다.

이후 서버가 이 요청에 대한 응답을 할 때, 응답 헤더에 Access-Control-Allow-Origin 라는 값으로 이 리소스에 접근하는 것이 허용된 출처를 같이 보내준다. 응답을 받은 브라우저는 자신이 보낸 Origin과 서버가 보낸 Access-Control-Allow-Origin을 비교한 후 이 응답이 유효한지 판별한다.

 

문제 원인

결론부터 말하자면, 서버인 Spring Project에 CORS 설정이 되어있지 않았다.

서버에서 응답 헤더에 Access-Control-Allow-Origin 값을 설정해주는 부분이 없으니, 브라우저에서 서버가 보내준 응답 헤더를 보면 Access-Control-Allow-Origin이 보이지 않는다. 

브라우저 콘솔에 나온 에러 메시지도 No 'Access-Control-Allow-Origin' header is presont on the requested resource. 였다. 즉, 요청한 Resource에 Access-Control-Allow-Origin 헤더가 없다란 뜻이다.

문제 해결

우선, Spring Project에 아래와 같이 CORS 설정을 추가해줬다.

브라우저가 받은 응답 헤더에 Access-Control-Allow-Origin이 생긴 것을 확인할 수 있다.

아래와 같은 새로운 에러가 나타났다. Access-Control-Allow-Origin 응답 헤더는 와일드카드인 *를 사용할 수 없다고 한다. Credintials 속성으로 credential mode를 제어할 수 있다고 한다.

 

서버에서 Access-Control-Allow-Origin 값을 세팅할 때 *가 아니라 요청 헤더의 Origin이 되도록, 아래와 같이 서버의 CORS 설정 부분을 수정하였다.

  • allowedOrigins로 허용되는 요청 헤더의 Origin을 설정한다.
  • allowedMethods로 요청 시 허용되는 method를 설정한다.
  • allowedCredentials는 서버가 Access-Control-Allow-Credentials 헤더를 리턴하는지 여부를 제어하는 것이다.

 

서버의 CORS 설정 변경 후, 브라우저에서 다시 요청을 보내보면 응답 헤더의 Access-Control-Allow-Origin 값이 *에서 요청 헤더의 Origin으로 변경된 것을 확인할 수 있다. 또한, Access-Control-Allow-Credentals 헤더 값이 true로 추가된 것을 확인할 수 있다.

 

참고 자료

https://velog.io/@jh100m1/CORS-%EC%97%90%EB%9F%AC%EA%B0%80-%EB%AD%94%EB%8D%B0-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%B4%EA%B2%B0%ED%95%98%EB%8A%94%EA%B1%B4%EB%8D%B0

 

CORS 가 뭔데 CORS 에러 어떻게 해결하는건데

눈물의 TIL 되시겠다. CORS.. 공부해야하지만 하기 싫어서 미뤘다가 오픈API 쓰면서 만난 CORS 에러 덕분에 개발은 잠시 미뤄두고 CORS의 개념부터 해결까지 공부해보려고 한다. 원리가 뭔데... 너 뭔

velog.io

https://www.ibm.com/docs/ko/sva/10.0.4?topic=stanza-allow-credentials 

 

allow-credentials

allow-credentials 항목은 리버스 프록시가 클라이언트에 Access-Control-Allow-Credentials 헤더를 리턴하는지 여부를 제어합니다. 구문 allow-credentials = {true, false} 설명 이 정책으로 보호되는 자원에 액세스할

www.ibm.com