※ 노마드 코더 "React JS 마스터클래스" 기반 프로젝트
기본 Setup
1. react-query 설치 ( react-query를 사용하면 데이터 fetching을 쉽게 할 수가 있다.
2. react-router-dom 설치
3. styled-components 설치
2. 각 route 생성 ( 각 코인 정보를 보여주는 Coin 과 코인 리스트를 보여주는 Coins)
공통 스타일 적용 : createGlobalStyle 사용
모든 컴포넌트에 기본적으로 적용되어야 하는 공통 스타일이 있다.
이는 createGlobalStyle이라는 스타일 컴포넌트를 통해 해결할 수 있다.
이 컴포넌트는 렌더링 될 때 전역 스코프에 스타일을 올려준다.
import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
*{
box-sizing: border-box;
}
body{
font-family: 'Source Sans Pro', sans-serif;
background-color: ${(props) => props.theme.bgColor};
color:${(props) => props.theme.textColor}
}
a{
text-decoration: none;
}`;
function App() {
return (
<>
<GlobalStyle />
<Router />
</>
);
}
Coins 페이지 만들기 : 코인 리스트 나열
데이터를 fetching 시켜 코인 정보를 불러와 리스트로 나열한다.
여기서 모든 태그는 스타일 컴포넌트화 시켜서 만들었다.
기억해야 할 부분이 몇가지 있었다.
- 리액트 라우터는 결국 브라우저에서 a태그로 보여지기 때문에 스타일을 입힐 때 a태그로 둔다.
- 배열 형태의 state에 인터페이스를 적용하기 위해선 인터페이스도 배열로 둔다.
const [coins, setCoins] = useState<CoinInterface[]>([]);
- ()()은 함수를 바로 실행시킬 수 있다. 데이터 feching에서 사용하였다.
useEffect(() => {
(async () => {
const response = await fetch("https://api.coinpaprika.com/v1/coins");
const json = await response.json();
setCoins(json.slice(0, 100));
setLoading(false);
})();
}, []);
각 코인을 클릭하면 코인 상세페이지로 이동해야 하는데,
앱의 최적화를 위해 Link에 props로 state를 담아서 전달하는 방법을 사용했다.
// 보내는 방법
<Link
to={{
pathname: `/${coin.id}`,
state: { name: coin.name },
}}
>
<Img
src={`https://coinicons-api.vercel.app/api/icon/${coin.symbol.toLowerCase()}`}
/>
{coin.name} →
</Link>
//받는 방법
const { state } = useLocation<RouteState>();
useLocation은 쿼리 파라미터를 가져올 수 있는 리액트의 훅이다.
props로 보냈던 state도 파라미터에 포함되어 있기 때문에 이 훅을 사용하여 값을 가져올 수 있다.
Coin 페이지 만들기 : 코인 상세 정보 보기
코인 정보 데이터를 fetching한다.
TS를 사용하기 때문에 JSON데이터를 TS으로 빠르게 변환해주어야 한다.
인터페이스를 직접 만들어 사용하는 것은 번거로우니 여러 사이트를 이용하여 변환시켜줄 수도 있당..
https://app.quicktype.io/?l=ts
Instantly parse JSON in any language | quicktype
app.quicktype.io
페이지를 꾸며준다.
존재하지 않을 수도 있는 데이터를 다룰 때는 경우에 따라 ?을 써서 여부를 나타내기도 한다.
<span>{info?.open_source ? "Yes" : "No"}</span>
차트 보기 : 중첩 라우팅(nested router)
상세 페이지 밑에 차트나 가격이 나타나게 하기 위해서 중첩라우팅을 한다.
return(
...
<Link to={`/${coinId}/price`}>Price</Link>
<Link to={`/${coinId}/chart`}>Chart</Link>
<Switch>
<Route path={`/${coinId}/price`} component={Price}></Route>
<Route path={`/${coinId}/chart`} component={Chart}></Route>
</Switch>
)
useRouteMatch 훅은 현재 특정 url에 있는지의 여부를 나타내 준다.
이 훅은 특정 url에 있는 것을 확인한다면 파라미터나 path 등이 담긴 객체를 리턴한다. 없을 경우 null을 리턴한다.
const chartMatch = useRouteMatch("/:coinId/chart");
스타일 컴포넌트에 isActive 프로퍼티를 담아 그 값에 따라 글씨 색으로 선택되었음을 알려준다.
const Tab = styled.span<{ isActive: boolean }>`
color: ${(props) =>
props.isActive ? props.theme.accentColor : props.theme.textColor};
`;
<Tab isActive={chartMatch !== null}>
<Link to={`/${coinId}/chart`}>Chart</Link>
</Tab>
데이터 불러오기 : react-query를 사용한 방법
앞서 말한 것처럼 react-query를 사용하면 api에 접근하여 데이터를 좀 더 쉽게 얻어올 수 있다.
그리고 불러온 데이터를 캐시에 저장하기 때문에 데이터를 매번 불러오지 않을 수 있다.
우선 QueryClient 설정을 해주어야 한다.
const queryClient = new QueryClient();
ReactDOM.render(
<div>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</QueryClientProvider>
</div>,
document.getElementById("root") as HTMLElement
);
그리고 fetch를 하는 fetcher 함수를 따로 만들어야 한다.
//api.tsx 생성
export function fetchCoins() {
return fetch("https://api.coinpaprika.com/v1/coins").then((response) =>
response.json()
);
}
이제 데이터를 불러보자.
const { isLoading, data } = useQuery<ICoin[]>("allCoins", fetchCoins);
useQuery 훅은 두가지 인자를 필수로 한다.
첫 번째는 queryKey로 쿼리의 고유식별자이다.
두 번째는 fetcher함수이다.
이 훅의 리턴값은 isLoading이라고 불리는 boolean값과 fetcher함수가 리턴하는 데이터 값이다.
만약 두 가지의 데이터를 불러와야 한다면 중복되는 값이 생길텐데 그럴 때는 아래와 같이 하면 된다.
export function fetchCoinInfo(coinId: string) {
return fetch(`${BASE_URL}/coins/${coinId}`).then((res) => res.json());
}
export function fetchCoinTickers(coinId: string) {
return fetch(`${BASE_URL}/tickers/${coinId}`).then((res) => res.json());
}
const { isLoading: infoLoading, data: infoData } = useQuery<InfoData>(
["info", coinId],
() => fetchCoinInfo(coinId)
);
const { isLoading: tickersLoading, data: tickersData } = useQuery<PriceData>(
["tickers", coinId],
() => fetchCoinTickers(coinId)
);
실시간으로 변동되는 데이터를 가져올 때는 useQuery 훅의 세번째 인자인 refetchInterval를 설정하면 좋다.
const { isLoading, data } = useQuery<IHistorical[]>(
["ohlcv", coinId],
() => fetchCoinHistory(coinId),
{ refetchInterval: 10000 }
);
추가적으로 우리는 react-query가 가진 Devtools라는 컴포넌트로 캐시에 있는 모든 데이터를 시각화하여 볼 수 있다.
import { ReactQueryDevtools } from "react-query/devtools";
function App() {
return (
<>
<GlobalStyle />
<Router />
<ReactQueryDevtools initialIsOpen={true} />
</>
);
}

이렇게 화면에 Devtools가 뜬다.
Price 차트 만들기
어떤 코인을 선택했는지 prop를 받게 한다.
react-query를 사용하여 api에 접근해 코인정보를 받아온다.
//Coin.tsx
<Route path={`/${coinId}/chart`}>
<Chart coinId={coinId} />
</Route>
/
//Chart.tsx
interface ChartProps {
coinId: string;
}
function Chart({ coinId }: ChartProps) {
const {} = useQuery(["ohlcv", coinId], () => fetchCoinHistory(coinId));
return <div>Chart</div>;
}
APEXCHARTS.js 라이브러리를 사용하여 데이터를 시각화 해줄 것이다.
일단 라이브러리 설치를 해준다.
npm install --save react-apexcharts apexcharts
apexchart 컴포넌트에 props를 담아 사용하면 된다.
import ApexChart from "react-apexcharts";
function Chart({ coinId }: ChartProps) {
const { isLoading, data } = useQuery<IHistorical[]>(["ohlcv", coinId], () =>
fetchCoinHistory(coinId)
);
return (
<div>
<ApexChart
type="line"
series={[
{
name: "Price",
data: data?.map((price) => price.close) as number[],
},
]}
options={{
theme: {
mode: "dark",
},
}}
/>
</div>
);
apexcharts.js 공식 문서에 다양한 props에 대한 설명이 나와있다.
https://apexcharts.com/docs/installation/#
Installation & Getting Started – ApexCharts.js
Integrating ApexCharts is as simple as it can get with extensive API docs and 100+ samples ready to be used. Check out all the options of ApexCharts
apexcharts.com

동적 타이틀 구현하기 : react - helmet 사용
리액트는 싱글페이지에 컴포넌트만 변화되는 SPA 방식이기 때문에 모두 동일한 타이틀을 가지고 있다.
react-helmet을 사용하면 페이지마다 다른 타이틀을 부여할 수 있다.
우선 reacy-helmet를 설치해준다.
npm i react-helmet
header 앞 최상단에 Helmet 컴포넌트를 추가하면 된다.
<Helmet>
<title>
코인
</title>
</Helmet>

'Tech > TypeScript' 카테고리의 다른 글
[Typescript / TS] Frame motion을 사용한 애니메이션 (0) | 2023.01.29 |
---|---|
[Typescript / TS] ToDo 칸반보드 만들기 (0) | 2023.01.23 |
[Typescript / TS] ToDo리스트 만들기 (0) | 2023.01.16 |
[Typescript / TS] Recoil을 사용한 state 관리 (0) | 2023.01.16 |
[Typescript / TS] 타입스크립트 기초 (0) | 2023.01.02 |
※ 노마드 코더 "React JS 마스터클래스" 기반 프로젝트
기본 Setup
1. react-query 설치 ( react-query를 사용하면 데이터 fetching을 쉽게 할 수가 있다.
2. react-router-dom 설치
3. styled-components 설치
2. 각 route 생성 ( 각 코인 정보를 보여주는 Coin 과 코인 리스트를 보여주는 Coins)
공통 스타일 적용 : createGlobalStyle 사용
모든 컴포넌트에 기본적으로 적용되어야 하는 공통 스타일이 있다.
이는 createGlobalStyle이라는 스타일 컴포넌트를 통해 해결할 수 있다.
이 컴포넌트는 렌더링 될 때 전역 스코프에 스타일을 올려준다.
import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
*{
box-sizing: border-box;
}
body{
font-family: 'Source Sans Pro', sans-serif;
background-color: ${(props) => props.theme.bgColor};
color:${(props) => props.theme.textColor}
}
a{
text-decoration: none;
}`;
function App() {
return (
<>
<GlobalStyle />
<Router />
</>
);
}
Coins 페이지 만들기 : 코인 리스트 나열
데이터를 fetching 시켜 코인 정보를 불러와 리스트로 나열한다.
여기서 모든 태그는 스타일 컴포넌트화 시켜서 만들었다.
기억해야 할 부분이 몇가지 있었다.
- 리액트 라우터는 결국 브라우저에서 a태그로 보여지기 때문에 스타일을 입힐 때 a태그로 둔다.
- 배열 형태의 state에 인터페이스를 적용하기 위해선 인터페이스도 배열로 둔다.
const [coins, setCoins] = useState<CoinInterface[]>([]);
- ()()은 함수를 바로 실행시킬 수 있다. 데이터 feching에서 사용하였다.
useEffect(() => {
(async () => {
const response = await fetch("https://api.coinpaprika.com/v1/coins");
const json = await response.json();
setCoins(json.slice(0, 100));
setLoading(false);
})();
}, []);
각 코인을 클릭하면 코인 상세페이지로 이동해야 하는데,
앱의 최적화를 위해 Link에 props로 state를 담아서 전달하는 방법을 사용했다.
// 보내는 방법
<Link
to={{
pathname: `/${coin.id}`,
state: { name: coin.name },
}}
>
<Img
src={`https://coinicons-api.vercel.app/api/icon/${coin.symbol.toLowerCase()}`}
/>
{coin.name} →
</Link>
//받는 방법
const { state } = useLocation<RouteState>();
useLocation은 쿼리 파라미터를 가져올 수 있는 리액트의 훅이다.
props로 보냈던 state도 파라미터에 포함되어 있기 때문에 이 훅을 사용하여 값을 가져올 수 있다.
Coin 페이지 만들기 : 코인 상세 정보 보기
코인 정보 데이터를 fetching한다.
TS를 사용하기 때문에 JSON데이터를 TS으로 빠르게 변환해주어야 한다.
인터페이스를 직접 만들어 사용하는 것은 번거로우니 여러 사이트를 이용하여 변환시켜줄 수도 있당..
https://app.quicktype.io/?l=ts
Instantly parse JSON in any language | quicktype
app.quicktype.io
페이지를 꾸며준다.
존재하지 않을 수도 있는 데이터를 다룰 때는 경우에 따라 ?을 써서 여부를 나타내기도 한다.
<span>{info?.open_source ? "Yes" : "No"}</span>
차트 보기 : 중첩 라우팅(nested router)
상세 페이지 밑에 차트나 가격이 나타나게 하기 위해서 중첩라우팅을 한다.
return(
...
<Link to={`/${coinId}/price`}>Price</Link>
<Link to={`/${coinId}/chart`}>Chart</Link>
<Switch>
<Route path={`/${coinId}/price`} component={Price}></Route>
<Route path={`/${coinId}/chart`} component={Chart}></Route>
</Switch>
)
useRouteMatch 훅은 현재 특정 url에 있는지의 여부를 나타내 준다.
이 훅은 특정 url에 있는 것을 확인한다면 파라미터나 path 등이 담긴 객체를 리턴한다. 없을 경우 null을 리턴한다.
const chartMatch = useRouteMatch("/:coinId/chart");
스타일 컴포넌트에 isActive 프로퍼티를 담아 그 값에 따라 글씨 색으로 선택되었음을 알려준다.
const Tab = styled.span<{ isActive: boolean }>`
color: ${(props) =>
props.isActive ? props.theme.accentColor : props.theme.textColor};
`;
<Tab isActive={chartMatch !== null}>
<Link to={`/${coinId}/chart`}>Chart</Link>
</Tab>
데이터 불러오기 : react-query를 사용한 방법
앞서 말한 것처럼 react-query를 사용하면 api에 접근하여 데이터를 좀 더 쉽게 얻어올 수 있다.
그리고 불러온 데이터를 캐시에 저장하기 때문에 데이터를 매번 불러오지 않을 수 있다.
우선 QueryClient 설정을 해주어야 한다.
const queryClient = new QueryClient();
ReactDOM.render(
<div>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
</QueryClientProvider>
</div>,
document.getElementById("root") as HTMLElement
);
그리고 fetch를 하는 fetcher 함수를 따로 만들어야 한다.
//api.tsx 생성
export function fetchCoins() {
return fetch("https://api.coinpaprika.com/v1/coins").then((response) =>
response.json()
);
}
이제 데이터를 불러보자.
const { isLoading, data } = useQuery<ICoin[]>("allCoins", fetchCoins);
useQuery 훅은 두가지 인자를 필수로 한다.
첫 번째는 queryKey로 쿼리의 고유식별자이다.
두 번째는 fetcher함수이다.
이 훅의 리턴값은 isLoading이라고 불리는 boolean값과 fetcher함수가 리턴하는 데이터 값이다.
만약 두 가지의 데이터를 불러와야 한다면 중복되는 값이 생길텐데 그럴 때는 아래와 같이 하면 된다.
export function fetchCoinInfo(coinId: string) {
return fetch(`${BASE_URL}/coins/${coinId}`).then((res) => res.json());
}
export function fetchCoinTickers(coinId: string) {
return fetch(`${BASE_URL}/tickers/${coinId}`).then((res) => res.json());
}
const { isLoading: infoLoading, data: infoData } = useQuery<InfoData>(
["info", coinId],
() => fetchCoinInfo(coinId)
);
const { isLoading: tickersLoading, data: tickersData } = useQuery<PriceData>(
["tickers", coinId],
() => fetchCoinTickers(coinId)
);
실시간으로 변동되는 데이터를 가져올 때는 useQuery 훅의 세번째 인자인 refetchInterval를 설정하면 좋다.
const { isLoading, data } = useQuery<IHistorical[]>(
["ohlcv", coinId],
() => fetchCoinHistory(coinId),
{ refetchInterval: 10000 }
);
추가적으로 우리는 react-query가 가진 Devtools라는 컴포넌트로 캐시에 있는 모든 데이터를 시각화하여 볼 수 있다.
import { ReactQueryDevtools } from "react-query/devtools";
function App() {
return (
<>
<GlobalStyle />
<Router />
<ReactQueryDevtools initialIsOpen={true} />
</>
);
}

이렇게 화면에 Devtools가 뜬다.
Price 차트 만들기
어떤 코인을 선택했는지 prop를 받게 한다.
react-query를 사용하여 api에 접근해 코인정보를 받아온다.
//Coin.tsx
<Route path={`/${coinId}/chart`}>
<Chart coinId={coinId} />
</Route>
/
//Chart.tsx
interface ChartProps {
coinId: string;
}
function Chart({ coinId }: ChartProps) {
const {} = useQuery(["ohlcv", coinId], () => fetchCoinHistory(coinId));
return <div>Chart</div>;
}
APEXCHARTS.js 라이브러리를 사용하여 데이터를 시각화 해줄 것이다.
일단 라이브러리 설치를 해준다.
npm install --save react-apexcharts apexcharts
apexchart 컴포넌트에 props를 담아 사용하면 된다.
import ApexChart from "react-apexcharts";
function Chart({ coinId }: ChartProps) {
const { isLoading, data } = useQuery<IHistorical[]>(["ohlcv", coinId], () =>
fetchCoinHistory(coinId)
);
return (
<div>
<ApexChart
type="line"
series={[
{
name: "Price",
data: data?.map((price) => price.close) as number[],
},
]}
options={{
theme: {
mode: "dark",
},
}}
/>
</div>
);
apexcharts.js 공식 문서에 다양한 props에 대한 설명이 나와있다.
https://apexcharts.com/docs/installation/#
Installation & Getting Started – ApexCharts.js
Integrating ApexCharts is as simple as it can get with extensive API docs and 100+ samples ready to be used. Check out all the options of ApexCharts
apexcharts.com

동적 타이틀 구현하기 : react - helmet 사용
리액트는 싱글페이지에 컴포넌트만 변화되는 SPA 방식이기 때문에 모두 동일한 타이틀을 가지고 있다.
react-helmet을 사용하면 페이지마다 다른 타이틀을 부여할 수 있다.
우선 reacy-helmet를 설치해준다.
npm i react-helmet
header 앞 최상단에 Helmet 컴포넌트를 추가하면 된다.
<Helmet>
<title>
코인
</title>
</Helmet>

'Tech > TypeScript' 카테고리의 다른 글
[Typescript / TS] Frame motion을 사용한 애니메이션 (0) | 2023.01.29 |
---|---|
[Typescript / TS] ToDo 칸반보드 만들기 (0) | 2023.01.23 |
[Typescript / TS] ToDo리스트 만들기 (0) | 2023.01.16 |
[Typescript / TS] Recoil을 사용한 state 관리 (0) | 2023.01.16 |
[Typescript / TS] 타입스크립트 기초 (0) | 2023.01.02 |