한 페이지 내에서 url 요소를 추가적으로 덧붙여 다른 컴포넌트를 띄우는 방법이다.
localhost:3000/coins/coinId/chart
에는 차트를 보여주는 라이브러리를 사용해 비트코인 차트를, localhost:3000/coins/coinId/price
에는 또 다른 컴포넌트를 띄워 줄 수 있다.
분기하고자 하는 페이지 파일에 Routes와 Route를 import 해준 뒤 Route 설정을 해준다.
coin.tsx
<Routes>
<Route path="price" element={<Price />} />
<Route path="chart" element={<Chart coinId={coinId as string} />} />
</Routes>
v6에서는 강의 상에서 사용한 Switch 호환되지 않음! 😵
coin 뒤에 붙는 url을 인식하기 위해서 Router.tsx
또한 수정해 주어야 한다.
<BrowserRouter>
<Routes>
<Route path="/:coinId/*" element={<Coin />} />
<Route path="/" element={<Coins />} />
</Routes>
</BrowserRouter>
"/:coinId/*" 로써 url의 coinId 뒤의 주소를 구분하여 인식할 수 있다.
Tab을 만들어 보자!
두 Nested Routes로 이동시켜 주고, 사용자가 현재 어디 주소에 접속해 있는지 직관적으로 보기 편하게끔 Tab을 만들어 제공한다. url의 매치 정보를 반환하는 useMatch
을 이용해 현재 접속한 주소를 구분할 수 있다.
coin.tsx
const priceMatch = useMatch("/:coinId/price");
const chartMatch = useMatch("/:coinId/chart");
👉🏻 현재 위치한 주소가 인자로 주어진 주소와 일치하는지 확인하는 변수. 맞다면 true, 아니라면 null 값을 반환한다.
<Tabs>
<Tab isActive={chartMatch !== null}>
<Link to={`/${coinId}/chart`}>Chart</Link>
</Tab>
<Tab isActive={priceMatch !== null}>
<Link to={`/${coinId}/price`}>Price</Link>
</Tab>
</Tabs>
React Query
란?
fetching, caching, 서버 데이터와의 동기화를 지원해주는 라이브러리
기존의 fetch나 axios를 이용하면 서버로부터 받아온 데이터를 담을 state 변수, 로딩 중임을 표시할 변수 등이 각각 필요했고, 이로 인해 코드가 불필요하게 길어지는 문제가 있었다. 또한 캐싱이 지원되지 않아 컴포넌트가 다시 로딩될 때마다 서버에 데이터 요청을 다시금 해야했다.
✅ React Query를 이용하면 background에서 이 모든 것이 수행되어 코드가 간결하고 깔끔해진다!
npm install react-query
index.tsx
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
ReactDOM.render(
<RecoilRoot>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</RecoilRoot>,
document.getElementById("root")
);
👉🏻 라이브러리 설치 후, index.tsx
에 변수를 QuryClient를 통해 생성해 주고, QueryClientProvider로써 App 컴포넌트를 감싸준다.
api.ts
const BASE_URL = `https://api.coinpaprika.com/v1`;
export function fetchCoins() {
return fetch(`${BASE_URL}/coins`).then((response) => response.json());
}
export function fetchCoinInfo(coinId: string | undefined) {
return fetch(`${BASE_URL}/coins/${coinId}`).then((response) =>
response.json()
);
}
export function fetchCoinTickers(coinId: string | undefined) {
return fetch(`${BASE_URL}/tickers/${coinId}`).then((response) =>
response.json()
);
}
export function fetchCoinHistory(coinId: string) {
return fetch(
`https://ohlcv-api.nomadcoders.workers.dev/?coinId=${coinId}`
).then((response) => response.json());
}
👉🏻 api function을 작성할 파일을 생성한다. 쿼리를 행할 함수들을 생성하고 export하면 사용하고자 하는 페이지에서 useQuery를 이용해 간단히 데이터를 가져올 수 있다.
✅ 서버 주소와 같은 반복되는 url은 따로 변수로 설정하여 빼주는 것이 좋다.
coin.tsx
import { useQuery } from "react-query";
import { fetchCoinInfo, fetchCoinTickers } from "../api";
const { isLoading: infoLoading, data: infoData } = useQuery<InfoData>(
["info", coinId],
() => fetchCoinInfo(coinId)
);
const { isLoading: tickersLoading, data: tickersData } = useQuery<PriceData>(
["price", coinId],
() => fetchCoinTickers(coinId)
);
const loading = infoLoading || tickersLoading;
👉🏻 isLoading을 React Query 내에서 자체적으로 제공하므로 데이터마다 로딩 변수를 설정할 필요 없음! 각 fetch 함수마다 데이터가 모두 로드 되면 isLoading이 false로 바뀐다.
👉🏻 각 useQuery는 고유한 key값을 가져야 한다. 배열로 key값과 함께 coinId를 넘겨주어 각 쿼리를 구분한다. 로딩 변수 또한 이름이 겹치므로 infoLoading, tickersLoading으로 이름을 바꿔 준다.
✅ React Query는 fetching 해온 데이터를 자동으로 캐싱해 주므로, 다른 페이지로 이동했다가 다시 쿼리가 있는 페이지로 돌아와도 이전에 fetching 해왔던 데이터가 캐싱되어 있기 때문에, 데이터를 재요청하지 않는다. 불필요한 서버 요청과 로딩을 줄일 수 있다!
const { isLoading, data } = useQuery<IHistorical[]>(
["ohlcv", coinId],
() => fetchCoinHistory(coinId)
{
refetchInterval: 10000,
}
);
👉🏻 실시간으로 변동하는 데이터의 경우 refetch 시간을 refetchInterval
설정을 통해 주기적으로 데이터 요청을 보낼 수 있다.
기존의 useState 변수와 loading 변수 남발보다 훨씬 간단하고 깔끔한 코드로 데이터를 가져올 수 있다. 🥰