브라우저에서는 기본적으로 동일한 출처에서한 요청만 받아드리는 SOP정책을 따른다.
여기서 Origin(출처)가 동일하다는 뜻은 => 프로토콜, 도메인, 포트번호가 동일하다는 것을 의미한다. 아래 예시를 보면 이해가 쉬울것이다.
http://example.com/ex //이 도메인에서 요청한다고 가정
http://example.com/user/1 // 성공, 프로토콜 http로 동일하고, example.com으로 호스트가 같음.
https://example.com/user/1 // 실패, 프로토콜이 HTTP, HTTPS로 동일하지 않음
http://notexample.com/user/1 // 실패, 호스트가 example.com, notexample.com으로 동일하지 않음
http://example.com:81/user/1 // 실패, HTTP는 기본 포트가 80이나 여기서 81으로 요청을 해 포트가 동일하지 않음.
Origin이 다른 곳에서 요청을 보냈을 때, 예외적으로 특정조건을 갖춘 경우 요청을 허용해주는 정책이다. (SOP의 예외상황이라고 보면 된다 => 출처가 달라도 요청을 허용해주는 것.)
보안상의 이유로 SOP가 생겨났다. 하지만, 웹과 브라우저의 발전이 고도화되면서 출처가 다른 곳에서도 데이터를 가져올 필요가 많아졌고, 이를 해결하기 위해 CORS라는 예외정책을 둔 것이라고 생각하면 쉽다.
서버는 CORS 정책예외를 구현하도록 도와주는 것 뿐, 결국 출처를 판단하는 것은 브라우저 상에서 이뤄진다.
브라우저는 Http 프로토콜을 이용해 요청을 보냄 > 헤더에 origin이라는 필드를 출처에 담아 보낸다.
서버는 응답헤더에 Access-Control-Allow-Origin을 담아 클라이언트로 전달한다.
(이 리소스를 접근하는 것이 허용된 출처)
이후 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 차단할지 말지를 결정한다.
만약 유효하지 않다면 그 응답을 사용하지 않고 버린다. (CORS 에러 !!)
보통 CORS는 서버쪽에서 handling 하는게 일반적이다.
CORS를 판단하는 로직은 브라우저에서 요청에 대한 응답을 받았을 때 이뤄지는데, 이때 헤더에Access-Control-Allow-Origin 값이 존재한다면, CORS에러가 발생하지 않는다.
즉, 서버에서 요청이 온 브라우저의 출처를 Access-Control-Allow-Origin 값에 넣어서 보내주면 해결된다. 아니면 CORS를 처리하는 미들웨어를 사용하는 방법도 존재한다.
하지만 외부 API를 사용하는 경우, 서버쪽의 코드를 수정 할 수 없는 경우들이 있다. 이때는 클라이언트에서 CORS를 처리해야한다.
클라이언트에서는 Proxy 서버를 이용해 CORS를 처리한다.
즉,서버로 요청을 보내기전 중개서버를 통해 출처를 같게 수정하고 서버에 접근하도록 하면 CORS 에러를 해결 할 수 있다.
구현과제에서 프록시 서버로는 http-proxy-middleware를 사용했다.
먼저 다음과 같이 src폴더안에 setupProxy.js파일을 생성한다.
React에서는 setupProxy.js 파일이 있으면 따로 설정을 해주지 않아도 자동으로 반영한다.
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
app.use('/v1',
createProxyMiddleware({
target: 'https://api.kakao.com',
changeOrigin: true,
}),
);
};
여기서 /v1은 api의 endpoint이다. 즉 api가 서버에서 리소스에 접근할 수 있도록 가능하게 하는 url이라고 볼 수 있다.
즉, '/v1'으로 시작하는 endpoint를 가진 모든 api를 target으로 주소를 바꿔주는 것이다.
target에는 내가 출처를 바꿀 서버의 출처를 적어주면 된다.
여기서 changeOrigin은 호출시 출처를 target값으로 바꿀지를 결정하는 여부이다.
const fetchData = async () => {
const datas = await API.get('/v1/music/mylist');
setMusicInfo(datas);
setLoading(false);
console.log(datas);
};
useEffect(() => {
fetchData();
}, []);
ref)
CORS : https://inpa.tistory.com/entry/WEB-📚-CORS-💯-정리-해결-방법-👏#CORS_에러_한방_이해하기