CORSPolicy

엥???

1. 발단

저는 2023년도 2학기 복학 예정입니다. 그래서 2학기에 들을 수업들을 수강 신청해야 되는데, 저는 그동안 학교에서 짜준 수업대로 듣고 살았기 때문에, 이번 수강 신청이 처음입니다…

그래서 누구보다도 수강신청에 숙달되기 위해 수강신청 연습 사이트를 만들기로 했습니다. 프론트 엔드로 웹 사이트를 제작하던 중, 수업 정보에 대한 API가 있으면 정보를 입력하기에 용이하다고 생각해서, 학교에서 사용되는 API를 사용하기로 합니다1.

1
fetch(SchoolURL);

구글링 해서 위와 같은 코드가 API의 값을 불러오는데 사용된다고 알게 되었습니다. 이를 이용하면 쉽게 마무리 지을수 있다고 생각하던 찰나, 다음과 같은 에러 메세지를 만나게 됩니다.

1
Access to fetch at 'http://mojanapi.dothome.co.kr/API.php' from origin 'http://ide.goorm.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

어… CORS policy?

2. Cross-Origin Resource Sharing

CORS. 교차 출처 리소스 공유라는 뜻을 가진 이 정책은 서로 다른 도메인에 있는 웹 사이트(혹은 서버)의 리소스를 요청할때 관여하는 브라우저 정책중 일부입니다.

SameOrigin

예를 들겠습니다. mojan3543.github.io에서 mojan3543.github.io의 API를 불러오고자 합니다. 이는 전혀 무리 없이 동작합니다. 왜냐하면 이 두 웹사이트는 동일한 도메인에 있는 웹사이트이므로, SOP(Same-Origin-Policy)를 따르기 때문에, 리소스를 요청하는데에 아무런 제약을 받지 않습니다2.

CrossOrigin

하지만, 위와 같이 서로 다른 도메인을 가진 웹 사이트에서 리소스를 요청하는 경우에는 COP(Cross-Origin-Policy)를 따르게 되어, 제가 겪었던 것 처럼 CORS 정책을 위반했다고 에러 메세지가 발생할 겁니다.

같은 도메인이 아니면 리소스를 허용하지 않는 이 괴상한 쇄국정책3은 사실 사용자의 보안을 위해 만들어졌습니다. XSSCSRF와 같이, 원하지 않는 웹사이트, 스크립트를 공격 목적으로 강제로 실행시키는 취약점을 막습니다.

하지만, 저처럼 초보 웹 개발자들의 발목을 잡는 주 원인이기도 합니다. 이럴때는 어떻게 해결을 해야될까요?

2.1. 해결법

CORS 정책 위반을 해결하기 위한 해결법은 대략 3가지가 있습니다.

  1. 서버의 헤더 설정을 변경한다 : 리소스를 호출하는 서버의 헤더 설정을 변경하는 방법입니다. 친절하게도 CORS 에러 메세지에는 Access-Control-Allow-Origin 헤더가 없다고 알려줍니다. 이를 백엔드 서버의 언어에 맞는 헤더를 추가해주면 간단하게 해결됩니다.
  2. 프록시 서버를 사용한다 : 만약에 서버의 헤더를 변경할 수 없는 상황이라면, 서버의 응답을 대신 받아서 다시 보내주는 프록시 서버를 이용하면 됩니다4. 실제로 서버를 구축하거나, CORS 우회용 서비스 서버를 구독하여 사용합니다.
  3. CORS 정책 우회용 프로그램을 설치한다 : 모든 CORS 에러의 근원은 클라이언트인 브라우저이므로, 브라우저의 설정을 약간 변경하는것만으로 해결이 됩니다! 물론, 클라이언트에서 해결을 했으므로, 전문 용어로 It works on my machine ¯\_(ツ)_/¯을 시전해야 합니다…

2.2. 선택한 해결법

학교 수강 API를 불러와야 하는 제가 선택 할 수 있는건 2가지 입니다. 프록시 서버를 사용하거나, CORS 정책 우회용 프로그램을 설치하는 것인데, 후자의 경우에는 제가 만든 사이트를 저만 사용해야 하는 단점이 있으므로, 이김에 프록시 서버를 직접 만들어 보여 0이었던 백엔드 경험치를 올려보고자, 프록시 서버를 구축하기로 결심합니다.

3. 프록시 서버 만들기

프록시 서버는 서버 사이드 스크립트 언어를 이용하여 구현합니다. 다양한 서버 사이드 스크립트 언어 중에서 제가 선택한 언어는, 기존에 조금이나마 다뤄 보았고, 현재도 많은 곳에서 사용되고 있는 PHP를 이용하기로 했습니다.

그리고 서버 호스팅은 PHP를 지원하고, 무료 호스팅으로 진입 장벽도 적은 닷홈 호스팅으로 서버 호스팅을 받아 서버 개발을 진행하게 됩니다.

