02 BookBucket - 내가 추가할 책을 참고해보자!📚[2.20]
2. Api로 베스트셀러 보여주기
1) 📕 아이콘 눌렀을 때
(1) 베스트 셀러 페이지로 이동
⓵ router 설치
npm install react-router-dom@6
⓶ BrowserRouter 컴포넌트를 최상위 태그에 감싸주자.
[index.js]
import React from "react";
import ReactDOM from "react-dom/client";
//import
import { BrowserRouter } from "react-router-dom";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
{/*이렇게 감싸라*/}
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
import도 하고 BrowserRouter 감싸고
⓷ 본격적으로 router 사용하자
import { Routes, Route, useNavigate } from "react-router-dom";
import BookSearch from "./pages/BookSearch/BookSearch";
- import 해주고
<Routes>
{" "}
<Route path="/" element={<Main />}>
{" "}
</Route>
<Route path="/bookSearch" element={<BookSearch />}>
{" "}
</Route>
<Route path="*" element={<div>없는 페이지에요</div>} />
</Routes>{" "}
- 경로설정 해주고
let navigate = useNavigate();
- useNavigate() 변수로 저장해두고
<button>
<FontAwesomeIcon
icon={faBook}
className="bookIcon cursor"
onClick={() => {
navigate("/bookSearch");
}}
/>
</button>
- 📕 아이콘 버튼을 클릭했을 때 /bookSearch로 이동해봅시다.
⓸ 이동 완료
2) 베스트 셀러 도서 api 가지고 오기
(1) api를 가지고 올 데이터를 list state에 저장
const [list, setList] = useState([]);
(2) 이용 등록을 통해 받은 인증키를 apiKey에 저장
const apiKey =
//비밀이지롱~
"*************************";
(3) useEffect를 통해 첫 렌더링했을 때 axios 실행
useEffect(() => {
axios
.get(
`http://book.interpark.com/api/bestSeller.api?key=${apiKey}&categoryId=100)
}, []);
key=interpark라고 되어있는 부분에 인증키를 저장해둔 apiKey를 넣는다.
(4) 출력방식을 쿼리스트링한다.
http://book.interpark.com/api/bestSeller.api?key=${apiKey}&categoryId=100&output=json
(5) .then으로 api로 받아질 데이터들을 state에 저장 시킨다.
useEffect(() => {
axios
.get(
`http://book.interpark.com/api/bestSeller.api?key=${apiKey}&categoryId=100&output=json)
.then((res) => {
setList(res.data.item);
})
.catch((error) => console.log("error", error));
}, []);
자 근데 여기서 문제가 생겼다. 굉장히 곤란하다.
xhr.js:247 GET http://cors-anywhere.herokuapp.com/http://book.interpark.com/api/bestSeller.api?key=5196C67D1E3BA4FBD1181C4103213BE2D73BFDFE73AC5DC257632D7DA1A15293&categoryId=100&output=xml 403 (Forbidden)
이는 CORS(Cross-Origin Resource Sharing) 정책이 적용된 서버에서, 다른 출처의 리소스를 요청할 때 발생하는 보안상의 에러다.
자 그럼 어떻게 해야하는가?
- 서버에서 CORS 정책 변경: 서버의 관리자가 응답 헤더를 변경하여 CORS 정책을 완화하거나 해결할 수 있습니다.
- 프록시 서버 사용: 클라이언트에서 요청하는 API를 프록시 서버에서 요청하도록 변경하여, 프록시 서버에서 CORS 정책을 우회할 수 있습니다.
- JSONP 사용: 일부 API에서는 JSONP(JSON with Padding) 방식을 지원하여, 이를 이용하여 요청할 수 있습니다. 이 방식은 스크립트 태그를 이용하여 다른 출처의 데이터를 요청하는 방식으로, CORS 정책을 우회할 수 있습니다.
나는 프록시 서버 사용으로 문제를 해결 할 것이다.
API 서버에 대한 프록시를 구성하려면, 먼저 자신의 도메인과 API 서버의 도메인이 다른 경우, 브라우저에서 CORS 에러를 방지하기 위해 프록시 서버를 만들어야 합니다. 이를 위해 보통 CORS-Anywhere 라이브러리와 같은 서드파티 프록시 서버를 사용하거나, 자체적으로 프록시 서버를 만들어 사용합니다.
자체적으로 프록시 서버를 만드는 방법은
- 서버 측에서 프록시 API 라우터를 만듭니다.
- 클라이언트 측에서, API 요청 주소 앞에 프록시 서버 주소를 붙입니다.
- 프록시 API 라우터에서, 클라이언트가 요청한 API 주소로 요청을 보냅니다.
- API 서버에서 응답을 받은 후, 프록시 서버를 통해 클라이언트로 응답을 전달합니다.
CORS-Anywhere 라이브러리를 사용해서 프록시 서버를 사용해보자.
const proxyUrl = "https://cors-anywhere.herokuapp.com/";
proxyUrl 변수에는 CORS-Anywhere 프록시 서버 주소가 할당이된다.
.get(
`${proxyUrl}http://book.interpark.com/api/bestSeller.api?key=${apiKey}&categoryId=100&output=json`
)
axios.get() 함수의 요청 주소 앞에 이 주소가 붙여져서 요청이 전송된다.
이렇게 하면 CORS 에러를 우회하여 API 요청이 성공적으로 이루어진다.
(6) 쿼리스트링 개선으로 더 깔끔하게 사용하자.
⓵ 주소에 들어갈 내용을 변수에 저장
const params = {
key: apiKey,
output: "json",
categoryId: "100",
};
⓶ 두번째 인자로 { params } 전달하면 끝!
.get(`${proxyUrl}http://book.interpark.com/api/bestSeller.api`, {
params,
})
(7) async await을 사용하자
- 훨씬 기존에 쓰던 것보다 보기도 좋고 성능도 좋다
useEffect(() => {
getInterparkBook();
}, []);
// async await
const getInterparkBook = async () => {
const params = {
key: apiKey,
output: "json",
categoryId: "100",
};
try {
const response = await axios.get(
`${proxyUrl}http://book.interpark.com/api/bestSeller.api`,
{
params,
}
);
return setList(response.data.item);
} catch (error) {
console.log("error", error);
}
};
(8) ui에 보여지도록한다.
<div className="row flex-row wrap">
{" "}
{list.map((book, i) => {
return (
<div className="reference" key={i}>
<img src={book.coverSmallUrl} alt="/"></img>
<h2>{book.title}</h2>
<h6>{book.author}</h6>
</div>
);
})}
</div>