Crypto_Tracker: 01

김정훈·2023년 7월 23일
0
post-thumbnail
post-custom-banner

SetUp

npx create-react-app 프로젝트_이름 --template typescript

Styled-Components 설치
npm i styled-components
npm i --save-dev @types/styled-components

ReactRouter 설치
npm I react-router-dom@5.3.0
npm i --save-dev @types/react-router-dom

useParams

ReactRouter에서 제공하는 기능으로, 동적으로 변하는 url의 주소의 정보를 뽑아오는데 사용된다.
url 주소가 /users/:coinId 라면 :coinId 동적으로 변하는 사용자의 고유 ID값.
useParams을 사용하면 이 부분을 쉽게 가져올 수 있다.

Router.tsx

<Route path="/:coinId">
    <Coin />
</Route>

Coin.tsx

interface RouteParams {
  coinId: string;
}

function Coin() {
  const { coinId } = useParams<RouteParams>();
  return <h1>Coin: {coinId}</h1>;
}

Reset Css

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
	display: block;
}
body {
	line-height: 1;
}
ol, ul {
	list-style: none;
}
blockquote, q {
	quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
	content: '';
	content: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}

Google Font
https://fonts.google.com/


Home 화면 만들기

Coins.tsx파일을 만든다.
Coin API주소
https://api.coinpaprika.com/v1/coins

그리고 styled-component을 활용해서 화면을 구성한다

import { Link } from "react-router-dom";
import styled from "styled-components";

const Container = styled.div`
  padding: 0px 20px;
// 화면의 크기에 따라서 중앙에 배치시키기 위해서 추가.
  max-width: 480px;
  margin: 0 auto;
`;
const Header = styled.header`
  height: 15vh;
  display: flex;
  justify-content: center;
  align-items: center;
`;
const CoinList = styled.ul``;

const Coin = styled.li`
  background-color: white;
  margin-bottom: 10px;
  border-radius: 15px;
  color: ${(props) => props.theme.bgColor};
  a{
    transition: color 0.2s ease-in;
    display: block;
    padding: 20px;
    /* 이 부분에 padding값+block속성을 줘서 한줄을 <Link>태그로 클릭할 수 있게 만들어줌 */
  }
  &:hover{
    a{
        color: ${(props) => props.theme.accentColor};
    }
  }
`;
const Title = styled.h1`
  color: ${(props) => props.theme.accentColor};
  font-size: 48px;
`;

const coins = [
  {
    id: "btc-bitcoin",
    name: "Bitcoin",
    symbol: "BTC",
    rank: 1,
    is_new: false,
    is_active: true,
    type: "coin",
  },
];

function Coins() {
  return (
    <Container>
      <Header>
        <Title>Coins</Title>
      </Header>
      <CoinList>
        {coins.map((coin) => (
          <Coin key={coin.id}>
            <Link to={`/${coin.id}`}>{coin.name} &rarr;</Link>
          </Coin>
        ))}
      </CoinList>
    </Container>
  );
}
export default Coins;

클릭했을 때 이동하는 부분은 a태그가 아닌 react-router의 Link태그를 이용해서 처리한다.
a태그를 이용하면 새로고침이 발생하기 때문이다.

이제 받아오는 API값의 형태를 TypeScript에게 가르쳐줘야 한다.

interface CoinInterface{
	id: string,
    name: string,
    symbol: string,
    rank: number,
    is_new: boolean,
    is_active: boolean,
    type: string,
}

const[coins, setCoins] = useState<CoinInterface[]>([]);

: coins는 CoinInterface의 타입을 가진다.

다음과 같이 useEffect을 사용해서 API을 처리한다.
response을 받기 위해서 await을 사용한다.
API을 다 받아오기 전에는, <Loader>컴포넌트가 나온다.

  • 지금 이렇게 데이터를 처리하는 부분은 React Query로 바꿀 것이다.
  useEffect(() => {
    (async() =>{
        const response = await fetch("https://api.coinpaprika.com/v1/coins");
        const json = await response.json();
        setCoins(json.slice(0,10));
        setLoading(false);
    })()
  },[])
{loading ? (
        <Loader>"Loading..."</Loader>
      ):(
        <CoinList>
        {coins.map((coin) => (
          <Coin key={coin.id}>
            <Link to={`/${coin.id}`}>{coin.name} &rarr;</Link>
          </Coin>
        ))}
      </CoinList>
      )}

Route State

코인 Symbol Api
https://coinicons-api.vercel.app/api/icon/${coin.symbol.toLowerCase()

각 코인화면 만들기
비하인더씬이란? Link태그로
: 보이지 않는 방식으로 데이터를 보내는 방식

