본문 바로가기

웹/JavaScript

[JavaScript] Access to fetch at... has been blocked by CORS policy 오류 해결 방법

 

개요

API 등을 불러와 사용할 때 아래와 같은 문구와 함께 오류가 발생하는 일이 있다. 

'api를 호출한 주소'의 원본에서 'api의 데이터를 보내주는 주소' 가져오기에 대한 액세스가 CORS 정책에 의해 차단되었습니다. : 요청된 리소스에 'Access-Control-Allow-Origin' 헤더가 없습니다. 불투명한 응답이 요구 사항을 충족하는 경우 요청 모드를 'no-cors'로 설정하여 CORS가 비활성화된 리소스를 가져옵니다.

 

에러메시지를 보아하니

- CORS 정책이란 것 때문에 데이터 가져오는게 차단되었고

- Access-Control-Allow-Origin 라는 게 있어야 하고

- 요청 모드를 no-cors로 설정해서 가져올 수도 있다는 것 같다.


발생 원인

CORS에 앞서 먼저 알아야 하는 개념이 하나 있다.

 

SOP(Same-Origin Policy)

직역하면 동일 출처 정책.

악질적인 사용자가 자신 또는 취약점이 있는 남의 사이트에 악성스크립트를 심어넣는 등의 공격을 방지하기 위해 등장했다. 이름 그대로 브라우저는 같은 출처에서만 데이터를 받아올 수 있고, 다른 사이트 출처의 HTTP 요청을 제한한다. 

CORS는 다른 출처에서도 데이터를 가져올 수 있게 하기 위한 정책이다. 

 

 

 

 

CORS란? 

교차 출처 리소스 공유(Cross-Origin Resource Sharing)의 약자. MDN Docs에서는 이렇게 설명하고 있다. 

추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행합니다.

좀 알아듣기 어렵게 써있는데, domain-a.com 이라는 출처에서 실행되는 웹 애플리케이션이 다른 출처인 domain-b.com이나 anotherdomain.com에서도 리소스를 가져올 권한이 있는지 없는지를 말한다. 

 

여기서 '출처가 다르다'는건 구체적으로 URL의 스킴(프로토콜), 호스트(도메인), 포트 셋 중 하나 이상이 다르다는 걸 말한다.

URL의 구성: 스킴, 호스트, 포트, 패스, 쿼리, 프래그먼트

 

 

 


해결방법 

1. 로컬에서만 해결하면 될 때 쓸 수 있는 방법

Moesif Origin & CORS Changer라는 확장 프로그램을 설치하면 특별한 설정 없이도 쉽게 해결할 수 있다. 

설치한 후에 on으로 켜주기만 하면 된다. 로컬에서 간단히 테스트를 할 때 유용하다. 

 

하지만 단순히 나혼자 로컬에서 보고 말 페이지가 아니라면, 좀 더 근본적인 대책이 필요하다.

 

2. 근본적인 해결방법

이 오류는 기본적으로 서버사이드에서 설정을 해줘서 해결이 가능하다. 다만 오픈 API를 사용하는 등 직접 서버를 건드릴 수 없는 경우, 우회 서버(프록시 서버)를 이용해 해결할 수 있다.

 

1) 서버에서 해결하기

서버에서 Access-Control-Allow-Origin 헤더 설정으로 SOP를 우회할 주소를 세팅해주면 된다. 주소란에 *를 넣으면 모든 주소를 우회할 수 있으나, SOP가 없는거나 마찬가지가 되어 보안 상 위험하므로 삼가도록 하자. 

app.get('/', function(req, res) {
    res.header("Access-Control-Allow-Origin", "www.domain-a.com");
    .....
})

 

2) 프록시 서버를 통해 우회하기

데이터를 보내는 서버 자체를 제어할 수 없는 경우, 우회 서버를 만들어서 해결할 수 있다. SOP는 서버-클라이언트의 관계일 때 발생하고 서버-서버 간의 데이터 이동에선 발생하지 않기 때문이다. 

내 경우엔 herokucors-anywhere의 코드를 포크한 프록시 서버를 만들어서 우회하는 방법으로 해결했다. 헤로쿠 배포까지의 자세한 과정은 이곳을 참고했다.

 

cors-anywhere 사용 시 '프록시 서버 주소 뒤에 API요청 주소를 합친 주소'로 데이터를 요청하면 된다. 

const cors_api_host = '프록시 서버 주소';
const api_link = 'API 요청 주소';
const cors_api_url = `${cors_api_host}${api_link}`; 
        
fetch(cors_api_url).then(/*이하생략*/)

콘솔에 출력해보면 무사히 데이터가 찍힌다.

 


참고 자료

- 모질라 web docs: CORS

- hunjison.log: SOP, CORS는 보안에서 왜 필요할까?

- CORS는 왜 이렇게 우리를 힘들게 하는걸까?

- https://xiubindev.tistory.com/115