본문 바로가기

개발바닥/FrontEnd

[개발] Frontend에서 Cors 해결방법

안녕하세요 devport 입니다. 웹 개발을 하다보면 같은 출처가 아닌 외부의 API를 사용하고자 할때 웹 브라우저에서 에러가 발생하게 됩니다. 에러가 발생하는 이유는 브라우저의 CORS 정책에 위반되기 때문입니다.

FrontEnd개발 시에 극복할 수 있는 방법들에 대해서 알아보도록 하겠습니다. 우선 간단히 SOP, CORS 정책에 대해서 알아보도록 하겠습니다.

 

SOP (Same-Origin Policy)

위키에서는 SOP를 "동일 출처 정책"이라고 합니다. 정책명 그대로 동일 출처의 리소스 (HTTP, IMG, CSS... 등)만을 요청할 수 있습니다. 그렇기 때문에 다른 출처의 데이터를 요청을 한다는 것은 정책에 위배되는 행위라고 볼 수 있습니다.
하지만 다른 출처의 요소들에 대해서 허용도 가능해야하기 때문에 예외가 존재합니다. 

 

SOP의 출처는 "출처 결정 규칙"에 의해서 동일 출처인지 판단할 수 있습니다.

동일한 출처는 프로토콜, 호스트, 포트가 동일할 경우에 대해서 같은 출처로 브라우저는 인식하게 됩니다.

 

CORS (Cross-Origin Resouce Sharing)

CORS는 동일한 도메인이 아닌 다른 도메인에 리소스를 요청할 시에 서로 다른 origin에 대하여 HTTP 요청이 가능하게 해주는 표준입니다.

 

 

 


브라우저에서는 보안상의 이유로 다른 도메인에서 허용하지 않은 요청에 대하여 제한하고 있습니다.
일반적으로 다른 도메인 상에 대한 데이터를 요청을 하게 될 경우 SOP에 의하여 제한 되게 됩니다.
서버 측에서 외부 요청에 대하여 완화적인 조치를 취하게 될 경우 다른 도메인에 대한 접근할 수 있게 됩니다.

웹을 개발하다가 CORS에러가 발생한다면 어떻게 대응해야할까요? 제가 찾아봤을때 3가지 방법이 있었습니다.

 

CORS 대응 방법1 - Access-Control-Allow-Origin 설정

보통 CORS 이슈가 발생한다면 서버 측에서 Access-Control-Allow-Origin 헤더에 접근권한을 설정할 수 있습니다.
"*"를 설정하게 된다면 모든 외부 출처에서 접속을 할 수 있게 됩니다. 이 방법은 당장은 편리하여 좋아보이지만 보안적인 이슈에 직면할 수 있기 때문에 이러한 방법은 지양해야겠습니다.
그렇기에 외부에서 사용한다는 요청에 대한 심사를 거친 후 Access-Control-Allow-Origin에 외부 출처를 일일이 적용하는것이 좋습니다. 외부 출처를 추가하는 방법은 origin이 "https://test.com"일 경우 "Access-Control-Allow-Origin: https://testA.com'으로 헤더를 적용하여 주면 됩니다.  

 

 

하지만 무려 서버측 헤더를 수정을 해야하기 때문에 서버측 헤더를 변경할 방법이 없다면 사용할 수 없는 방법입니다.

 

만약 서버를 수정할 수 있다는 가정하에 Express 서버를 예로 들어 보겠습니다. Express서버에서는 헤더를 직접 추가 하는 방법과 Middleware를 사용하여 Access-Contorl-Allow-Origin을 설정을 할 수 있었습니다.

var app = require('express')()
var cors = require('cors')

// Access-Control-Allow-Origin 적용방법1: 직접 헤더에 적용
app.all('/*', function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Headers', 'X-Requested-With')
  next()
})

// Access-Control-Allow-Origin 적용방법2: cors 미들웨어 사용
app.use(cors())

app.listen(80, function () {
  console.log('http server is listening on port 80')
})

cors()를 적용하게 된다면 모든 origin을 허용하게 됩니다. 일부의 origin을 적용하고 싶다면 cors 미들웨어 문서를 참고하시길 바랍니다.

 

CORS 대응 방법2 - JSONP 설정

다른 도메인에 접근할 수 있는 우회적인 기법 중에 JSONP(JSON with Padding) 방법이 있습니다. 웹 브라우저에서 실행되는 Javascript는 통신을 이용해 외부 출처에서 데이터를 받아오는 것이 불가능하지만, HTML <script> 요소는 외부 출처로 부터 조회된 내용을 실행하는 것이 허용되고 있습니다. 다만 Javascript 문법 오류가 발생하게 됩니다. 

문법 오류가 발생하는 이유는 Javascript에서 변수, 상수로 정의 되지 않고 json형식이 입력되면서 발생하게 됩니다.

JSONP는 이러한 웹브라우저 특성을 이용하여 json 데이터를 서버가 콜백함수로 감싸 javascript 문법을 유효하게 만들어 전달하여 클라이언트에서 응답을 받을 수 있게 됩니다.

"http://server.example.com/User/1234"요청을 예로 든다면

parseResponse 콜백함수를 JSONP 요청을하기위해 포함을 합니다.

<script type="application/javascript"
        src=http://server.example.com/Users/1234?callback=parseResponse">
</script>

외부의 서비스인 server.example.com은 JSON 데이터를 패딩하여 클라이언트에 전송합니다.

praseResponse({"Name": "Foo", "Id": 1234, "Rank": 7})

JSONP 내용을 확인해보시면 아시겠지만 결국 서버에서 패딩을 요청한 콜백함수로 감싸줘 전송을 해야한다는 문제가 발생합니다. 외부 API에서 콜백함수를 제공하지 않는다면 사용할 수 없는 방법이 되겠습니다. 위 JSONP내용은 JSONP문서를 참고 하여 설명해드렸습니다.


CORS 대응 방법3 - Proxy 설정

외부 도메인서버에 접근하고자할 때 바로 외부 도메인 서버를 통하는 것이 아닌 자신의 서버를 매개로 하여 외부서버에 요청하는 방식입니다.

 

 

 

서버에서 요청을 하게될 때에는 브라우저의 규약인 CORS 정책에 영향을 받지 않습니다.  그러한 이점을 이용하여 Proxy Server라는 출처를 통하기 때문에 CORS 정책을 위반하지 않게되어 우회 할 수 있게됩니다.

 

Express 서버일 경우 express-http-proxy 미들웨어를 통해서 간단하게 Proxy Server를 구현할 수 있습니다.

var proxy = require('express-http-proxy')
var app = require('express')()

app.use('/proxy', proxy('www.text.com'))