1.4~5 React masterClass (CoinStore)

hun2__2·2022년 1월 5일
0

Have a fruitful vacation

목록 보기
9/24
post-thumbnail

TS를 이용해서 winter를 고치면서 어려웠던 점은 object의 type을 지정하는 것과 contextAPI와 같은 상태관리를 안하니 props로 받아야해서 불필요한 과정이 많았다는 점이다 그리고 다시 마저 강의를 듣기 시작했는데... 모든것이 해결되었다... 역시 이 불편함을 가만히 둘 리가 없지ㅎ

reactMasterClass의 첫번째 작업물로는 https://api.coinpaprika.com/ 에서제공하는 api를이용해서 CoinStore를 TS를 이용해서 만들어봤다.
coin들의 정보를 받아와서 보여주는 웹페이지이다.

먼저 styled-reset을 사용하는것 대신에 styled-reset의 github에서 코드만 복사해와 css를 reset시켰다.
(출처 : https://www.npmjs.com/package/styled-reset)
폰트는 googlefont의 Nanum Pen Script를 사용했고
(출처 : https://fonts.google.com/specimen/Nanum+Pen+Script?subset=korean)
색깔은 flatuicolors에서 추천해주는 색조합을 참고하였다.
(출처 : https://flatuicolors.com/)

내가 원하는 css로 맞추기 위해서 Globalstyle이라는 styled-component를 만들어 reset코드와 폰트를 넣어주었다. 폰트를 넣는 방법은 추후에 포스터로 따로 다루겠다.(간단함)

그렇게 간단한 css setting을 끝내고 본격적으로 api에서 data를 받아올 것이다.
먼저 Router.tsx에 홈페이지가 될Coins 라우터와 개별 coinpage가 될 coin 라우터를 만들어주고

function Router() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/:coinId">
          <Coin />
        </Route>
        <Route path="/">
          <Coins />
        </Route>
      </Switch>
    </BrowserRouter>
  );
}

export default Router;

Coins에서는 모든 coin에 대한 정보와
(Coins : https://api.coinpaprika.com/v1/coins)
coin에서는 개별coin에 대한 detail한 정보를 받아올것이다.
(Coin : https://api.coinpaprika.com/v1/coins/{coin_id},
price : https://api.coinpaprika.com/v1/tickers/{coin_id})

먼저 Coins부터 만들어보면
첫 렌더링이될때 data를 받아와야함으로 useEffect를 사용해서 api를 연결해주고 그 데이터를 받아줄 state를 만든다. 이때 js와의 차이점은 state를 처음 정의할 때 state의 타입을 명시해줘야한다는 점이다.
console창에 api한테서 받아오는 data를 쳐보고 state에 들어갈 값의 타입을 알아보고 지정해주어야 한다.
coin안의 data들의 type을 알려주는 interface를 만들고 그 정보를 넣을 const[coins, setCoins] = useState<CoinProps[]>();를 만들어준다.
(뒤에서 배운 react-query로 나중에 바꿔줄 것이다)

interface CoinProps {
  id: string;
  name: string;
  symbol: string;
  rank: number;
  is_new: boolean;
  is_active: boolean;
  type: string;
}

const [coins, setCoins] = useState<CoinProps[]>();
  
useEffect(() => {
    (async () => {
      const response = await fetch("https://api.coinpaprika.com/v1/coins");
      const json = await response.json();
      setCoins(json.slice(0, 50));
      setLoading(false);
    })();
  }, []);

그리고 data를 받아왔는지 확인하는 loading값도 boolean 타입의 state를 만들어서 true일때는 loading중을 띄워주고 false일때는 data를 띄워 줄 것이다.
받아온 coins data가 너무 많으니 ?.slcie.map(() => {})을 통해서 짤라준다
cf) .와 ?. 차이는 TS에서는 .이라고 하면 무조건 있어야 하고 ?.는 없어도 가능하게 해준다.

  const [loading, setLoading] = useState(true);

  return (
    <Container>
      <Header>
        <Title>코인</Title>
      </Header>
      {loading ? (
        <Loader>Loading...</Loader>
      ) : (
      {coins?.slice(0, 100).map(
      (coin)=> <div>{coin.name}</div>
      )
      })}
    </Container>
  );

이제 styled-component를 통해 이쁘게 꾸며주고, 코인의 이미지를 제공해주는 https://cryptoicon-api.vercel.app/api/icon에서 코인 이미지까지 추가해주고

배경색등을 사용하기 위해 App.js에서 ThemeProvider 으로 Router를 감싸준다.
ThemeProvider은 styled-component가 제공하는 모듈로 prop로 theme을 필요하는데
이전에 배운것과 같이 styled-component의 DefalutTheme을 이용하여 값을 만들어 주고

import { DefaultTheme } from "styled-components";

// ! theme 값 지정
export const theme: DefaultTheme = {
  bgColor: "#808e9b",
  textColor: "#d2dae2",
  accentColor: "#ffdd59",
};

이 값을 사용하기 위해서 styled-components의 type에 추가해줘야한다.

import "styled-components";

// ! 여기에 테마의 속성값 타입을 지정해준다.
declare module "styled-components" {
  export interface DefaultTheme {
    textColor: string;
    bgColor: string;
    accentColor: string;
    // borderRadius: string;

    // colors: {
    //   main: string;
    //   secondary: string;
    // };
  }
}

그리고 이제 App.tsx에서 감싸주면

function App() {
  return (
    <>
      <ThemeProvider theme={theme}>
        <GlobalStyle />
        <Router />
      </ThemeProvider>
    </>
  );
}

완성!


이런 화면을 만들 수 있다.

그 다음으로 개별 coin에 대한 data를 보여주는 router를 만들것이다.
이 router의 path는 /:coinId이다. coins에서 link로 이동할 수 있도록 만들어 준다.
react-router에서 제공하는 link에서 to를 이용해서 a태그의 href 역할을 해주는데 여기에는 url만 담는것 뿐만아니라 data도 담아서 보내줄 수 있다. 함수도 가능하다!
(참고 : https://v5.reactrouter.com/web/api/Link)

<Link
	to={{
	pathname: `/${coin.id}`,
	state: { name: coin.name },
	}}
>

이와같이 data를 같이 보내주고 들어온 component에서 react-router가 제공하는 useLocation 사용하면 data에 접근 할 수 있다.
(참고 : https://v5.reactrouter.com/web/api/Hooks/useLocation)

넘겨받는 data안의 값들에 대한 type을 지정해주고 useLocation 이용해서 data를 받아올 수 있다.
또한 useParams를 사용하면 URL의 params를 알 수 있다.

interface CoinProps {
  coinId: string;
}
interface StateProps {
  name: string;
}

const { coinId } = useParams<CoinProps>();
const { state } = useLocation<StateProps>();

이제 이것을 이용해서
Coin : https://api.coinpaprika.com/v1/coins/{coin_id},
price : https://api.coinpaprika.com/v1/tickers/{coin_id}
의 data를 받아올 것인다.

이번에는 data를 react-query를 이용하여 받아올 것인데 react-query를 이용해서 받아오는 것의 장점은 코드가 훨씬 간결해지고 자동으로 cashing을 해주어서 한 번 data를 받아오면 url이 변경되어도 data가 사라지지 않는다.

사용하는 방법은 먼저

yarn add react-query

를하고 역시나 유명한 라이브러리는 type이 이미 있으므로

yarn add @types/react-query

react-query를 사용하기 위해서는
QueryClient()를 생성해주고 ThemeProvider를 사용했을 때 처럼 감싸주면 그리고 그 안에 있는 component들이 react-query를 사용할 수 있다.

import { QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";

const queryClient = new QueryClient();

function App() {
  return (
    <>
      <QueryClientProvider client={queryClient}>
        <ThemeProvider theme={theme}>
          <GlobalStyle />
          <Router />
        </ThemeProvider>
        {/* //! 이걸 사용해서 캐시에 뭐가 저장되어있는지 확인 할 수 있다 */}
        <ReactQueryDevtools initialIsOpen={true} />
      </QueryClientProvider>
    </>
  );
}

그리고 여기에 추가로 개꿀인게 ReactQueryDevtools 라는 devtool을 제공해주는데 이것을 추가하면 현재 react-query로 cashing된 data들을 보여준다!!

이제 data를 불러오는 곳에서 react-query 제공하는 hook인 useQuery를 사용한다.
공식홈페이지의 useQuery 사용 예시는 아래와 같다.
(참고 : https://react-query.tanstack.com/quick-start)

const { isLoading, error, data } = useQuery('repoData', () =>
     fetch('https://api.github.com/repos/tannerlinsley/react-query').then(res =>
       res.json()
     )
   )

componenet에서 fetch해오는게 싫으면 따로 fetch해오는 파일을 만들자

// api.ts
// !fetcher function 모음

const BASE_URL = `https://api.coinpaprika.com/v1`;

export function fetchCoinInfo(coinId: string) {
  return fetch(`${BASE_URL}/coins/${coinId}`).then((res) => res.json());
}

export function fetchCoinPrice(coinId: string) {
  return fetch(`${BASE_URL}/tickers/${coinId}`).then((res) => res.json());
}

그리고 coin.tsx에서

  const { isLoading: infoLoding, data: info } = useQuery(
    ["info", coinId],
    () => fetchCoinInfo(coinId),
    { refetchInterval: 5000 }
  );
  const { isLoading: tickersLoading, data: priceInfo } = useQuery(
    ["tichers", coinId],
    () => fetchCoinPrice(coinId)
  );

이렇게 사용할 수 있다.
여기서 useQuery는 고유key값과 fetch함수를 받고 obj도 선택적으로 받을 수 있다.
반환값으로는 Loading 상태를 알려주는 isLoading값과 fetch된 data를 반환해준다.
cf) error가 났을 경우를 위한 error값도 반환해준다. 자세한건 react-query에서 정리하자

그러면 coins에서 작업했던 state작업들이 필요없어진다. (fetch가 완료될 때 바로 data를 저장한 obj로 반환해주고, loading 이라는 state도 필요 없어진다.)

  const [coins, setCoins] = useState<CoinProps[]>([]);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    (async () => {
      const response = await fetch("https://api.coinpaprika.com/v1/coins");
      const json = await response.json();
      setCoins(json.slice(0, 50));
      setLoading(false);
    })();
  }, []);

이랬던 코드가

const { isLoading : loading, data : coins } = useQuery<CoinProps[]>("allCoins", fetchCoins);

이렇게 바뀐다... 심지어 cashing까지.... 개쩐다...

받아온 data들이 이름이 겹치지 않게 수정해주고 (ES6문법) 두data 모두 받았을 때만 loading이 끝나도록 수정해준다.

const loading = infoLoding || tickersLoading;

여기도 이제 styled-component를 이용해서 이쁘게 꾸며주면


이렇게 data가 잘 넘어온다!

이번에 배운것중 핵심을 다시 살펴보면

  • react-router 더 많은 기능(link에 path외 담는것 몰랐음... 역시 공식문서 읽어야돼...) 과 유용한 hook인 useParams, useLocation를 배워보았고
    새로운 version도 나온김에 react-router 공식문서를 읽고 따로 공부하고 정리하는 시간이 필요함을 느꼈다.

  • react-query라는 완전 짱짱인 기술을 배웠다! 하지만 지금은 '이렇게 쓰는 거구나'정도만 아는상태이지 내께 아니다 이것도 공식문서를 읽으면서 공부해봐야겠다... 후...^^ 영어공부 빡시게해야지ㅜㅜ

  • TS를 사용하는 방법이 조금씩 익숙해지는 것,,,같...다...?? obj들의 type을 지정하는게 빡셌는데 console창을 잘 이용하면 조금 수월하게 할 수 있는 방법도 배우고 나중에 이걸 자동으로 할 수 있는 방법이 있다니깐 열심히 배워봐야지!! TS의 궁금함 중 많은 부분이 해결이되어서 쭉 듣고나서 winter에 한번에 적용해봐야겠다. 그러고 나면 나도 내 페이지도 많이 성장할 것 같다!!

다음 포스터에서는 ApexChart를 이용해서 chart를 꾸미는 것과 Recoil을 이용해서 상태관리를해서 다크모드, 라이트모드를 만드는 것을 포스터 할 것이다! 좋아좋아 점점 재미있어지고 있으으

ps.
오늘도 못 일어남.... 내일은 꼭 일어나자...젭알,,, 그냥 눈뜨면 양치하고 바로 밖으로 나가!
영어공부가 잘 안되가는것 같아서 뭔가 계획이 필요하다고 느껴졌다 때마침 양킹 이라는 유튜버가 하는 영어공부스터디를 알게되었고 감사하게도 데일리미션 등 계획을 짜서 올려주시는 스터디가 있길래 참여하기로 결심했다! 내일 아침에 6시에 참가 되면 바로 하고 아니면 다음날부터! 매일매일 2시간씩은 영어부터 하고 하루를 시작하자
그리고 도형이 부모님께 상담어떻게 드릴지도 예상해 놓자 이대로는 안돼!!! 돈받고 일하는데 애가 성적이 똥이면 안돼... 어머님께 말씀드리고 숙제 검사 타이트하게 해달라고 말씀드려야겠어
오늘도 운동을 못했네...? 몸도 점점 근육빠지고 흐물흐물해지고있다... 솔직히 말해서 그냥 게을러진듯ㅜㅜ 세수하고 푸쉬업 60개 턱걸이 30개 사레레 3세트만이라도 하고 자자! 저거하는데 딱 10분이다 이것도 안하면 시간이 아니라 의지가 없는거야!!!!!!!!!!!!!!!!!!!!!

profile
과정을 적는 곳

0개의 댓글