React 의 App.js 에서 fetch를 통해 express 에 연동된 DB 데이터를 불러들이는 과정에서 계속해서 에러가 났다.
useEffect(() => {
callApi()
.then(res => setMusics(res))
.catch(err => console.log("this is error " + err));
}, [])
async function callApi() {
const response = await fetch('/musicdata')
const body = await response.json(); // <- 에러가 계속 나는 부분
return body;
}
원래 대로라면 setMusics(res)를 통해 musics 에 데이터가 들어가고 확인이 되어야하는 데 그러지않아 우선 response를 console.log로 찍어보니 이상이 없었고 body를 찍어보니 이상이 있음을 알았다.
'Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0' 라는 에러가 계속 나왔고 인터넷에 검색해보니 나와 똑같은 문제를 겪고 있는 사람을 발견했고 주소 앞에 express rest api 가 돌아가고 있는 host 이름과 포트번호 까지 주소에 포함해서 적어서 해결했다는 것을 확인했다.
const response = await fetch('/musicdata')
//을
const response = await fetch('http://localhost:5000/musicdata')
//으로 변경
위 처럼 코드를 변경하였지만 다른 에러가 발생하였고 '~has been blocked by CORS policy~' 라는 문구를 포함한 에러였다. 역시 에러문을 검색했더니 CORS 이슈 관련 문제 임을 알게 되었고 CORS가 무엇인지에 대해 검색해봤다.
CORS
Cross-Origin Resource Sharing 의 약자로써 한글로 간략히 풀자면 '출처가 교차되는 자원이 공유되고 있다', 즉 출처가 다른 자원이 공유되고 있다 라는 뜻이 되겠다. 브라우저에서 사이트 끼리 데이터를 주고받는 과정에서, 도메인 및 포트 번호가 다른 사이트들 간에 api 요청을 할때 브라우저가 보안상의 이유로 api 를 막는 것이다. 현재 내 프로젝트의 경우 5000 포트 로 구동 되고 있는 서버(express) 에서 3000 포트 로 구동되고 있는 클라이언트(App.js)에 데이터를 전송하려했기 때문에, CORS 에러가 발생한 것이다.
해결방법
1. 단순 요청 (Simple requests)
이 방법은 교차 출처 리소스 공유 표준으로 브라우저에서 해당 정보를 읽는 것이 허용된 출처를 서버에서 설명할 수 있는 새로운 HTTP 헤더를 추가함으로써 동작한다. 서버가 적절한 헤더를 전송하지 않으면 요청자에게 응답 데이터가 공개되지 않는다. 다시 말해 서버에서 데이터를 전송할 때 클라이언트에서 이 정보를 읽을 수 있는지 확인 할수 있는 헤더를 덧붙여보낸 다는 것이다. 이 방법은 원래 코드에 단 한줄만 추가하면 됐고 나는 이 방법으로 간단히 해결할수 있었다.
app.get('/musicdata', (req, res) => {
connection.query('SELECT * from musicdata', (error, rows) => {
if (error) throw error;
res.header("Access-Control-Allow-Origin", "*") //추가
res.send(rows);
});
});
응답의 헤더에 "Access-Control-Allow-Origin : *" (와일드 카드) 을 덧붙이는 것이고, 이것은 모든 사이트 들이 cross-site방식으로 이 도메인에 접근할 수 있다는 것을 의미한다. 만일 특정 사이트 하나만이 접근할수 있길 원한 다면 "*"이 아닌 그 사이트의 도메인이 들어가야 한다.
2. 프리플라이트 요청 (Preflighted request)
위의 단순 요청과 달리 먼저 OPTIONS 메서드(목표 리소스와의 통신 옵션을 설명하기 위해 사용) 를 통해 다른 도메인에HTTP 요청을 보내 실제 요청이 전송하기에 안전한지 확인한다. cross-site 요청은 유저 데이터에 영향을 줄 수 있기 때문에 이와같이 미리 전송하는 것이다.
위의 그림처럼 Main request라는 실 요청이 따로있고 인증헤더를 전송하여 그 이전에 서버의 허용여부를 미리 체크하는 것이다.
3. 인증정보를 포함한 요청 (Credentialed requests)
인증정보를 포함한 요청은 HTTP cookies 와 HTTP Authentication 정보를 인식한다. 기본적으로 XMLHttpRequest 나 Fetch 호출에 있어서 브라우저는 자격 증명을 보내지 않는다. 그렇기 때문에 XMLHttpRequest 객체나 Request 생성자가 호출될 때 특정 플래그를 설정해야한다.
const invocation = new XMLHttpRequest();
const url = 'http://bar.other/resources/credentialed-content/';
function callOtherDomain() {
if (invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true; //플래그
invocation.onreadystatechange = handler;
invocation.send();
}
}
위는 XMLHttpRequest 호출을 통한 예제이며 'withCredentials' 라는 플래그를 설정한 것을 볼 수 있다. 이것을 통해 credentialed request가 수행된 것이고 서버측에서 'Access-Control-Allow-Credentials: true' 로 응답하지 않으면, 응답은 무시된다. 또한 이 credentialed request에 응답할 때 'Access-Control-Allow-Origin' 헤더에 "*" (와일드카드)를 사용하는 대신에 반드시 요청하는 도메인 값을 지정해야 한다.
'개발일지 > React' 카테고리의 다른 글
[음악 관리 프로그램] 메인 페이지 업데이트 (useEffect) (0) | 2020.09.19 |
---|---|
[음악 관리 프로그램][에러 해결] 404, 500 ERROR (0) | 2020.09.16 |
[에러해결] Invalid Hook Call Error (1) | 2020.09.14 |
[음악 관리 프로그램 만들기] node JS - MySQL - React JS 연동하기 (0) | 2020.09.05 |
yarn eject 에러가 났을 때 (0) | 2020.05.03 |