Wine API 프로젝트 - 2

HyunHo Lee·2021년 12월 21일
2

토이 프로젝트

목록 보기
3/14
post-thumbnail

와인 샘플 API을 이용하여 데이터를 받아오는 부분을 설계해보자.

src 디렉터리 구조이다.


{
  "winery":"Maselva",
  "wine":"Emporda 2012",
  "rating":{
    "average":"4.9",
    "reviews":"88 ratings"
  },
  "location":"Spain\n·\nEmpordà",
  "image":"https://images.vivino.com/thumbs/ApnIiXjcT5Kc33OHgNb9dA_375x500.jpg",
  "id":1
}

먼저 우리가 받아올 데이터 구조를 확인하기 위해 wine 데이터를 확인해보자.

TypeScript를 사용하기 때문에 데이터들의 타입을 선언해주어야 한다. types 디렉터리wine.ts 라는 파일을 생성해주었다.


//types/wine.ts
export interface Wine {
  winery: string;
  wine: string;
  rating: {
    average: string;
    reviews: string;
  };
  location: string;
  image: string;
  id: number;
}

각각의 데이터 타입들을 선언해주었다. Wine 인터페이스를 이용하여 데이터 선언을 해줄 것이므로 export해주었다.


import type { NextPage } from "next";
import axios from "axios";
import useSWR from "swr";
import { Wine } from "../../types/wine";

// 한번 불러오고 다시 불러오지 않아도 될 녀석들.. ssr 해보자..
const fetcher = (url: string) => axios.get(url).then((res) => res.data);

const PortPage: NextPage = () => {
  const { data, error } = useSWR(
    "https://api.sampleapis.com/wines/port",
    fetcher
  );
  if (error) return <div>Faild to Loading...</div>;
  if (!data) return <div>Loading...</div>;
  return (
    <div>
      <h1>port</h1>
      <main>
        {data.map((wineData: Wine) => {
          const { id, wine, winery } = wineData;
          return (
            <div key={`port-wine-list-${id}`}>
              <h1>{wine}</h1>
              <p>{winery}</p>
            </div>
          );
        })}
      </main>
    </div>
  );
};

export default PortPage;

우리는 useSWR을 사용해볼 것이다. 그래서 fetcher을 먼저 선언했다. url만 가져와서 사용하기 때문에 fetcher에 url의 타입을 string으로 선언했다.

useSWR은 data와 error을 받고, useSWR( "데이터받을주소", fetcher ); 와 같이 useSWR을 사용하면 된다.

error인 경우는 로딩 실패 메세지를 띄어주고, data가 없는 경우는 로딩 메세지를 띄어주고 있다.


        {data.map((wineData: Wine) => {
          const { id, wine, winery } = wineData;
          return (
            <div key={`port-wine-list-${id}`}>
              <h1>{wine}</h1>
              <p>{winery}</p>
            </div>
          );
        })}

받아온 데이터는 이제 사용하기위해 return부분에서 {}을 이용하여 맵핑해주자. 객체 하나 하나를 wineData로 받아오고 있는데, 우리가 먼저 types/wine.ts에서 선언해준 Wine 인터페이스를 사용해서 wineData의 타입을 선언해준다.

이와 같이 wines/port 페이지에 와인 API에서의 port정보가 가져와진것을 볼 수 있다. 이제 리팩토링 해보자.


추상화 리팩토링

//utils/fetcher.ts
import axios from "axios";

export const fetcher = (url: string) => axios.get(url).then((res) => res.data);

현재 우리는 와인의 한 종류인 port의 데이터를 불러와서 작업하고 있다. 위의 fetcher의 경우에는 모든 wines디렉터리의 페이지에서 사용될 것이다.

utils 디렉터리fetcher.ts를 생성해주었다.
여기에 fetcher을 따로 빼주었다.


//constants/index.ts
export const WINE_API_ENDPOINT = "https://api.sampleapis.com/wines/";

우리는 https://api.sampleapis.com/wines/port으로 port 와인을 가져왔다. 만약에 화이트 와인의 데이터를 가져오려면https://api.sampleapis.com/wines/whites를 사용해야한다. 딱 봐도 반복되는 부분이 보인다. constants의 index.ts에 와인 API의 엔드포인트 값을 변수로 저장해주었다.


//hooks/useWindData.ts
import useSWR from "swr";
import { fetcher } from "../utils/fetcher";
import { WINE_API_ENDPOINT } from "../constants";

export const useWineData = (path: string) => {
  return useSWR(`${WINE_API_ENDPOINT}${path}`, fetcher);
};

이제 useSWR을 커스텀하기 위해 hooks 디렉터리useWindData.ts을 생성하자. 커스텀 훅을 생성해주는 것인데, use라는 이름으로 파일명이 시작된다. 위에서 분리해둔 fetcher를 가져와서 사용하고, WINE_API_ENDPOINT도 가져와서 사용하자.

useWineData("port") 의 형식으로 사용할 것이다. path인자를 받아올 것인데 string이기 때문에 타입을 위와같이 선언했다.


//components/Error.tsx
export const Error = () => {
  return <div>Faild to Loading...</div>;
};

에러 메세지를 리턴해주는 컴포넌트


//components/Loading.tsx
export const Loading = () => {
  return <div>Loading...</div>;
};

데이터가 없을 경우 로딩 메세지를 리턴해주는 컴포넌트


//components/WineCard.tsx
import { Wine } from "../types/wine";

interface WineProps {
  wineData: Wine;
}

export const WineCard = ({ wineData }: WineProps) => {
  const { wine, winery } = wineData;

  return (
    <div>
      <h1>{wine}</h1>
      <p>{winery}</p>
    </div>
  );
};

데이터를 출력해서 리턴해주는 컴포넌트 ( 비슷한 동작이기 때문에 컴포넌트로 나누어줌 )


//components/index.tsx
export * from "./Error";
export * from "./Loading";
export * from "./WineCard";

컴포넌트 디렉터리 index.tsx를 생성해주었다. 위와 같이 코드를 작성해주면 import { Error, Loading, WineCard } from "../../conponents"; 와 같이 사용할 수있다.


//pages/wines/port.tsx
import type { NextPage } from "next";

import { Wine } from "../../types/wine";
import { useWineData } from "../../hooks/useWindData";
import { Error, Loading, WineCard } from "../../conponents";

const PortPage: NextPage = () => {
  const name = "port";
  const { data, error } = useWineData(name);
  if (error) return <Error />;
  if (!data) return <Loading />;
  return (
    <div>
      <h1>port</h1>
      <main>
        {data.map((wineData: Wine) => {
          return (
            <WineCard
              key={`${name}-wine-list-${wineData.id}`}
              wineData={wineData}
            />
          );
        })}
      </main>
    </div>
  );
};
export default PortPage;

이제 port.tsx 파일을 위와 같이 사용하면 된다.

profile
함께 일하고 싶은 개발자가 되기 위해 달려나가고 있습니다.

0개의 댓글