nextjs reverse proxy로 cors에러 해결하기

Maliethy·2021년 6월 2일
8

react error

목록 보기
1/7

1. issue

react와 nextjs로 개발하는 서버(http://localhost:3060)가 실서버(https://backserver.example.co.kr, 백엔드)와 api통신을 하자 cors에러가 발생했다.

Access to XMLHttpRequest at 'https://backserver.example.co.kr/Web/SignIn' from origin 'http://localhost:3060' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

실서버는 현재 상용화 중이라 수정이 어려워 프론트 단에서 해당 에러를 처리해야 했다.
(참고로 아래 예시에서 프론트는 서버와 JWT를 사용해 access token과 refresh token으로 인증을 시도하고 있다.)

2. solution

브라우저가 아닌 서버와 서버의 통신은 cors에러가 발생하지 않는다는 아이디어를 기반으로 문제를 해결할 수 있었다. 프론트에서 nextjs webpack설정을 바꿔 webpack dev server로 프록시 가상 서버를 만들어 '브라우저->webpack dev server(프록시 서버)-> 실서버'로 요청을 보내는 식으로 설정해보았다.

next.config.js/rewrites에 설명된 것처럼 webpack dev server의 reverse proxy 설정을 해주어 브라우저의 요청을 마치 dev server의 요청인 것처럼 돌려서 실서버에 요청을 보낸다.

아래 next.config.js설정에서 rewrites() 함수가 그 일을 하고 있다. source에는 nextjs 개발 서버 주소(http://localhost:3060)를 가리키도록 하고 destination(https://backserver.example.co.kr/)은 실서버 주소를 가리키도록 한다.

next.config.js

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});
const withPlugins = require('next-compose-plugins');
const withAntdLess = require('next-plugin-antd-less');
const withAntdLessConfig = {
  // optional
  modifyVars: { '@primary-color': '#348fe2' },
  // optional
  lessVarsFilePath: './pages/antd-custom.less',
  // optional
  lessVarsFilePathAppendToEndOfContent: false,
  // optional https://github.com/webpack-contrib/css-loader#object
  cssLoaderOptions: {},

  // Other Config Here...

  future: {
    // if you use webpack5
    webpack5: true,
  },
};
const plugins = [
  // [withBundleAnalyzer],
  [withAntdLess, withAntdLessConfig],
];
const nextConfig = {
  webpack: (config, { webpack }) => {
    const prod = process.env.NODE_ENV === 'production';
    const newConfig = {
      ...config,
      mode: prod ? 'production' : 'development',
    };
    if (prod) {
      newConfig.devtool = 'hidden-source-map';
    }
    return newConfig;
  },
  async rewrites() {
    return [
      {
        source: '/:path*',
        destination: `https://backserver.example.co.kr/:path*`,
      },
    ];
  },
};

module.exports = withPlugins(plugins, nextConfig);

velopert님의 cors-and-proxy 게시글에 설명되어 있는 것처럼 API의 도메인과 nextjs 서버의 도메인이 다르다면 (예: nextjs 서버는 velog.io, API 는 api.velog.io), axios의 글로벌 baseURL을 개발 모드와 배포 모드로 나누어 설정해준다.

axios.defaults.baseURL = process.env.NODE_ENV === 'development' ? '/' : 'https://backserver.example.co.kr/';

위와 같은 설정이 의미하는 바는 브라우저가 개발 환경에선 프록시 서버 쪽('/')으로 요청하고, 프로덕션에선 실제 API 서버('https://backserver.example.co.kr/')로 요청을 하게 설정한다.

결과는 다음과 같다. 프록시 서버가 브라우저의 http://localhost:3060 라는 주소를 그대로 사용한 것처럼 나온다. 그럼에도 실서버는 브라우저가 아닌 프록시 서버에서 요청이 들어온 것으로 알고 cors에러를 피해간다. 실서버에 잘못된 비밀번호로 요청을 보냈기 때문에 user의 비밀번호가 일치하지 않는다는 에러 메세지를 보내주었다.

성공했을 때 요청과 응답 정보는 다음과 같다.

profile
바꿀 수 있는 것에 주목하자

2개의 댓글

comment-user-thumbnail
2022년 10월 11일

문의 드려봅니다. 개발시에 이렇게 CORS를 피해갈 수 있다고 하는데 그럼에도 불구하고 인증관련 Cookie는 전달되지 않는 것 같은데 맞는지요? 그렇다면 이렇게 해도 개발에서 쓸 수있는 기능은 제약이 있지 않을까요? 참고링크를 달아봅니다.
https://security.stackexchange.com/a/191740

1개의 답글