본문 바로가기

관리하지않음/대학과제

크롬 CORS 에러 해결방법

크롬 브라우저에서 Javascript로 API 데이터를 가져올 때 발생한 CORS 에러 해결방법을 정리했습니다.

발생한 에러를 찾아서 적었고, 맨 마지막에 해결한 방법이 있습니다.


두달전쯤 공적마스크 API를 활용한 웹페이지를 만들었습니다.

그 때는 로컬에서 xmlHttpRequest를 사용해도 데이터가 잘 받아와져서 문제없이 빠르게 구현했습니다.

그래서 아무 걱정없이 기능 구현만 하고 제출했었는데, 최근 다른 API를 사용해서 웹페이지를 만들던 중 CORS policy 에러가 발생했습니다.

 

CORS policy 에러 화면

정확히는 Access to XMLHttpRequest at 'URL 주소' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. 에러였습니다.

 

origin이 'null'이라는건 뭐고, 'Access-Control-Allow-Origin' header가 없다는건 또 뭔지 찾아봤습니다.

 

에러 1. Access to XMLHttpRequest at 'URL 주소' from origin 'null' has been blocked by CORS policy.

크롬의 개발자모드>Network를 열어보니 Requests Headers에서 origin값이 null로 보내진걸 확인했습니다.

 

에러 1

그럼 이 origin 값을 바꾸면 되지 않을까 해서 검색했는데요.

많은 검색 끝에 아래의 stackoverflow 링크에서 찾은 것은,

 

Origin null is not allowed by Access-Control-Allow-Origin

 

"크롬에서 로컬로 파일을 열어서는 해결할 수 없다." 입니다. (첫번째 Answer를 보면 됩니다.)

브라우저마다 다르지만, 크롬은 SOP(Same Origin Policy)가 엄격해서 file://로 열때 해결할 수 없다고 합니다.

물론 크롬 확장프로그램을 사용하거나, jQuery를 사용해서 jsonp 라는걸 이용하거나, 크롬을 "--allow-file-access-from-files" 옵션과 함께 열면 가능하다고는 하는데요,

문제는 위 방법이 모두 꼼수이고, 과제를 채점하는 교수님께는 통하지 않는 방법이라는 점입니다.

에러 2. No 'Access-Control-Allow-Origin' header is present on the requested resource

그럼 Origin 'null'은 로컬 시스템으로는 해결할 수 없는 문제라는걸 알았습니다.

그다음에 발생한 'Access-Control-Allow-Origin' 헤더가 없다는건 뭔지 알아봤습니다.

 

에러 2

Response Headers를 보면 'Access-Control-Allow-Origin' 헤더가 정말 없습니다.

 

의문점 1. 공적마스크 API는 왜 로컬 시스템에서 사용할 수 있었을까?

정답은 아래 캡쳐화면에 있었습니다.

 

 

'Access-Control-Allow-Origin : *' 헤더가 있는게 보이시나요?

공적 마스크 API 서버에서는 위 헤더가 있어서 origin 헤더가 'null' 이어도 데이터가 받아와졌습니다.

 

결론 1.  API 서버에서 'Access-Control-Allow-Origin : *'을 추가해주지 않는 이상, 크롬에서 "file://" URL로 API 데이터를 받아올 수 없다.

1인 개발자가 무슨 권한이 있어서 API 서버의 코드를 변경할 수 있겠습니까...

 

결국 Origin 값을 변경해야 위 에러를 해결할 수 있다는 결론이 나왔습니다.

Origin이 null이 되는 로컬 시스템은 빼고, 서버를 하나 만들고 HTML 파일을 실행해야 한다고 생각했습니다.

그런데 여기서 궁금한 점이 생겼습니다.

 

의문점 2. Origin에 값만 넣어주면 되는걸까?

의문점을 해결하기 위해서 bitnami로 깔아둔 Apache Web Server에서 html 파일을 실행했습니다.

의문점 2

 

사진을 보시면 아시겠지만, 아파치 서버 위에서 HTML 파일을 돌린다고 에러가 해결되지 않았습니다.

Origin이 null 값이었을 때와 똑같이 CORS 에러가 발생합니다.

Header를 확인해보니 Origin 과 Referer 의 주소가 다릅니다. 이래서 CORS 에러가 똑같이 발생했습니다.

 

서버 위에서 돌린다고 간단히 문제가 해결되는 문제가 아니었습니다.

 

결론 2. 서버에서 API 데이터를 받아서 포워딩을 해주고, 포워딩 한 데이터를 다시 받아와야한다.

 

결론 2까지 오는데도 숨이 꼴딱 넘어갈 뻔 했습니다.

하루 왠종일 삽질해서 화가나려는 순간, 공공데이터포털에서 PHP 코드를 제공하는걸 발견했습니다.

 

공공 데이터 포털에서 제공하는 샘플코드들

역시 하늘이 무너져도 솟아날 구멍이 있다는 표현은 이럴때 쓰는게 아닌가 싶습니다.

아파치 서버에서 api.php 파일을 만들고 코드를 복붙했습니다.

<?php
$ch = curl_init();
$url = 'http://61.43.246.153/openapi-data/service/busanBIMS2/busInfo'; /*URL*/
$queryParams = '?' . urlencode('ServiceKey') . '=서비스키값'; /*Service Key*/
$queryParams .= '&' . urlencode('lineno') . '=' . urlencode('1002'); /**/

curl_setopt($ch, CURLOPT_URL, $url . $queryParams);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
$response = curl_exec($ch);
curl_close($ch);

var_dump($response);
?>

 

그리고 HTML 파일에서 다음과 같이 localhost:8080/api.php 에서 데이터를 가져왔습니다.

<!DOCTYPE html>
<html>

<body>
  <button id="button">Button</button>
  <script>
    const button = document.getElementById("button");
    button.addEventListener('click', show);
    var xhr = new XMLHttpRequest();
    xhr.addEventListener("readystatechange", function() {
      if (this.readyState === 4) {
        console.log(this.responseText);
      }
    });

    function show() {
      xhr.open("GET", "http://localhost:8080/api.php");
      xhr.send();
    }
  </script>
</body>

</html>

 

실행 결과, CORS 에러 없이 데이터를 잘 가져오는 걸 확인했습니다.

코드 실행 결과

여기까지 오는데 이틀정도 걸렸습니다.

웹 개발을 소홀히 해온 저로써는 정말 고통의 시간이었지만 결국 해결했습니다.

+ string(932) 값은 php 코드 때문에 넘어온 값인거 같은데, php를 몰라서 내비뒀습니다.

 

해결방법. 아파치 서버에서 PHP 코드로 데이터 포워딩을 했다.

많이 사용하는 웹서버 샘플 코드를 공공데이터 포털에서 제공해주고 있어서, 확인하고 사용하면 될 것 같습니다.

php 코드는 안짠지 오래여서,, 저는 해결방법을 알았으니 요새 공부하고있는 Django로도 짜보려고 합니다.

비슷한 문제는 많으나, 구글에 검색해도 잘 안나와서 제 방법을 공유합니다.


잘못된 내용이 있다면 언제든지 댓글이나 메일로 알려주시면 감사하겠습니다.

이 포스팅이 도움이 되었다면 공감 부탁드립니다.

궁금한 점은 언제든지 댓글 남겨주시면 답변해드리겠습니다:D