[CS, React] 리액트 Proxy 사용하기

LMH·2023년 2월 6일
3

이번 포스팅에서는 프록시의 개념과 리액트에서 프록시 서버를 이용해서 CORS 정책을 우회하는 방법에 대해서 정리하겠습니다.

프록시란?

프록시는 컴퓨터 네트워크에서 서버과 클라이언트 사이에서 요청과 응답을 중계하는 서버를 의미합니다. 다른 서버로의 자원 요청 중계를 통해 분산 시스템 구조를 단순화할 수 있습니다.

프록시 서버에서는 클라이언트로부터 요청된 자원들이 캐시로 임시 저장되어 있습니다. 클라이언트가 자원을 재요청할 경우 프록시 서버에 있는 데이터를 제공 받아 데이터 전송시간과 서버의 외부 트래픽을 감소 시킵니다.

또한, 서버를 대상으로 하는 외부 공격이 들어올 경우 필터링하여 클라이언트 측의 보안을 향상 시킬 수도 있습니다.

프록시 종류

포워드 프록시(Forward Proxy)

프록시 서버가 클라이언트의 뒤에 위치하며 클라이언트가 서버의 주소를 기반으로 데이터을 요청하면 프록시 서버가 해당 서버에 연결하고 데이터를 가져옵니다. 예를 들어 클라이언트가 google.com로 데이터를 요청하면 프록시가 대신 google.com의 데이터를 받아 클라이언트에게 넘겨(forward) 줍니다.

보통 프록시 서버라 하면 이 포워드 프록시를 의마합니다.

포워드 프록시 장점

  • 클라이언트 보안 : 포워드 프록시 서버는 방화벽 처럼 특정 사이트 접속이나 요청을 막을 수 있습니다.
  • 캐싱 : 캐싱을 통해 동일한 요청을 할 경우 캐싱된 정보(페이지)를 그대로 반환할 수 있고, 이는 서버의 부하를 줄이는 효과도 낼 수 있습니다.
  • 암호화 : 클라이언트 요청은 포워드 프록시 서버를 통과할 때 암호화되며 다른 서버를 통과할 때 최소한의 정보만 갖게 되므로 클라이언트의 IP를 감출 수 있으며, IP 추적 시 프록시 서버의 IP만 노출 됩니다.

리버스 프록시(Reverse Proxy)

리버스 프록시는 웹서버/WAS 앞에 위치하며 서버들을 제어하고 보호 합니다. 클라이언트는 데이터를 가지고 있는 서버가 아닌 프록시 서버에 요청을 보냅니다. 이 경우, 클라이언트 입장에서는 리버스 프록시 서버는 보통의 서버로 인식합니다.

보통 기업의 네트워크 환경에는 DMZ라는 내부 네트워크와 외부 네트워크 사이 구간이 존재합니다. 이 구간에 메일 서버, 웹 서버, FTP 서버 등 외부 서비스를 제공하는 서버가 위치합니다. 하지만 WAS의 경우 DB와 연결되어 있으므로 DMZ가 아닌 내부망에 위치시키는데 리버스 프록시를 DMZ에 위치 시켜 보안을 강화합니다.

일반적으로 WEB(Apache, nginx) - WAS(Tomcat) 분리 형태를 Reverse 프록시라고 볼 수 있습니다.

리버스 프록시 장점

  • 로드 밸런싱 : 리버스 프록시 서버를 본 서버 앞에 여러개 배치하면 서버과 과부화되지 않게 로드 밸런싱이 가능합니다.
  • 서버보안 : 리버스 프록시는 본 서버의 IP 주소를 노출 시키지 않아 DDOS 공격과 같은 공격을 막을 수 있습니다.
  • 캐싱 : 포워드 캐싱과 마찬가지로 캐싱을 통해 서버 부하를 줄일 수 있습니다.
  • 암호화 : SSL or TSL 암호화, 복호화를 하면 클라이언트와 안전한 통신을 할 수 있으면서 서버의 부담을 줄일 수 있습니다.

정리

클라이언트 앞에 있을 경우 포워드, 서버 앞에 있을 경우 리버스 프록시입니다. 포워드 프록시는 클라이언트 IP를 감추고 서버 프록시는 서버 IP를 감출 수 있습니다. 또한 포워드 프록시는 직접 서버 url로 요청을 보내지만, 리버스 프록시는 프록시 서버 url로만 접근이 가능합니다.

리액트에서 프록시 서버 활용

Webpack Dev Server

