My Boundary As Much As I Experienced

프록시 사용을 고려해볼 여러 상황 (CORS에러 해결, API KEY 숨기기) 본문

FrontEnd/Next.js

프록시 사용을 고려해볼 여러 상황 (CORS에러 해결, API KEY 숨기기)

Bumang 2024. 3. 27. 20:24

프록시(Proxy)란?

클라이언트와 서버 사이에서 데이터를 전달해 주는 서버. 웹 캐시 기능이 있는 경우가 많으며, 방식에 따라 프론트의 IP주소를 숨기거나(포워드 프록시), 서버의 IP주소를 숨기거나(리버스 프록시) 할 수 있다고 한다.

 

 

공부하게 된 계기

이번 기술면접에서 CORS에러가 날 때 어떻게 해결할거냐는 질문을 받았다.

예전에 '빨리 잡아'라는 숙박 어플 만들 때 나를 고생시켰던 CORS에러..

그 당시 백엔드 개발자가 프론트 origin을 whitelist에 추가하면서 해결했던 경험이 기억이 났다.

 

'이를 해결하려면 클라이언트와 서버가 기본적으로 동일한 오리진이거나 아니면 허용된 오리진을 설정해야됩니다.

그러니 서버개발자에게 화이트리스트에 프론트엔드 오리진을 추가해달라고 요청할 것입니다.'

라는 대답을 했었다.

 

그러나 '같이 협업하는 백엔드 개발자가 아니라, 외부 API를 사용하는데 CORS에러가 발생하면 어떻게 할 것인가?'라는

CTO님의 2차 질문에 당황했었다. 내가 이용해본 외부 API는 거의 다 OPEN API였고, 호출하면서 CORS에러를 맞아본 경험은 없기 때문이었다.

 

집에 와서 찾아보니 같이 협업하는 회사의 API를 이용하면서 CORS에러를 맞아본 다른 개발자들의 후기들이 있었다.

협업하는 다른 회사의 api를 써야하는 경우도 있겠구나.. 그리고 그쪽 백엔드 개발자와 연락이 잘 안 되고 있는 경우도 발생할 수 있겠다.

이럴 때 손만 놓고 있기보다는 프록시 서버를 이용하여 프론트엔드 측의 origin 주소를 바꿔 해결할 수 있다고 한다.

이 뿐만 아니라, 프록시 서버의 용도는 더욱 다양하게 활용될 수 있다. 리버스 프록시를 이용하여 서버 엔드포인트를 숨길수도 있다고 하니, 다음 토이프로젝트에서 이런 프록시 서버를 활용해서 위와 같은 이점을 가져가봐야겠다.

 

 

1. 프록시 서버를 이용하여 개발 서버에서 CORS에러 해결하기

CORS에러는 이미 전에 포스팅한 경험이 있으므로, CORS에러 자체가 뭔지에 대한 자세한 설명은 생략하겠다..!

 

a. 남이 만든 프록시 서버를 사용하는 경우:

요청해야 하는 URL 앞에 남이 만든 프록시 서버 URL 을 붙여 요청하게 되면 해결할 수 있다.

 

https://cors-anywhere/herokuapp.com
↑ 이 프록시 서버를 꽤 많이 활용하는거 같다. 이걸로 요청을 가로채 HTTP 응답 헤더에 Access-Control-Allow-Origin : * 를 설정해 주면 된다고 한다. 다만 현재 무료 프록시 서버 대여 서비스들은 모두 악용 사례 때문에 api 요청 횟수 제한을 두어 실전에서는 사용하기 무리이다. 따라서 테스트용이나 맛보기용으로 사용하되, 실전에서는 직접 프록시 서버를 구축하여 사용하여야 한다.

 

