API에서 받아온 data 시각화 하기
APEXCHAR란 자바스크립트 chart library 이다.
다양한 차트가 있다.
사용하기 위해선 명령어를 실행시켜야 한다.
intergrations 에서 react 꺼로 들어가서 npm install 한다.
npm install --save react-apexcharts apexcharts
그리고 그 전에 typescript에게 data가 어떻게 생겼는지 말을 해줘야 한다.
API가 준 값을 알려주자.
인터페이스로 API의 타입을 정의해준다
그리고 apex를 import 한 후 다양한 옵션들을 설정해 주면
이러한 차트가 나타나게 된다.
즉 어떤 옵션이 들어가야하는지만 안다면 원하는 대로 차트를 꾸밀 수 있는 것이다.
이는 사이트의 options
에서 확인할 수 있다.
import { useQuery } from 'react-query';
import { fetchCoinHistory } from './api';
import ApexChart from 'react-apexcharts';
interface IHistorycal {
time_open: string;
time_close: string;
open: number;
high: number;
low: number;
close: number;
volume: number;
market_cap: number;
}
interface ChartProps {
coinId: string;
}
function Chart({ coinId }: ChartProps) {
const { isLoading, data } = useQuery<IHistorycal[]>(['ohlcv', coinId], () =>
fetchCoinHistory(coinId)
);
//14개를 받아와야 하므로 배열로 전달.
return (
<div>
{isLoading ? (
'Loading chart...'
) : (
<ApexChart
type="line"
series={[
{
name: 'price',
data: data?.map((price) => price.close) as number[],
// 맵함수는 리턴하는 값으로 array를 만들어 준다.
//as 를 이용하셔도 됩니다. 저 데이터는 number 배열이다! 라고강제함.
// 맵함수는 리턴하는 값으로 array를 만들어 준다.
},
]}
//series는 보내고 싶은 모든 data가 들어있다.
options={{
theme: { mode: 'dark' },
chart: {
width: 500,
height: 300,
toolbar: { show: false },
background: 'transparents',
},
grid: { show: false },
stroke: { curve: 'smooth', width: 3 },
yaxis: { show: false },
xaxis: {
labels: { show: false },
axisTicks: { show: false },
axisBorder: { show: false },
},
}}
/>
//apexvhart는 위와같이 다양한 옵션이 있다.
)}
</div>
);
}
// const params = useParams();
// console.log(params);
//coinId 가져오기 또는 서브 Router 에서 porps로 받아오기
export default Chart;
##Coins.tsx
import styled from 'styled-components';
import { Link } from 'react-router-dom';
import { useQuery } from 'react-query';
import { fetchCoins } from './api';
const Container = styled.div`
padding: 0px 20px;
max-width: 480px;
margin: 0 auto;
`;
const Header = styled.header`
height: 20vh;
display: flex;
justify-content: center;
align-items: center;
`;
const CoinsList = styled.ul``;
const Coin = styled.li`
background-color: white;
color: ${(props) => props.theme.bgColor};
margin-bottom: 10px;
padding: 20px;
border-radius: 15px;
a {
padding: 5px; // 좀 더 넓은 범위에서 transition 효과 적용 가능
transition: color 0.2s ease-in;
}
&:hover {
a {
color: ${(props) => props.theme.accentColor};
}
// 아래에서는 a가 아닌 Link라는 이름으로 사용했지만
// css에서는 anchor 를 선택해야 했다. 이건 모든 react router link들이
// 결국에는 anchor로 바뀔거기도 하고,
// react router dom이 우리 대신 설정을 도와줄 특별한 event listener들이 있기도 하다
}
`;
const Title = styled.h1`
font-size: 48px;
color: ${(props) => props.theme.accentColor};
`;
const Loader = styled.span`
text-align: center;
display: block;
`;
const Img = styled.img`
width: 25px;
height: 25px;
margin-right: 10px;
`;
interface ICoin {
id: string;
name: string;
symbol: string;
rank: number;
is_new: boolean;
is_active: boolean;
type: string;
}
function Coins() {
const { isLoading, data } = useQuery<ICoin[]>('allCoins', fetchCoins);
console.log(isLoading, data);
//useQery는 두개의 식별자가 필요하다. 첫번째는 고유식별자, 두번째는 fetcher함수이다.
// useQuery는 isLoading 이라고 불리는 boolean값을 return하는데 이전에 있던
// const[lading,setLoading]과 setLoading(false)를 대체할 수 있는 것이다.
// 총 설명: useQuery hook에서 fetcher함수 fetchCoins를 불러오고 그함수가
// isLoading 즉, fetcher함수가 끝난다면 react Query가 말해줄 것이다.
// 그리고 return 값을 data에 넣어줄 것이다. 아래에서 state에 넣었던 것처럼..
// +) data 타입을 명시하기 위해 <CoinInterface[]>를 넣어준다.
// 위 코드와 아래 코드는 같다.
// const [coins, setCoins] = useState<CoinInterface[]>([]);
// 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, 100));
// setLoading(false);
// })();
// }, []);
return (
<Container>
<Header>
<Title>코인</Title>
</Header>
{isLoading ? (
<Loader>"Loading..."</Loader>
) : (
//loading 이 참이면 Loading... 출력, 거짓이면 CoinsList 보여줌
<CoinsList>
{data?.slice(0, 100).map((coin) => (
<Coin key={coin.id}>
<Img
src={`https://cryptoicon-api.vercel.app/api/icon/${coin.symbol.toLowerCase()}`}
/>
<Link
to={{
pathname: `/${coin.id}`,
state: { name: coin.name },
//Link를 이용해 string 이외에 더 많은 데이터를 보낼 수 있다
}}
>
{coin.id}
</Link>
</Coin>
))}
</CoinsList>
)}
</Container>
);
}
export default Coins;
const BASE_URL = `https://api.coinpaprika.com/v1`;
export async function fetchCoins() {
// const response = await fetch(`${BASE_URL}/coins`);
// const json = await response.json();
// return json;
//가독성 높은 오래된 방법
return fetch(`${BASE_URL}/coins`).then((response) => response.json());
// 두 코드는 같은 것이다. fetcher 함수인 fetchCoin 은 URL을 부르고 URL로 부터 json을 return 한다.
}
export async function fetchCoinInfo(coinId: string) {
//coinId 타입 명시 필요, coin Id를 fetch하는 함수
return fetch(`${BASE_URL}/coins/${coinId}`).then((response) => response.json());
}
export async function fetchCoinTickers(coinId: string) {
//coinId 타입 명시 필요 coin Ticker를 fetch하는 함수
return fetch(`${BASE_URL}/tickers/${coinId}`).then((response) => response.json());
}
export function fetchCoinHistory(coinId: string) {
const endDate = Math.floor(Date.now() / 1000);
const startDate = endDate - 60 * 60 * 24 * 7 * 2;
return fetch(
`${BASE_URL}/coins/${coinId}/ohlcv/historical?start=${startDate}&end=${endDate}`
).then((response) => response.json());
}