//Coins.tsx -> Coin.tsx로 보낸다.
<Link
	to={{
		pathname: `/${coin.id}`,
		state: {name: coin.name}
	}}>
	{coin.name} &rarr;
</Link>

Coin.tsx

interface RouteState{
	name: string;
}

const {state} = useLocation<RouteState>();
// 비하인드씬으로 보낸 데이터를 받아온다.

function Coin(){
	<Title>{state.name}</Title>
}

Coin Data를 받기 위한 URL
https://api.coinpaprika.com/v1/coins/btc-bitcoin
https://api.coinpaprika.com/v1/tickers/btc-bitcoin

지금은 useEffect와 async await을 이용하여 fetch하는데, 나중에는 ReactQuery로 바꿀 것이다.

  useEffect(()=>{
    (async() =>{
      const infoData = await(
        await fetch(`https://api.coinpaprika.com/v1/coins/${coinId}/`)
      ).json();
      console.log(infoData); // → temp1
      const priceData = await(
        await fetch(`https://api.coinpaprika.com/v1/tickers/${coinId}/`)
      ).json();
      console.log(priceData); // → temp2
    })();
  }, [])

console.log해서 데이터를 뽑은 다음 console창에서 Store object as global variable로 저장 : temp1
Object.keys(temp1).join();
: 나오는 결과값 그대로 Interface InfoData로 복사
Shift+Option+i : 선택한 모든 문자열에 가장 우측 끝으로 포커싱

타입의 value 얻어오기
Object.values(temp1).map(v=> typeof v).join()

만약 가져온 값중에서 Object가 있다면 그것이 어떤 Object인지 확인해서 Typescript에게 가르쳐줘야한다.

interface Test{
	tags: Object
}

// ↓
interface ITag{
	coin_counter: number;
	ico_counter: number;
	id: string;
	name: string
}
interface Test{
	tags: ITag[];
}

Nested Router

: route안에 있는 또 다른 route이다.
프로젝트 안에는 Router.tsx라는 파일이 있다.
그래서 Coin.tsx안에서 쓰는 Route가 결국 Nested Router가 되는 것이다.

Router.tsx

  • coinId는 동적 라우팅 매개변수 : URL의 일부를 변수로 취급하여 해당 변수의 값에 따라 동적으로 다른 컴포넌트를 렌더링하거나 데이터를 가져오는 데 사용한다.
    → '/' 다음에 오는 어떠한 경로도 매치가 될 수 있다. (/btc , /eth, /lite)
function Router() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/:coinId">
          <Coin />
        </Route>
        <Route path="/">
          <Coins />
        </Route>
      </Switch>
    </BrowserRouter>
  );
}

Coin.tsx

  • 동적 라우팅 매개변수를 가져오는 방법은 다음과 같이 useParams() 훅을 사용해서 가져온다.
interface RouteParams{
	coinId: string;
}
function Coin() {
  const { coinId } = useParams<RouteParams>();
	...
  return (
	  ...
          <Switch>
            <Route path={`/:coinId/price`}>
            
              <Price></Price>
            </Route>
            <Route path={`/:coinId/chart`}>
              <Chart></Chart>
            </Route>
          </Switch>
        </>
      )}
    </Container>
  );
}

useRouteMatch(Price, Chart 탭 만들기)

: 현재 URL과 라우터에 정의된 경로를 비교하요 일치하는지 확인하는데 사용한다.
priceMatch,chartMatch을 사용하여 현재 URL과 일치하는 지 확인하고, isActive prop을 이용하여,
사용자가 클릭한 탭의 색을 accentColor로 적용해준다.

const Tab = styled.span<{isActive: boolean}>`
  text-align: center;
  text-transform: uppercase;
  font-size: 12px;
  font-weight: 400;
  background-color: rgba(0, 0, 0, 0.5);
  padding: 7px 0px;
  border-radius: 10px;
  color: ${(props)=> props.isActive ? props.theme.accentColor : props.theme.textColor};
  a {
    display: block;
  }
`;

const priceMatch = useRouteMatch('/:coinId/price');
const chartMatch = useRouteMatch('/:coinId/chart');

<Tabs>
    <Tab isActive={chartMatch !== null}>
        <Link to={`/${coinId}/chart`}>Chart</Link>
    </Tab>
    <Tab isActive={priceMatch !== null}>
        <Link to={`/${coinId}/price`}>Price</Link>
    </Tab>
</Tabs>
profile
WebDeveloper
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 7월 23일

이런 유용한 정보를 나눠주셔서 감사합니다.

답글 달기