Proxy에 대해서 알아보기 전에 이전 섹션에서 배운 CORS 에 대해서 알고 가자면 CORS는 Cross-Origin Resource Sharing의 줄임말로, 한국어로 직역하면 교차 출처 리소스 공유라고 해석할 수 있다.
웹 개발을 하다보면 반드시 마주치는 에러중 하나라고 배웠다...
우리가 어떤 사이트를 접속할때 인터넷 주소창에 우리는 URL이라는 문자열을 통해 접근하게 된다.
Origin 을 출처라고 부르는데 Protocol, Host, Port 로 구성되어 있는 URL 이라고 생각하면 된다.
CORS 에러가 나는 이유는 같은 출처(Origin)의 리소스만 공유가 가능하다는 정책인 SOP 라는 정책 때문에 CORS 에러가 나오는 것이다. Protocol, Host, Port로 구성되어 있는 이 Origin이 하나라도 다르면 동일한 출처로 보지 않는다.
예를 들면 내가 네이버에서 로그인을 하고서 로그아웃을 하지 않았거나 자동 로그인 기능으로 브라우저에 로그인 정보가 남을 수도 있다. 그 상태에서 내가 내 로그인 정보를 노리는 다른 사이트에 방문하면 해커가 나의 로그인 정보를 이용할 수도 있기 때문에 다른 사이트와의 리소스 공유를 제한하는 SOP가 생겨나게 되었다.
이러한 보안상 이점 때문에 SOP은 모든 브라우저에서 기본적으로 사용하고 있는 정책이다.
React 앱을 개발할 때 다른 API 서버와 통신을 가능하게 해주는 라이브러리 이다.
우리가 React 앱 개발 단계에서는 로컬 환경에서 앱을 실행한다.
개발을 할 때 API 서버랑 통신이 필요한 경우가 많이 필요하게 되는데 이때 SOP 정책에 의해서 CORS 에러를 자주 접하게 된다.
하지만 우리는 개발하는 단계에서는 리액트의 Proxy를 사용하면 CORS 에러 같은 걸 쉽게 해결해준다.
이는 별도의 응답 헤더를 받을 필요 없이 브라우저는 React 앱으로 데이터를 요청하고, 해당 요청을 백엔드로 전달하게 된다.
React 앱에서 브라우저를 통해 API를 요청할 때, proxy를 통해 백엔드 서버로 요청을 우회하여 보내게 되는데 그러면 백엔드 서버는 응답을 React 앱으로 보내고, React 앱은 받은 응답을 백엔드 서버 대신 브라우저에게 전달한다. 이렇게 되면 출처가 같아지기 때문에 CORS 정책을 위반하지 않는다.
✔ 배포 단계에서는 쓰이지 않기 때문에 따로 CORS 설정을 해주어야 한다.
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(() => {
...
})
}
webpack dev server에서 제공하는 proxy는 전역적인 설정이기 때문에, 종종 해당 방법이 충분히 적용되지 않는 경우가 생기기도 한다. 그래서 수동으로 proxy를 적용해줘야 하는 경우가 있는데, 이때는 http-proxy-middleware 라이브러리를 사용해야 한다.
터미널에서 라이브러리를 설치한다.
npm install http-proxy-middleware --save
그리고 React App의 src 파일 안에서 setupProxy.js 파일을 생성하고, 안에서 설치한 라이브러리 파일을 불러온 다음, 아래와 같이 작성을 한다.
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api', //proxy가 필요한 path prameter를 입력합니다.
createProxyMiddleware({
target: 'http://localhost:5000', //타겟이 되는 api url를 입력합니다.
changeOrigin: true, //대상 서버 구성에 따라 호스트 헤더가 변경되도록 설정하는 부분입니다.
})
);
};
그리고 기존의 fetch, 혹은 axios를 통해 요청하던 부분에서 도메인 부분을 제거한다.
export async function getAllfetch() {
const response = await fetch('우회할 api주소/params');
.then(() => {
...
})
}
export async function getAllfetch() {
const response = await fetch('/params');
.then(() => {
...
})
}
페어와 함께 webpack dev server의 proxy 기능을 사용해 우회하여 응답을 받아옵니다.
먼저 api, api2, my-app 에서 전부 npm install 을 해주고 각 터미널에서 개발 서버를 열었다.
서버를 처음 열어보면
이러한 CORS 에러가 나오고 있다.
처음은 webpack dev server의 proxy 기능을 사용하여 CORS 에러를 없애 보았다.
my-app 폴더 src 안에 있는 package.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"
}
이런식으로 작성해 위에서 배운대로 그대로 적용 했다.
그리고 BookService.js 에서
export const getAllBooks = async () => {
const response = await fetch('/api/books');
return await response.json();
};
export const createBook = async (data) => {
const response = await fetch('/api/book', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ book: data }),
});
return await response.json();
};
로컬호스트를 지워주니까 CORS 에러가 사라졌다.
레포지토리로 받아온 과제를 살펴보면 api2라는 폴더가 존재하고 있습니다. 실제로 프로젝트 및 실무를 할 때, 하나의 도메인이 아닌 여러 개의 도메인에서 응답을 받아와야 하는 경우가 종종 있습니다. 이럴 때는 유연하게 proxy를 설정할 필요가 있습니다.
페어와 함께 webpack dev server의 proxy 기능 대신 http-proxy-middleware의 proxy 기능을 사용하여 proxy를 유연히 설정해 2개의 도메인에서 모두 응답을 받아옵니다.
페어와 함께 api2에 관련된 fetch 함수를 만들고, 컴포넌트를 하나 이상 만들어 2개의 도메인에서 모두 응답을 받아오는지 테스트해봅니다.
이번엔 http-proxy-middleware를 사용하기 위해서 먼저 my-app에서 라이브러리를 설치해주었다.
npm install http-proxy-middleware --save
그리고 설명 그대로 setupProxy.js 파일을 생성하고 2개의 도메인에서 모두 응답을 받아 오기 위해 이처럼 작성했다.
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:3080', //api
changeOrigin: true,
})
);
app.use(
'/api2',
createProxyMiddleware({
target: 'http://localhost:3070', //api2
changeOrigin: true,
})
);
};
그 이후 과제 1에서 package.json 에서 작성한
"proxy" : "http://localhost:3080"
를 다시 지운 후에 My books 컴포넌트를 그대로 카피하여 api2 에 들어있는 데이터를 보고 My Todos 를 만들어 주었다.
api2의 데이터도 받아올때
export const getAllBooks = async () => {
const response = await fetch('/api/books');
return await response.json();
};
export const createBook = async (data) => {
const response = await fetch('/api/book', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ book: data }),
});
return await response.json();
};
export const getAllTodos = async () => {
const response = await fetch('/api2/todos');
return await response.json();
};
export const createTodo = async (data) => {
const response = await fetch('/api2/todo', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ todo: data }),
});
return await response.json();
};
이런식으로 localhost 도메인을 지워주어야 한다.
결과
CORS 에러 없이 각각 데이터를 잘 받아오는 모습을 볼수 있었다.
배운지 좀 시간이 지나서 CORS 에러에 대해서 거의 잊어버리고 있었다.. 그래서 다시 섹션을 돌아가서 복습을 한 후에 과제를 진행했다. 오늘 다시 복습하면서 잊고 있었던 CORS 정책에 대하여 다시 알아보고 개발단계에서 CORS 에러 없이 편하게 작성하는 방법을 배워서 좋았다. 😉