React 개발환경에서의 CORS를 위한 proxy 설정

김태완·2021년 4월 14일
11

문제상황

React를 개발할 때, 보통 react-script start를 통해 React dev server를 띄어놓고 개발을 한다.
React dev server의 접속 주소는 보통 http://localhost:3000 을 사용한다.
만약 api server의 주소가 http://apiserver.com:5000이라고 하면, 따로 CORS설정을 해놓지 않은 이상,
api request를 날리면 CORS 오류가 뜬다.

Access to fetch at ’https://apiserver.com:5000/api/test’ from origin ’http://localhost:3000’ has been blocked by CORS policy: The value of the ‘Access-Control-Allow-Origin’ header in the response must not be the wildcard ’*’ when the request’s credentials mode is ‘include’.

해결방법

react-script 의 proxy 설정

react-script 공식 홈페이지 문서에 proxy를 설정하는 방법이 있다.

package.json에 proxy 한 줄을 추가하면 된다!

//package.json
{
  ...,
  "proxy": "http://apiserver.com:5000",
  ...,
}

그러면 다음과 같이 request가 처리된다.

  1. browser에서 React dev server(http://localhost:3000)으로 요청을 보낸다.
  2. React dev server가 해당 요청을 api server(http://apiserver.com:5000)에 보낸다.
  3. api server가 response를 react dev server에게 전달한다.
  4. React dev server는 이 response를 그대로 browser에게 전달해준다.
    이때, browser 입장에서는 api server가 아닌, React dev server가 응답한 것 처럼 보이게 된다.

문제해결!! 일줄 알았으나..

새로운 문제

websocket

서버와의 양방향 통신을 위해, 보통 websocket을 많이 사용한다.
websocket으로 서버와 통신할때는 protocol을 http가 아니라 ws를 사용하게 된다.

ex) ws://apiserver.com:5000/socketio?...

위의 방법처럼 proxy를 설정하게되면, websocket 요청들의 protocol이 http로 바뀌어서 서버에 전달되서 websocket을 사용할 수 없게 된다.

http-proxy-middleware

github issue: Setting "proxy" in package.json Fails for WebSockets

위 이슈에서 제시한 해결책은 http-proxy-middleware라는 모듈을 사용하는 것이다.
option에 ws: true를 주면되는군..!

react-script의 공식문서에도 proxy를 manual하게 설정하는 방법이 나와있다.

두 가지를 조합해봤을때, src/SetupProxy.js를 만들어서 아래와 같이 만들면 될 것 같았다.

// src/SetupProxy.js
// failed code
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://apiserver.com:5000',
      changeOrigin: true,
      ws: true
    })
  );
  app.use(
    '/socket.io',
    createProxyMiddleware({
      target: 'ws://apiserver.com:5000',
      changeOrigin: true,
      ws: true
    })
  );  
};

websocket 요청은 /socket.io로 그냥 api 요청은 /api로 나누어져서 잘 들어가질 것이라 생각했다.
하지만, 결과는 실패... 😥
뭔가 원하는대로 request들이 분류가 잘 안되는 것 같았다.

해결 방법

열심히 구글링한 결과 두 가지를 알아냈다.

1. filtering을 잘못하고 있었다.
뭔가 filtering이 이상해서 http-proxy-middleware 코드를 살펴봤다.

github code

createProxyMiddleware에 인자를 하나만 넣으면 option이고, 두개를 넣으면 filter, option인 것 같다!
filter를 app.use의 첫 argument에 넣지말고, createProxyMiddleware의 첫 인자에 넣어보자!

2. http-proxy-middleware의 README를 읽어보며 router라는 좋은 옵션을 발견했다.
굳이 app.use를 2개 만들지말고 이 옵션을 사용해보자!

2가지를 고려해서 새로운 SetupProxy.js를 만들었다.

// src/SetupProxy.js
const createProxyMiddleware = require('http-proxy-middleware')
module.exports = app => {
  app.use(
    createProxyMiddleware(
      ['/api', '/socket.io'],
      {
        target: 'http://nginx:80',
        changeOrigin: true,
        ws: true,
        router: {
          '/socket.io': 'ws://nginx:80'
        }
      }
    )
  )
}

문제해결!!

느낀점

  1. typescript를 사용하자.
    http-proxy-middleware 코드를 읽을때 ts여서 코드파악이 용이했다.
    미리 type을 다 정의해 놓는 게 가독성에 좋은 효과를 준다는 것을 처음 느껴봤다.
  2. 공식 document를 맹신하지말자..

참조

  1. https://evan-moon.github.io/2020/05/21/about-cors/

2개의 댓글

comment-user-thumbnail
2022년 3월 11일

잘 읽고갑니다

답글 달기
comment-user-thumbnail
2023년 2월 4일

스프링에 리액트 연동하려고 프록시 관련으로 알아보고 있었는데 잘 참고하겠습니다.

답글 달기