URL을 분해하면 다음과 같다. 이때 출처는 protocol과 host 그리고 포트번호까지 합쳐진 것을 의미한다.
웹 생태계에서는 다른 출처로 리소스 요청을 제한하는 것과 관련된 두가지 정책이 있다.
SOP은 같은 출처에서만 리소스 공유를 허용하도록 하는것이다. 그러나 웹이라는 오픈스페이스 환경에서 다른 출처에 있는 리소스를 가져와서 사용하는 일은 굉장히 흔한 일이다. 그래서 몇가지 예외 조항을 두고 이 조항에 해당하는 리소스 요청은 출처가 다르더라도 허용하도록 했다. 이를 CORS정책을 지킨 리소스 요청이라 한다.
다른 출처로 리소스 요청을 하면 SOP 정책 위반이고 SOP의 예외 조항인CORS 정책까지 지키지 않는다면 다른 출처의 리소스를 사용할 수 없게 된다.
그런데 이 출처를 비교하는 로직이 브라우저(클라이언트)에서 일어난다. 우리가 CORS 정책을 위반하는 리소스 요청을 하더라도 해당 서버가 같은 출처에서 보낸 요청만 받겠다는 로직을 가지고 있는 경우가 아니라면 서버는 정상적으로 응답하고 브라우저에서 이 응답을 분석하여 폐기시킨다. 즉 브라우저와 서버간의 통신이 아닌 서버간의 통신을 할 때는 CORS 정책에 제한받지 않는다.
인터넷에 검색하면 CORS에러를 해결할 수 있는 여러가지 방법이 나와있다. 나는 proxy server를 만들어서 데이터를 받아 왔다.
기본적인 틀은 다음과 같다.
cors 모듈
express로 만든 서버에서 cors 모듈을 설치해서 미들웨어로 처리해준다. cors모듈은 서버측 응답 헤더에 접근 권한을 주는 헤더를 추가해준다. cors모듈에 옵션을 추가해주면 특정 도메인이나 특정 요청에만 응답하도록 설정할 수 있다. 나는 일단 옵션을 설정하지 않았다.
server.js
//server.js
const express = require('express');
const app = express();
const cors = require('cors');
//cors 모듈
const foodData = require('./foodData.js');
app.use(cors());
//app.use() : 미들웨어를 app에 바인딩
//app.get() : app.get(path, Handler)
//=>path로 요청이 들어오면 Handler가 동작
app.get('/', async(req, res)=> {
const { foodName } = req.query;
//client에서 query로 전해준 데이터
await foodData(foodName ,(error, data) => {
if(error){
res.send(error)
}else{
console.log('프록시 서버에서:',data)
res.send(data)
}
})
})
app.listen(8080, ()=>{
console.log('서버 8080포트 연결 성공')
})
foodData.js
//공공데이터 포탈에서 데이터 가지고 오는 함수
const fetch = require('node-fetch')
const serviceKey =''
const foodData = (foodName,callback) => {
const url = `http://apis.data.go.kr/1471000/FoodNtrIrdntInfoService1/getFoodNtrItdntList1?serviceKey=${serviceKey}&desc_kor=${foodName}&pageNo=1&numOfRows=3&type=json`;
fetch(url,{method: 'GET'})
.then((res)=>res.json())
.then((data)=>{
const foodData = data.body.items
callback(undefined, foodData)
})
.catch((error)=>{
console.log('에러 발생', error)
callback(error)
})
}
module.exports = foodData
App.js
function App() {
const inputRed = useRef();
const submitHandler = (event) => {
event.preventDefault();
const enteredFoodName = inputRed.current.value;
// input에 입력한 값 query로 프록시 서버로 전달
fetch(`http://localhost:8080?foodName=${enteredFoodName}`)
.then((res)=>res.json())
.then(data => console.log(data))
}
return (
<div className="App">
<form onSubmit={submitHandler}>
<input ref={inputRed} />
<button type='submit'>제출하기</button>
</form>
</div>
);
}
export default App;
발생한 에러
1. Error [ERR_REQUIRE_ESM]: require() of ES Module [node-fetch 경로] from [import한 위치] not supported.
Error: ERR_UNESCAPED_CHARACTERS
=>해결방법
처음에 axios로 모든 요청을 보냈는데 다음과 같은 에러가 떴다. 아마 요청할 때 쿼리에 한글이 들어가서 발생한 에러같은데 그래서 url을 encode를 해줬더니 다음 에러는 뜨지 않았다.
그런데 axios로 요청 보낼 때 다른 에러가 떠서 fetch를 깔아줘서 fetch로 요청을 보냈는데 이번엔 encode한 url을 쓰니깐 에러가 났다. 그래서 결과 코드에서는 encode한 url말고 그냥 url로 요청을 보냈는데 이게 어떻게된 영문인지 모르게쒀;;;
Getting connect ECONNREFUSED 127.0.0.1:80 when attempting HTTP request
위에서 axios를 썼을 때 에러가 떴다 그랬는데 그 에러가 이 에러다. 그래서 fetch로 바꿔주니깐 해결되긴 했는데...
=>해결방법
TypeError: Converting circular structure to JSON
공공 데이터 api에서 응답확인하겠다고 console.log(response)넣었더니 다음과 같은 에러 떴다.
=>해결방법