Proxy

현채은·2023년 6월 7일
0
post-thumbnail

🔐 CORS 정책이 필요한 이유


브라우저에서 기본적으로 API를 요청할 때, 브라우저의 현재 주소와 API의 주소 도메인이 일치해야만 데이터에 접근할 수 있다. 만약 다른 도메인에서 API를 요청해서 사용할 수 있게 해주려면 CORS설정이 필요하다.

CORS

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

출처

: 웹 콘텐츠의 출처(Origin)는 접근할 때 사용하는 ①.URL의 스킴(프로토콜), ②.호스트(도메인), ③.포트로 정의된다. 두 객체에서 3가지가 일치하는 경우 같은 출처를 가졌다고 말한다. 일부 작업은 동일 출처 콘텐츠로 제한되나, CORS를 통해 제한을 해제할 수 있음

로컬환경에서 개발한 앱은 기본적으로 localhost:3000으로 사작한다. 그러나 나중에 로컬환경에서 개발한 실제 서비스, 프로젝트의 클라이언트에서 서버의 API로 요청하게 되면, 이 포트로 요청하는 것이 차단됨
➡️ 개발할 당시에는 이 현상이 굉장이 귀찮은 일로 느껴질 수 있지만, 실제로 개발한 서비스 & 프로젝트가 모든 출처의 접근을 허락한다면 이는 큰 문제로 야기될 수 있음

만약 실제 서비스가 되는 상용앱을 운영 중이라면, 구축한 클라이언트 뒤의 서버와 연결되어 있는 DB에는 라이브 데이터(live data)가 쌓일 것

라이브 데이터(live data)

: 실제 서비스 되고 있는 앱의 데이터베이스(Dat Base, DB)에 적재되고 있는 데이터를 의미 ( 유저 및 상품, 결제 등 다양한 정보들)

이런 라이브 데이터는 민감성이 높은 데이터들 위주 ➡️ 보안이 매우매우 중요!
그러나 서비스 & 프로젝트가 모든 출처의 접근을 허락한다면, 이러한 보안성이 현저히 낮아지고, 해킹 위험에 그대로 노출되게 됨...

따라서, 모든 도메인을 허용해서는 안되고, "특정"도메인을 허용하도록 구현해야함

  • 프론트 개발자 ➡️ 백엔드 개발자 : 프론트엔드 개발 서버 도메인을 허용해 달라고 요청
  • 백엔드 개발자 : 응답 헤더에 필요한 값들을 담아서 전달
    서버에서 적절한 응답 헤더를 받지 못하면 브라우저에서 에러 발생

🔐 Proxy


Proxy란?

: 클라이언트가 자신을 통해서 다른 트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템 & 응용프로그램을 가리킴

  • Proxy란 '대리'라는 의미를 갖고 있으며, 서버와 서버 사이의 중계역할을 한다고 보면 됨
    ➡️ 왜 사용하냐 ? 보안상의 이유로 직접 통신할 수 없는 두 점 사이에서 대리로 통신을 수행하여 보안성, 성능, 안정성을 향상 시키기 위해서 사용한다.

그러나 위 과정 없이 React 라이브러리, 혹은 Webpack Dev Server에서 제공하는 proxy 기능을 사용하욘 CORS 정책을 우회할 수 있다. (별도의 응답 헤더를 받을 필요 없음)

  • 브라우저는 React 앱으로 데이터를 요청하고, 해당 요청을 백엔드로 전달
    ➡️ 여기서 React 앱이 서버로부터 받은 응답 데이터를 다시 브라우저로 전달하는 방법을 쓰기 때문에 브라우저는 CORS 정책을 위반했는지 모르게 됨 ( 브라우저를 proxy 기능을 통해 속이는 것)

[그림]proxy 적용 전 흐름

위 그림은 proxy를 적용해 브라우저를 속이기 전 흐름

  • 프론트엔드, 즉 개발한 React 앱에서 브라우저 쪽으로 요청을 보냄
  • 브라우저는 백엔드, 즉 서버 쪽으로 리소스를 요청하게 됨
    ➡️ 이때 접근 권한이 있는지, 출처가 같은지 확인하는데 이때 백엔드 서버는 정상적으로 200OK 응답을 브라우저에게 보냄
  • 마지막으로 브라우저는 받은 리소스 및 응답과 함께 출처가 같은지 아닌지 확인
    ➡️ 이때, 출처가 다르면 응답을 파기(CORS Error), 출처가 같다면 응답을 파기하지 않고 프론트엔드쪽으로 응답을 보내줌

[그림]proxy 적용 후 흐름

위 그림은 proxy를 적용해 브라우저를 속인 후 흐름

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

Proxy 사용법


①.webpack dev server proxy


먼저 webpack dev server에서 제공하는 proxy 기능을 사용하는 방법

  • webpack dev server 의 proxy를 사용하게 되면, 브라우저 API를 요청할 때 백엔드 서버에 직접 요청을 하지 않고, 현재 개발서버의 주소로 우회요청을 하게 됨
    ➡️ 웹팩 개발서버에서 해당 요청을 받아 그대로 백엔드 서버로 전달하고 백엔드 서버에서 응답한 내용을 다시 브라우저쪽으로 반환
  • 웹팩 개발 서버의 proxy 설정은 원래 웹팩 설정을 통해서 적용하지만, CRA를 통해 만든 리액트 프로젝트에서는 package.json에서 "proxy" 값을 설정하여 쉽게 적용할 수 있도록 구성
...
"browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
	"proxy" : "우회할 API 주소"
}

proxy는 보통 맨 밑에 작성을 함

그리고 기존 fetch 혹은 axios를 통해 요청하던 부분에서 도메인 부분을 제거

// 제거하기 전
export async function getAllfetch() {

    const response = await fetch('우회할 api주소/params');
    .then(() => {
			...
		})
}
// 제거한 후
export async function getAllfetch() {

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

②.React Proxy 사용법


webpack dev server에서 제공하는 proxy는 전역적인 설정이기 때문에, 종종 해당 방법이 충분히 적용되지 않는 경우 발생
➡️ 수동으로 proxy를 적용해줘야 하는 경우가 있는데, 이때는 http-proxy-middleware 라이브러리를 사용해야 함

  • http-proxy-middleware 라이브러리 설치
npm install http-proxy-middleware --save
  • 그리고 React App 의 src 파일 안에서 setupProxy.js 파일을 생성하고, 안에서 설치한 라이브러리 파일을 불러온 다음 아래와 같이 작성
const {createProxyMiddleware} = require('http-proxy-middleware');

module.export = function(app) {
  app.use(
    '/api', // proxy가 필요한 path parameter를 입력
    createProxyMiddleware({
      target: 'http://localhost:5000', // 타겟이 되는 api url를 입력
      changeOrigin: true, // 대상 서버 구성에 따라 호스트 헤더가 변경되도록 설정
    })
  );
};
  • 그리고 기존의 fetch, 혹은 axios를 통해 요청하던 부분에서 도메인 부분 제거
    ➡️ webpack dev server에서 제공하는 proxy 기능을 사용할 때와 동일
export async function getAllfetch() {

    const response = await fetch('우회할 api주소/params');
    .then(() => {
			...
		})
}

export async function getAllfetch() {

    const response = await fetch('/params');
    .then(() => {
			...
		})
}
profile
프론트엔드 개발자

0개의 댓글