프록시 서버의 로직은 꽤나 간단합니다. PHP같은 경우에는 cURL을 이용해서 API 서버에 데이터를 전송하고, 받은 데이터를 그대로 Json 형태로 반환하면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
    $URL = "http://mojan3543.dothome.co.kr/API.php";

    $cURL = curl_init();
    // URL
    curl_setopt($cURL, CURLOPT_URL, $URL);
    // cURL Setting
    curl_setopt($cURL, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($cURL, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($cURL, CURLOPT_SSL_VERIFYPEER, false);
    // About GET
    curl_setopt($cURL, CURLOPT_CUSTOMREQUEST, "GET");

    $Result = curl_exec($PostCurl);

    echo $Result;
?>

이걸 기반으로 코드를 서버에 올리고, 첫 테스팅을 합니다.

MixedContent

좋은 뉴스네요! 새로운 오류입니다. Mixed Cotent. 구글링 해보니 http와 https와는 서로 리소스 호출을 할 수 없게 되어있다고 합니다. 그러면 URL을 $URL = "https://mojan3543.dothome.co.kr/API.php로 설정 하면 해결 되겠네요!

CORSPolicy

흠… 또 다시 CORS로 왔습니다. 사실 당연한 일입니다. 2.1. 해결법에서 설명 했듯이, Access-Control-Allow-Origin 헤더를 추가 해줘야 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
    header("Access-Control-Allow-Origin: *");

    $URL = "http://mojan3543.dothome.co.kr/API.php";

    $cURL = curl_init();
    // URL
    curl_setopt($cURL, CURLOPT_URL, $URL);
    // cURL Setting
    curl_setopt($cURL, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($cURL, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($cURL, CURLOPT_SSL_VERIFYPEER, false);
    // About GET
    curl_setopt($cURL, CURLOPT_CUSTOMREQUEST, "GET");

    $Result = curl_exec($PostCurl);

    echo $Result;
?>

다음과 같이 Access-Control-Allow-Origin: *로 설정을 해놓는다면, 아무 도메인으로 리소스 요청을 하더라도 정상적으로 동작합니다! 이렇게 다시 설정을 하고 실행해줍니다.

CORSPolicy

??? 또 다시 CORS 오류입니다. 제가 뭔가 잘못 설정했을까요? 아니면 뭐가 문제일까요…

NoHTTPS

아파치 헤더 설정도 건드려 보고, 방화벽도 설정했으나 여전히 미궁속으로 빠진채, 고객센터에 방화벽 관련 문의를 드렸습니다. Response 헤더를 같이 첨부해 드렸는데, https로 나와있다고 말씀을 하시길래 혹시 https로 URL를 설정 해서 안되나 싶어서 API 주소에 브라우저로 직접 접속 해봤습니다.

404

그렇습니다. 닷홈 호스팅은 무료 도메인 제공을 할때, SSL 인증서를 지원하지 않아, HTTPS가 지원하지 않는겁니다. 그제야 깨달은 저는 테스트 페이지를 https에서 http로 변경하고, 다시 fetch를 넣자, 모든게 정상적으로 동작했습니다.

4. 닷홈 호스팅으로 API 서버를 만들 때 알아야 할것들

그동안의 삽질로 알아낸 결과입니다.

  1. 닷홈 호스팅은 API 호출을 하기 위해선, 방화벽을 해제해야 합니다. 이는 호스팅 1:1 문의로 방화벽 해제 문의를 드리면 빠른 시간 내로 해결해 주십니다.
  2. API 호출을 하는 웹 사이트가 HTTPS를 사용한다면, SSL 인증서를 발급받아야 합니다. 이는 무료로 해결 할 수 없는 방안이며, 1년에 대략 1만 3천원 하는 유료 도메인과, SSL 설치 비용으로 2만원을 지불해야 합니다.
  3. PHP에서의 헤더 설정과 아파치 헤더 설정은 동일한 것 입니다. API 서버를 만들때 딱히 아파치 헤더 설정을 건들 이유는 없습니다.
  4. API 서버를 만들 때 SSL 외에는 결제를 필요로 하지 않습니다. 멍청 비용으로 웹 호스팅을 결제하지는 마시길 바랍니다.

5. 마치며

대략 1주간 피 튀기는 혈투 끝에 5만원의 손실을 보고 많은 점을 배웠습니다. 이렇게 사람이 성장 하는게 아닐까요? 하지만 이 글을 보는 여러분들은 굳이 시간과 돈을 버려가며 성장 하지는 마시길 바랍니다.

  1. 웹 크롤링에 오픈 API가 아닌, 개발자 도구를 통해 알아낸 비공식 API를 사용하는것에 대한 논란이 많습니다. 특히 여기어때가 경쟁 업체인 야놀자의 데이터베이스를 무단 크롤링을 하고, 이 무단 크롤링이 위법이라는 판결을 받게 되면서, 크롤링이 불법으로 인식 되는 경우가 더러 있습니다. 하지만, 비 상업적으로 이용하거나, 서버에 과한 부하를 주지 않는 한에서는 허용되는 분위기로 남아 있습니다. 

  2. 단, 폰트는 예외입니다. 

  3. 여기서 궁금증이 들 수 있습니다. 이렇게 모든 리소스를 차단하면 어떻게 인터넷이 유지가 되었을까? 사실은 모든 리소스를 차단하지는 않습니다. CORS 정책은 인증 되지 않은 Ajax을 철저하게 차단하고, 그 외 인증된 Ajax, 이미지, 스타일 시트, 동영상 등은 자유롭게 호출하고 응답할 수 있습니다. 

  4. 여기서 또 궁금증이 들 수 있습니다. 어차피 프록시 서버에서 불러오는 때에도 서로 다른 도메인에서 리소스 요청을 하기 때문에, CORS 정책을 위반 하는건 아닌가? 아닙니다. CORS 정책은 브라우저에 한하여 적용되고 있습니다. 그러므로 브라우저에서 CORS 에러가 발생해도, 데이터는 정상적으로 응답합니다! 브라우저 단에서 응답 온 데이터를 차단해서 문제입니다.