axios({
  method:"GET",
  url:`https://cors-anywhere/herokuapp.com/{URL},
  header:{
	'APIKey':'....'
  }
})

 

 

나중에 이런 프록시 서버는 어떻게 만드는건지도 좀 찾아봐야겠다...

 

b. http-proxy-middleware 라이브러리 사용하기

역시 npm 라이브러리에는 없는게 없다.

아래처럼 설정해놓으면 api호출 시마다 nodejs 미들웨어처럼 작동하며 origin을 변경해준다고 한다.

그 뿐만 아니라,  pathRewrite: { "^/api:" } 속성을 추가하면, 프론트에서 노출되는 엔드포인트도 숨겨준다고 한다.

  • src/setupProxy.js
    • api/ 로 시작하는 요청 url 인 경우 설정한 target 주소에서 요청하는 것으로 둔갑해줌
    • /api 부분은 빠져야 하므로 pathRewrite 도 시켜준다.
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = (app) => {
    app.use(createProxyMiddleware('/api', {
        target: 'http://myservices.com',
        changeOrigin: true,
    })
    );
}

 

 

C. Create-React-App 환경이라면 손쉽게 package.json에서 해결하기

create-react-app으로 만든 파일의 package.json에서는 proxy라는 속성을 추가하여 손쉽게 origin변경을 할 수 있다고 한다.

왜 vite환경의 package.json에선 안 될까?.. 이 부분에 있어서는 추가 리서치를 해봐야겠다. 

{
  "name": "lol-state",
  "version": "0.1.0",
  "private": true,
  "proxy" : "https://kr.api.endpoint-you-want" // 해당 줄 추가.
  "dependencies": {
    "axios": "^0.18.0",
    "react": "^16.4.0",
    "react-animated-number": "^0.4.4",
    "react-circular-progressbar": "^0.8.0",
    "react-dom": "^16.4.0",
    "react-script": "^2.0.5",
    "react-scripts": "1.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

 

d. webpack-dev-server proxy 기능

cra 개발환경에서, webpack의 config에서도 proxy 설정하는 방법도 있다. 개인적으로 C 방법이 더 간편해보이긴 하나, 이런 방법도 있다는 점도 참고해보면 좋다. (그러나 이미 vite환경을 사용하는 것에 익숙해져서.. 사용해볼 일은 없을거 같다.)

// 프록시 쓰지 않았을때
// localhost:8080(클라이언트 측) --X (CORS)--> domain.com (서버 측)

// 프록시를 설정 후
// localhost:8080(클라이언트 측) --O 프록시가 설정된 Webpack Dev Server--> domain.com (서버 측)

module.exports = {
  devServer: {
    proxy: {
      "/api": {
        target: "domain.com",
        changeOrigin: true,
      },
    },
  },
};

중간의 프록시 서버 덕분에, (만약 서버 엔드포인트가 domain.com라면) 서버에서는 같은 도메인(domain.com)에서 온 요청으로 인식하여 CORS 에러가 발생하지 않는다.

 

 

e. next.js에서의 프록시 설정 (next.config.js)

넥스트js 환경에서도 config파일을 수정하여 손쉽게 프록시 설정을 할 수 있다.

다음과 같은 내용을 적어주면 쉽게 localhost3000주소에서 localhost5000주소로 요청을 보내도 proxy로 CORS정책을 우회할 수 있다.

 

넥스트 공식문서: https://nextjs.org/docs/pages/api-reference/next-config-js/rewrites

module.exports = {
	async rewrites() {
		return [
			{
				source: "/:path*",
				destination: "http://localhost:5000/:path*",
			},
		];
	},

 

 

 

2. 리버스 프록시를 이용하여 API 엔드포인트를 숨기기

사실 환경변수를 사용하여 로컬에서 api 엔드포인트를 감추고, 이를 github나 vercel의 환경변수로 등록하여 배포해본 경험은 있다.

(firebase hosting의 경우 github action등을 통해 환경변수를 전달해야되어 좀 더 귀찮다.)

그러나 이렇게 환경 변수로 가리는 것은 github같은 외부 저장소에서 api_key를 노출하지 않기 위함일 뿐이다.

실제로 배포 사이트에서 network탭에 들어가면 내가 보내는 요청들의 api endpoint는 그대로 노출된다.

 

물론 허용된 origin이 아니라면 차단되겠지만, 공격받지 않으려면 아예 포착되지 않는 것이 제일 좋다.

 

이를 위해, NginX 혹은 Docker를 reverse proxy로 사용하여 endpoint를 감추는 방식을 많이 사용한다고 한다.

그러나 국내 포스팅 글에서는 실제로 구현하는 과정이 나와있는 글이 별로 없어서 영어 검색을 하여 아래의 글들을 찾았다.

NginX에 대한 배경지식이 아직 부족하여 정확히 이해하진 못하겠다.

 

지금 듣고 있는 Node강의의 커리큘럼에 NginX를 다뤄보는 부분도 있던데

다 듣고난 뒤 아래 부분들에 대해 추가 스터디 후 정리를 해야겠다.

 

NginX의 공식문서:

https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/

 

https://stackoverflow.com/questions/77855913/nginx-reverse-proxy-to-hide-my-real-servers

 

https://medium.com/rahasak/nginx-reverse-proxy-with-url-rewrite-a3361a35623c

 

 

 

참고 자료: 

https://inpa.tistory.com/entry/WEB-📚-CORS-💯-정리-해결-방법-👏 

https://lazywon.tistory.com/69

https://ryulog.tistory.com/138

https://velog.io/@zero-black/CORS-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0-Reverse-Proxy-server-%EA%B5%AC%EC%B6%95-with-Nginx

https://velog.io/@zero-black/CORS-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0-Reverse-Proxy-server-%EA%B5%AC%EC%B6%95-with-Nginx