(React) React App에서 CORS 이슈 해결하기

호두파파·2022년 3월 4일
32

React

목록 보기
32/38


🖱 들어서며 : CORS 경험해보셨나요?

React App을 개발하다보면, 공공기관 오픈 API 혹은 네이버나 카카오에서 제공하는 기업 API로 데이터를 요청할 일이 굉장히 자주 발생한다.

postman 같은 개발 어플리케이션을 이용해서 서버의 응답을 확인하면 아주 잘 반응하는데, 정작 local 환경에서 api 서버로 요청을 보내면 아래와 같은 에러를 콘솔창에서 확인할 수 있다.

😇 축하합니다. 이제 당신은 CORS의 늪에 빠지셨습니다. 이제 CORS 이슈를 어떻게 극복해나갈지 차근차근 알아보자.

🖱 CORS : Cross Origin Resource Share

CORS, 한글로는 '교차 출처 리소스 공유'라고 하는 이 개념은 동일한 출처일 경우에는 데이터의 요청과 전달에 아무런 문제가 없지만, 서로 다른 아이피 혹은 서버에서 데이터의 요청할 때 아주 중요한 개념이다.

웹 어플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차출처 HTTP 요청을 실행하기 때문이다.

교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다. - MDN 문서

요약하자면 CORS란 서로 다른 출처간에 리소스를 전달하는 방식을 제어하는 체제라고 할 수 있다.

네이버 API 혹은 카카오 API를 이용하면, 클라이언트 키와 클라이언트 시크릿 같은 그 고유한 값을 HTTP 헤더에 실어 요청을 보내야 하고, 서버는 헤더에 실린 값을 확인해 인가 토큰을 발행하고 인가 토큰을 받은 클라이언트(프록시 서버)는 데이터 서버에 요청을 보내 원하는 정보를 응답 받게 된다.

하지만 모든 API에서 이런 식으로 CORS 이슈를 해결해주지 못한다는 것이다.

오픈 API 특히나, 정부에서 관리하는 오픈 API는 API 요청에 클라이언트 시크릿을 실어서 요청을 하게 되는데, 서버를 통하거나 중간에 프록시 서버를 두지 않고 클라이언트에서 요청할 경우 CORS 에러가 뿜어지는 것을 확인할 수 있다.


🛠 에러로그 : 오픈 API를 사용할 때 CORS 이슈 해결하기

클라이언트에서 외부 API로 요청을 보낼 때 흔히 겪는 시나리오는 다음과 같다.

  • 클라이언트에서 외부 API 서버로 바로 요청을 보낸다.
  • 😇 cors 에러가 콘솔에 짜잔!
  • 외부 API를 사용하고 있기 때문에 서버를 제어할 수 없으므로, HTTP 응답헤더(Access-Control-Allow-Origin)를 설정할 수 없다.

CORS는 브라우저에 관련된 정책이기 때문에, 서버 간의 통신에는 이 정책이 적용되지 않는다 포스트맨을 이용해서 요청을 보냈을 때 CORS가 발생하지 않는 이유가 바로 여기에 있다.

이제 선택을 해야한다.

1) Node.js를 이용해서 별도 서버를 구축해서 서버를 중간에 두고 요청을 보낼 것이냐
2) 리액트 앱에서 라이브러리를 통해 프록시를 구축할 것이냐

node.js 서버를 이용해서 CORS 이슈 극복하기

하지만, CSR로만 웹 어플리케이션을 구현하라는 의도는 결국, 클라이언트 단계에서 CORS 이슈를 극복하는 접근 방법을 보여주라는 의도로 이해를 했고, 고민 결과 프록시 미들웨어를 이용해주기로 했다. 😡

🧮 프록시 서버
프록시 서버는 클라이언트가 프록시 서버를 통해 다른 네트워크에 간접적으로 접속할 수 있게 해준다. 쉽게 이해하자면 "중계서버"라고 이해하면 된다.

package.json 마지막 줄에 "proxy": "api 주소"를 추가해주는 방법으로 proxy를 우회할 수 있지만 이는 개발 단계에서만 유효하지 배포를 했을때는 작동하지 않는다.