React 라이브러리, 혹은 Webpack Dev Server에서 제공하는 프록시 기능을 사용하면 CORS 정책을 우회할 수 있습니다. 이는 별도의 응답 헤더를 받을 필요 없이 브라우저는 React 앱으로 데이터를 요청하고, 해당 요청을 백엔드로 전달하게 됩니다.

프록시 서버를 사용하여 우회하지 않는 경우에 React 앱에서 브라우저 쪽으로 요청을 보냅니다. 그러면 브라우저는 백엔드, 즉 서버 쪽으로 리소스를 요청하게 됩니다. 이때 접근 권한이 있는지, 즉 출처가 같은지 확인하는데 이때 백엔드 서버는 정상적으로 200 OK 응답을 브라우저에게 보냅니다. 마지막으로 브라우저는 받은 리소스 및 응답과 함께 출처가 같은지 아닌지 확인하게 되는데, 이때 출처가 다르다면 응답을 파기(CORS Error) 하고, 출처가 같다면 응답을 파기하지 않고 다시 프론트엔드 쪽으로 응답을 보내주는 것입니다.

프록시 서버를 사용할 경우 React 앱에서 브라우저를 통해 API를 요청할 때, 프록시 서버를 통해 백엔드 서버로 요청을 우회하여 보내게 됩니다. 그러면 백엔드 서버는 응답을 React 앱으로 보내고, React 앱은 받은 응답을 백엔드 서버 대신 브라우저에게 전달합니다. 이렇게 되면 출처가 같아지기 때문에 브라우저는 이 사실을 눈치 채지 못하고 허용하게 됩니다.

사용법

package.json에서 요청을 보내는 서버의 도메인을 설정해 주면 간단하게 프록시 기능을 사용할 수 있습니다. 아래의 코드는 서버의 도메인이 "http://localhost:4000"인 경우의 설정입니다.

"browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
	"proxy" : "http://localhost:4000"
}

설정을 완료할 경우, 데이터를 fetch 할때, 도메인을 제외한 Path parameter만 명시하면 됩니다.

export async function getAllfetch() {
// 프록시 설정 전
    const response = await fetch('우회할 api주소/params');
    .then(() => {
			...
		})
}
// 프록시 설정 후
export async function getAllfetch() {

    const response = await fetch('/params');
    .then(() => {
			...
		})
}

React Proxy

http-proxy-middleware 라이브러리를 사용해서 프록시를 사용할 수 있습니다. 명령어로 라이브러리를 설치합니다.

npm install http-proxy-middleware --save

React App의 src 파일안에 setUpProxy.js 파일을 생성하고 다음과 같이 설정해 주면 됩니다.

아래의 코드는 클라이언트가 "/api"라는 Path parameter로 요청하면 프록시가 도메인이 "http://localhost:3070"인 서버에서 요청을 보낸 후 받은 응답을 클라이언트에게 보냅니다.

그리고 클라이언트가 "/api2" 또는 "/api3" 라는 Path parameter로 요청할 경우 도메인이 "http://localhost:3080"인 서버에 요청을 보낸 후 받은 응답을 클라이언트에게 보냅니다.

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

module.exports = function (app) {
  app.use(
    "/api", //proxy가 필요한 path prameter를 입력합니다.
    createProxyMiddleware({
      target: "http://localhost:3070", //타겟이 되는 api url를 입력합니다.
      changeOrigin: true, //대상 서버 구성에 따라 호스트 헤더가 변경되도록 설정하는 부분입니다.
    })
  );
  app.use(
    ["/api2","/api3"], //proxy가 필요한 path prameter를 입력합니다.(여러개일 경우 배열로 입력합니다.)
    createProxyMiddleware({
      target: "http://localhost:3080", //타겟이 되는 api url를 입력합니다.
      changeOrigin: true, //대상 서버 구성에 따라 호스트 헤더가 변경되도록 설정하는 부분입니다.
    })
  );
};

아래와 방식과 같이 작성하는 것도 가능합니다.


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

module.exports = function (app) {
  app.use(
    ["/api", "/api2","/api3"], //proxy가 필요한 path prameter를 입력합니다.(여러개일 경우 배열로 입력합니다.)
    createProxyMiddleware({
      target: "http://localhost:3070", //타겟이 되는 api url를 입력합니다.
      changeOrigin: true, //대상 서버 구성에 따라 호스트 헤더가 변경되도록 설정하는 부분입니다.
      router : {
      	"/api2" : "http://localhost:3080",
        "/api3" : "http://localhost:3080"
      }
    })
  );
};
profile
새로운 것을 기록하고 복습하는 공간입니다.

0개의 댓글