본문 바로가기
프론트엔드로 가는 길/portfolio 제작 여정기 👩🏻‍💻

02 BookBucket - 내가 추가할 책을 참고해보자!📚[2.20]

by woody-j 2023. 2. 21.

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) 정책이 적용된 서버에서, 다른 출처의 리소스를 요청할 때 발생하는 보안상의 에러다.

 

자 그럼 어떻게 해야하는가?

더보기
  1. 서버에서 CORS 정책 변경: 서버의 관리자가 응답 헤더를 변경하여 CORS 정책을 완화하거나 해결할 수 있습니다.
  2. 프록시 서버 사용: 클라이언트에서 요청하는 API를 프록시 서버에서 요청하도록 변경하여, 프록시 서버에서 CORS 정책을 우회할 수 있습니다.
  3. JSONP 사용: 일부 API에서는 JSONP(JSON with Padding) 방식을 지원하여, 이를 이용하여 요청할 수 있습니다. 이 방식은 스크립트 태그를 이용하여 다른 출처의 데이터를 요청하는 방식으로, CORS 정책을 우회할 수 있습니다.

나는 프록시 서버 사용으로 문제를 해결 할 것이다.

더보기

API 서버에 대한 프록시를 구성하려면, 먼저 자신의 도메인과 API 서버의 도메인이 다른 경우, 브라우저에서 CORS 에러를 방지하기 위해 프록시 서버를 만들어야 합니다. 이를 위해 보통 CORS-Anywhere 라이브러리와 같은 서드파티 프록시 서버를 사용하거나, 자체적으로 프록시 서버를 만들어 사용합니다.

 

자체적으로 프록시 서버를 만드는 방법은

  1. 서버 측에서 프록시 API 라우터를 만듭니다.
  2. 클라이언트 측에서, API 요청 주소 앞에 프록시 서버 주소를 붙입니다.
  3. 프록시 API 라우터에서, 클라이언트가 요청한 API 주소로 요청을 보냅니다.
  4. 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>