본문 바로가기
Ruby on Rails

rails CORS 이슈 처리하기

by 혜리루 2019. 12. 26.

1. 서론

최근 회사 동료분과 사내 행사 관련한 웹사이트를 만드는 토이프로젝트를 했습니다.

백엔드파트인 저는 api개발을, 웹파트인 동료분은 프론트를 맡아서 각각 개발한 후 웹에 api를 붙이려는 계획이었는데요,

간단한 사이트니까 주말동안 뚝딱 만들어오죠?ㅋㅋ 라며 경거망동하게 입을 놀렸던 스스로를 반성하게 되는 보람된 시간이었습니다.

 

사실 개발보다는 배포과정에서 이슈가 많았는데요, 그 얘기는 나중에 또 적도록 하겠습니다.

아무튼 우여곡절 끝에 잘 배포를 했고 http test에서 response가 잘 나오는 걸 확인하고 동료분께 api를 드렸습니당.

 

 

"혜리님 오류나는데요 ㅠ"

"네..?ㅠ 왜요..?ㅠ"

 

 

그냥 브라우저 상에서 api를 직접 호출할 때는 잘 작동을 하지만

local web 개발 환경에서는 response가 빈값으로 돌아오는 이상한 오류였습니다, 

UserInfo.vue:31 Error: Network Error
  at t.exports (createError.js:16)
    at XMLHttpRequest.p.onerror (xhr.js:81)

다른 웹파트 동료분께 여쭤보고 구글링을 해보니 CORS가 관련된 문제였습니다.

 

 

사실 저는 CORS가 뭔지도 몰랐습니다.

그래서 CORS가 뭐하는 놈인지 찾아보았습니다.

 

2. CORS


교차 출처 리소스 공유(Cross-origin resource sharing, CORS), 교차 출처 자원 공유는 웹 페이지 상의 제한된 리소스를 최초 자원이 서비스된 도메인 밖의 다른 도메인으로부터 요청할 수 있게 허용하는 구조이다.[1] 웹페이지는 교차 출처 이미지, 스타일시트, 스크립트, iframe, 동영상을 자유로이 임베드할 수 있다.[2] 특정한 도메인 간(cross-domain) 요청, 특히 Ajax 요청은 동일-출처 보안 정책에 의해 기본적으로 금지된다.

CORS는 교차 출처 요청을 허용하는 것이 안전한지 아닌지를 판별하기 위해 브라우저와 서버가 상호 통신하는 하나의 방법을 정의한다.[3] 순수하게 동일한 출처 요청보다 더 많은 자유와 기능을 허용하지만 단순히 모든 교차 출처 요청을 허용하는 것보다 더 안전하다. CORS의 사양은 원래 W3C 권고안으로 출판되었으나[4] 해당 문서는 구식(obsolete)인 상태이다.[5] 현재 CORS를 정의하면서 활발히 유지보수된 사양은 WHATWG의 Fetch Living Standard이다.[6]


출처: https://ko.wikipedia.org/wiki/%EA%B5%90%EC%B0%A8_%EC%B6%9C%EC%B2%98_%EB%A6%AC%EC%86%8C%EC%8A%A4_%EA%B3%B5%EC%9C%A0

 

위키피디아에서 긁어왔는데 무슨 말인지 잘 모르겠군요...

다시 정리하자면 CORS란 Cross Origin Resource Sharing 의 줄임말로 Cross-site Http Request를 가능하게 하는 표준 규약입니다. Http 요청은 기본적으로 cross-site 요청이 가능합니다. 즉 haeree.com 도메인에서 dongdong.net의 리소스를 요청할 수 있다는 말입니다. rest api를 널리 사용하는 요즘에는 엄청 당연하다고 생각이 되지만 이런 요청이 불가능한 상황이 있습니다.

 

에러 로그에 보면 XMLHttpRequest에서 오류가 난 것을 볼 수가 있는데요, XMLHttpRequest는 보안상의 이유로 same-origin 정책을 따른다고 합니다. XMLHttpRequest사용하는 웹 어플리케이션은 어플리케이션과 동일한 출처(프로토콜, 호스트, 포트) 의 리소스 만 요청할 수 있습니다! 오.. 엄청 귀찮은 정책이네요...

 

저희 토이프로젝트 같은 경우는 api는 elastic beanstalk으로, 웹은 s3로 배포를 해서 서로 도메인이 달랐습니다. 

 

이럴 때 필요한 것이 CORS입니다. CORS는 헤더에서 브라우저와 서버가 상호 통신하는 방법을 결정 한 후에 해당 cross-site 요청이 안전한지 아닌지를 판별합니다.

 

쉽게 얘기해서 서버에서 응답을 할 때 헤더에 요청을 허용할 도메인, http 메소드 등을 함께 적어주면 에러가 나지 않는다는 말입니다. 얏호!

 

3. rack-cors

ruby에는 cors 설정을 도와주는 gem이 있습니다. 

 

# Gemfile.rb

# Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
gem 'rack-cors'

 

rails 프로젝트를 api 옵션으로 생성하면 위의 코드가 주석처리 되어있습니다. 우리는 이 코드를 주석처리만 해주면 됩니다.

 

# config/application.rb   
   
   config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins '*'
        resource '*', headers: :any, methods: [:get, :post, :options]
      end
    end

그리고 application.rb 파일에 위와 같은 코드를 추가해주시면 됩니다.

이 코드는 모든 도메인에 대해 cross-site 요청을 허용합니다. 빠르고 편하지만 보안에는 취약하겠죠.

 

# config/application.rb   
   
   config.middleware.insert_before 0, Rack::Cors do
      allow do
        origins 'localhost:3000', '127.0.0.1:3000',
            /\Ahttp:\/\/192\.168\.0\.\d{1,3}(:\d+)?\z/
            # regular expressions can be used here
        resource '*', headers: :any, methods: [:get, :post, :options]
      end
    end

 

어떤 도메인으로 특정하고 싶다면 위처럼 콤마로 구분을 해서 도메인 주소를 적어주시면 됩니다.  정규식을 이용하하면 도메인을 특정하지 않고 패턴으로 도메인을 설정할 수 있습니다.

 

use Rack::Cors do
  allow do
    origins 'localhost:3000', '127.0.0.1:3000',
            /\Ahttp:\/\/192\.168\.0\.\d{1,3}(:\d+)?\z/
            # regular expressions can be used here

    resource '/file/list_all/', :headers => 'x-domain-token'
    resource '/file/at/*',
        methods: [:get, :post, :delete, :put, :patch, :options, :head],
        headers: 'x-domain-token',
        expose: ['Some-Custom-Response-Header'],
        max_age: 600
        # headers to expose
  end

  allow do
    origins '*'
    resource '/public/*', headers: :any, methods: :get

    # Only allow a request for a specific host
    resource '/api/v1/*',
        headers: :any,
        methods: :get,
        if: proc { |env| env['HTTP_HOST'] == 'api.example.com' }
  end
end

출처: https://github.com/cyu/rack-cors

 

위처럼 복잡한 로직도 설정이 가능합니다.

 

 

에러 로그에 자세한 정보가 없어서 당황했는데 해결방법은 생각보다 쉬웠던 이슈였습니다.

댓글