🛷 Http-Proxy-Middleware

npm i http-proxy-middleware로 라이브러리를 프로젝트에 설치해서 require로 불러준다.

❗️ 중요한 것은 CRA로 개발 중일 경우 src 폴더에 setupProxy.js라고 파일을 만들어줘야 제대로 앱이 프록시 설정을 파싱할 수 있다.

간혹 util 폴더를 만들고, 라이브러리 관련 파일을 몰아 놓는 경우가 있는데, proxy 관련 파일을 반드시 ./src에 위치해야 한다.

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function (app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'api요청주소 * 엔드포인트까지만 넣어주자'
      pathRewrite: {
        '^/api': '',
      },
    }),
  );
};

위 처럼 코드를 작성해두면, 로컬 환경에서 http://localhost:3000/api 로 시작되는 요청을 라이브러리가 'api요청주소/api'로 프록싱해주게된다.

브라우저는 클라이언트와 서버의 출처가 다르지만 같은 출처로 받아들이게 되어 CORS 문제를 일으키지 않는다.

import axios from 'axios';
import { API_URL } from '../constants';

export const getData = async pageCount => {
  try {
    const { data } = await axios.get('/api'+`${API_URL}&perPage=10&currentPage=${pageCount}`);
  } catch (err) {
    console.log(err);
  }
};

위처럼 axios.get('/api' + 'api주소의 나머지 부분) 요청을 보내주면 네트워크 200코드와 함께 요청이 성공적으로 이루어졌음을 확인할 수 있다.


🤪 Bonus : netlify로 배포했을때 Proxy 세팅하기

로컬 개발환경에서 proxy를 구축했다고 의기양양하겠지만, 배포 단계에서 또 다른 이슈를 만날 수 있다. 분명 로컬환경에서는 api 요청하면 잘 되었는데, 왜 배포를 하니 또 CORS 이슈를 겪어야만 하는 것일까? 😡

netlify는 https 방식을 default로 설정해서 제공한다. 처리하는 경로가 HTTP에서 HTTPS URL로 예상대로 리디렉션되지 않을 수 있어 CORS 이슈가 발생하게 되는 것이다.

때문에, netlify로 배포를 해야 한다면, 아래와 같이 netlify에서 프록시를 지원하도록 프로젝트에서 세팅을 해주어야 한다.


netlify 공식문서 자세히 읽어보기

먼저 route 디렉토리(😇 절대 다른 곳이면 안됨)에 netlify.toml 파일을 생성한다.

그리고 내용을 다음과 같이 작성한다.

주의할 점은 반드시 api 요청 주소 뒤에 ':splat'을 붙여줘야 한다는 것이다. 내 경우엔 proxy middleware로 우회할 경로값을 '/api'로 세팅했기 때문에 저렇게 작성을 해주었다. 이 점은 어떻게 작성했느냐에 따라 달라지기 때문에 자신의 코드를 살펴보도록 하자.

예상대로 아주 잘 빌드가 되는 것을 확인할 수 있다.

데이터 요청도 잘 이뤄지고 있다 😇


🖱 정리

CORS 이슈는 리액트를 이용해 웹 어플리케이션을 제작하는 주니어 개발자라면 언젠가는 한 번씩 꼭 경험하게 된다. 콘솔창에서 cors를 맞이하게 된다면 패닉을 일으키지 말고, 요청하는 api의 명세를 제대로 읽는 것이 중요하다.

어떤 api는 헤더에 Access-Control-Allow-Origin: *를 실어서 보낼 것을 요구하기도 하고, 네이버 api는 발급받은 클라이언트 키와 시크릿을 함께 보낼 것을 요구한다.

이것은 케이스마다 다르기 때문에 위 내용이 모든 케이스에서 정확한 처방이 될 수 없다.

다만, 이 글을 통해 더 이상 CORS 이슈로 고통받는 이가 없길 바랄 뿐이다 🤒


참조

profile
안녕하세요 주니어 프론트엔드 개발자 양윤성입니다.

0개의 댓글