웹 브라우저의 보안 정책으로 서로 다른 출처(Origin) 간의 리소스 요청을 제한하는 방식.
- 출처: 프로토콜, 도메인, 포트가 모든 같은 경우
브라우저에서는 기본적으로 클라이언트 측 스트립트로 다른 출처에 직접적으로 API요청 보낼 때, 브라우저의 현재 주소와 API의 주소 도메인이 일치해야만 데이터를 접근할 수 있게 되어 있다. 다르면 브라우저에서 차단한다.
예를 들면, 로컬에서 개발할 시 기본적으로 프로젝트의 클라이언트 부분에서localhost:3000으로 시작한다. 프로젝트의 클라이언트에서 서버의 API로 요청하게 되면, 이 포트의 요청을 차단한다.
만약 다른 도메인이세 API를 요청해서 사용할 수 있게 해주려면 CORS 설정이 필요하다.
그렇다고 모든 출처의 접근을 허락한다면 큰 문제를 야기할 수 있다.
서버와 연결되어 있는 DB에 라이브 데이터(live data)가 쌓인다. 라이브 데이터는 민감성이 높은 데이터들 위주이기 때문에 보안이 중요하다. 그래서 모든 출처의 접근을 허락한다면 보안성이 낮아진다.
따라서, 모든 도메인을 허용해서는 안 되고, 특정 도메인을 허용하도록 구현해야 한다.
프론트엔드 개발자가 백엔드 개발자에게 프론트엔드 개발 서버 도메인을 허용해 달라고 요청을 해야하고, 백엔드 개발자는 응답 헤더에 필요한 값들을 담아서 전달을 해줘야 한다. 서버에서 적절한 응답 헤더를 받지 못하면 브라우저에서 에러가 발생한다.
이러한 과정 없이 React 라이브러리 아니면 Webpack Dev Server에서 제공하는 proxy 기능을 사용하면 CORS 정책을 우회할 수 있다.
이렇게 하면 브라우저는 응답 헤더를 받을 필요 없이 React 앱으로 데이터를 요청하고, 해당 요청을 백엔드로 전달하게 된다. 여기서 React 앱이 서버로부터 받은 응답 데이터를 다시 브라우저로 전달하는 방법을 쓰기 때문에
- 익명성 보장: 클라이언트의 IP 주소를 웹 서버에 노출시키지 않고, 프록시 서버의 IP주소로 대신 통신하여 클라이언트의 실제 위치를 숨길 수 있다.
- 캐싱: 프록시는 이전에 요청한 리소스를 캐시에 저장하여 동일한 리소스를 반복적으로 요청하는 경우, 웹 서버에 다시 접근하지 않고 저장된 캐시에서 직접 제공한다. 이는 속도를 향상시키고 네트워크 트래픽을 줄인다.
- 보안: 프록시는 외부와의 직접 통신을 차단하고, 웹 애플리케이션 뒷단에 있는 서버로 요청을 보내므로 보안측면에서 추가적인 보호를 제공한다.
- 필터링: 프록시는 웹 콘텐츠에 대한 접근 권한을 관리할 수 있어서 부적절한 콘텐츠에 대한 액세스를 차단하거나 제한하는 데 도움이 된다.
- 로드 밸런싱: 여러 대의 웹 서버가 있는 경우 프록시는 이들 사이에서 요청을 분산시켜 로드 밸런싱을 수행하여 서버의 부하를 고르게 분산시킬 수 있다.
- 클라이언트 애플리케이션에서 서버로 요청을 생성한다.
- 그 요청을 브라우저로 보내져 접근 권한이 있는지 확인하는 요청을 서버로 보낸다.
- 서버는 요청을 받아서 처리하고, 요청에 대한 응답을 생성한다.
- 서버는 브라우저로 응답을 준다.
- 브라우저는 받은 리소스 및 응답과 함께 출처가 같은지 아닌지를 확인한다.
- 출처가 다르면 CORS 에러를 발생시키고 응답을 파기하고, 출처가 같으면 응답을 파기하지 않고 클라이언트쪽으로 응답을 보낸다.
- 클라이언트 애플리케이션에서 요청을 생성한다.
- 클라이언트 애플리케이션은 프록시로 요청을 보낸다.
- 프록시 서버는 받은 요청을 서버로 전달한다.
- 서버는 요청을 처리하고, 응답을 프록시 서버로 반환한다.
- 프록시 서버는 응답을 클라이언트 애플리케이션으로 전달한다.
- 클라이언트 애플리케이션은 프록시로부터 받은 응답을 처리한다.
프록시 서버와 클라이언트 애플리케이션은 동일한 출처를 가지기 때문에 브라우저는 이를 같은 출처로 인식한다. 브라우저를 속여서 CORS 에러를 없애고 허용하게 만든다.
웹팩(Webpack)은 주로 자바스크립트 애플리케이션의 번들링과 모듈 번들러로서의 역할을 수행하는 도구다.
개발단계에서는 웹팩으로 번들링된 애플리키에션을 빠르게 테스트하고, 개발하는데 필요한 서버가 필요한데, 이를 위해 사용하는 것이 웹팩 개발 서버(Webpack Dev Server)다.
// pakage.json
(...),
"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:3080"
}
proxy에 연결할 서버 주소를 적는다.
이후 fetch나 axios 같이 통신할 때, 도메인 부분을 제거하고 적는다.
webpack dev server의 proxy 설정은 전역적으로 하나의 주소만 설정할 수 있기 때문에 유연하게 적용할 수 없다.
http-proxy-middleware 라이브러리를 사용해서 유연하게 대응할 수 있다.
http-proxy-middleware 는 Node.js 기반 웹 개발 환경에서 프록시를 설정하고 관리하기 위한 미들웨어 패키지다. 웹팩 개발 서버에서는 주로 개발 중에 로컬 환경에서 외부 API와의 통신을 프록시로 처리하기 위해 이 패키지를 사용한다.
npm install http-proxy-middleware --save
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api', //proxy가 필요한 path prameter를 입력합니다.
createProxyMiddleware({
target: 'http://localhost:3080', //타겟이 되는 api url를 입력합니다.
changeOrigin: true, //대상 서버 구성에 따라 호스트 헤더가 변경되도록 설정하는 부분입니다.
})
);
app.use(
'/api2',
createProxyMiddleware({
target: 'http://localhost:3070',
changeOrigin: true,
})
)
};
이렇게 주소를 여러 개 적어서 프록시 설정을 해줄 수 있다. 여러 라우트의 접근이 가능하다.
확실히 클라이언트, 서버 따로 CORS 설정을 하는 것이 간편해 보인다. 빌드나 배포할 때는 지우자!