React JS 마스터클래스 (3. CRYPTO TRACKER -3 )

짜스의 하루 ·2024년 6월 5일

Price Chart part Two


APEXCHARTS.JS에서 chart에 보여줄 디자인을 가져와서 사용해볼 예정이다.

그러기 전에, chart에 넣어야 api가 계속 안불러져서 오류가 발생했었다. 알고 봤더니 니꼬쌤께서 만드신 api로 접속을 해야 한다고 한다!
기존의 fetch 주소를 변경하고, 니꼬쌤이 제공한 api를 사용해보겠다.


하면서 형변환을 해서 data를 넘겨주면
이렇게 제대로 차트 (데이터)가 생성되는 것을 확인할 수 있다.. 이것 때문에 어제 하루 날리고.. 갑자기 타입스크립트가 안되고 JSX가 안된다고 하고.. 새로운 오류들을 발견한 하루.. 무사히 넘기고 오늘 다시 달려볼 예정이다 🥲

자 이제 Chart 컴포넌트의 코드들을 간단하게 살펴볼 시간이다.

interface IHistorical {
  time_open: string;
  time_close: number;
  open: number;
  high: number;
  low: number;
  close: string;
  volume: number;
  market_cap: number;
}

interface ChartProps {
  coinId: string;
}

interface 로 타입을 지정해주고,

function Chart({ coinId }: ChartProps) {
  const { isLoading, data } = useQuery<IHistorical[]>(['ohlcv', coinId], () =>
    FetchCoinHistory(coinId)
  );

coinId에 ChartProps (타입 지정),
useQuery로 받아온 data에 IHistorical (타입 지정) 을 해주었다.

  • useQuery 훅을 사용하여 데이터를 가져온다. useQuery 훅은 데이터 로딩 상태 및 실제 데이터를 관리하는 데 사용되는데, 여기서는 OHLCV 데이터를 가져오는 비동기 작업을 수행한다.

  • 반환된 데이터가 아직 로딩 중인지를 확인하여, 로딩 중일 경우 "Loading chart..."라는 메시지를 표시하고, 데이터가 로드되면 차트를 렌더링한다.

  • <ApexChart> 컴포넌트를 사용하여 차트를 생성한다. 이 컴포넌트는 ApexCharts 라이브러리를 기반으로한다.
    --> <ApexChart> 컴포넌트의 series prop은 차트의 데이터를 정의한다. 여기서는 "Price"라는 이름의 시리즈를 만들고, 각 데이터 항목의 close 가격을 부동 소수점 숫자로 변환하여 사용한다.

  • 차트의 타입은 "line"으로 설정되며, 옵션 객체에는 차트의 외관과 레이아웃에 대한 다양한 설정이 포함된다. 예를 들어, 차트의 높이와 너비, 테마, 라인 스타일, 축 등을 설정할 수 있다. 다양한 레이아웃에 대한 스타일은 공홈에서 참고하면 된다.

최종적으로, <div> 요소 내에 차트 컴포넌트를 포함하여 렌더링한다. 데이터가 아직 로드되지 않은 경우 로딩 메시지를 표시하고, 데이터가 로드된 경우 차트를 표시하게 된다.

이렇게 코드를 정리하면, 이렇게 차트가 뿅 하고 나타난다!

디자인을 조금 변경해보자

1. 그라데이션 색상
2. 날짜
3. 가격은 소수점 2자리까지
이렇게 속성을 변경을 해주었다.

여기서 궁금한 점 :
categories: data?.map((price) => price.time_close * 1000) ?? []에서
?? [] 은 왜 ? 추가가 된 것일까 ?

--> ?? 연산자는 Nullish 병합 연산자로 데이터가 존재하지 않는 경우를 대비하여 빈 배열을 반환하는 것이다.

따라서 data가 null 또는 undefined인 경우에도 코드가 오류 없이 실행될 수 있도록 하기 위해 ?? 연산자를 사용하여 빈 배열을 기본값으로 설정한 것이다.


자 이제 내가 마무리 할 시간이다!

뒤로가기 버튼 만들기

하나의 코인 정보를 확인하고 뒤로가기 버튼을 누르고 다시 메인 화면을 볼 수 있게 된다면,
훨씬 간편할 것 같다!

뒤로가기 버튼을 구현하기 위해서 원래는 useNavigate 를 사용해야 한다.
간단하게 설명하자면, 일반적으로 React Router를 사용하여 사용자가 UI 요소를 클릭하거나 다른 상호 작용을 할 때 페이지를 전환한다.

그러나 때로는 이와 같은 사용자 상호 작용 없이도 페이지를 전환해야 할 때가 있다.
내가 구현할 단순한 뒤로가기 버튼 같은 경우가 있다!

나는 지금 니꼬 선생님의 강의를 따라가기 위해서 React Router v5를 사용중에 있어 useNavigate를 사용하지 못한다!
그래서 대체로 useHistory를 사용했다.

useHistory는 React Router에서 제공하는 훅 중 하나로, 브라우저 세션 기록을 관리하는 데 사용된다. 이 훅을 사용하면 브라우저의 이전 및 다음 페이지를 추적하고, 페이지 간의 이동을 관리할 수 있다.

여기서, goBack() 메서드를 지원하는데, 이는 이전에 있었던 곳으로 이동을 시켜준다. 만약 내가 Bitcoin -> Price -> Chart 순으로 정보를 확인했는데, goBack()이 적용된 버튼을 누르면
역추적(?)해서 Price -> Bitcoin 페이지로 이동하게 된다.

하지만, 나는 단순히 메인 페이지 (" / ")로 이동해야 하기 때문에!

<Button> 컴포넌트를 만들고, onClick 이벤트를 주었다.
onClickBtn 이벤트에서는 history.push('/') 를 주어, 메인 화면으로 이동할 수 있도록! 했다.


간단하게, <Button> 컴포넌트를 살펴보자면,
theme에 적용했던 색깔들을 가져와서 css에 적용시켰다!
&:hover 를 사용해 버튼에 마우스를 올렸을 때, accentColor를 주어 변환을 시켰다. 위의 사진은 마우스를 올렸을 때 모습이다. 마우스를 내리면, 배경 화면의 색깔과 똑같다!

Price 컴포넌트와 표 꾸미기

사실 주식에 대해서, 코인에 대해서 알지를 못해서 가격에 어떤 정보를 사람들에게 뿌려줘야 할까..
고민이 참 많았다..

interface PriceData {
  id: string;
  name: string;
  symbol: string;
  rank: number;
  circulating_supply: number;
  total_supply: number;
  max_supply: number;
  beta_value: number;
  first_data_at: string;
  last_updated: string;
  quotes: {
    USD: {
      ath_date: string;
      ath_price: number;
      market_cap: number;
      market_cap_change_24h: number;
      percent_change_1h: number;
      percent_change_1y: number;
      percent_change_6h: number;
      percent_change_7d: number;
      percent_change_12h: number;
      percent_change_15m: number;
      percent_change_24h: number;
      percent_change_30d: number;
      percent_change_30m: number;
      percent_from_price_ath: number;
      price: number;
      volume_24h: number;
      volume_24h_change_24h: number;
    };
  };
}

여기서 가격에 대한 것들을 뽑아야 하는데,
간단하게 percent_change 에 관한 것들 1h, 6h, 12h, 24h만 딱 깔끔하게 뽑아서 뿌려보자! 했다.

Price.jsx (중요부분만 뽑았다)

interface PriceProps {
  coinId: string;
}

function Price({ coinId }: PriceProps): JSX.Element {
  const { isLoading, data } = useQuery<PriceData>(
    ['price_detail', coinId],
    () => FetchCoinsTickers(coinId)
  );

  return (
    <div>
      {isLoading ? (
        'Loading chart...'
      ) : (
        <ApexCharts
          type="bar"
          height={350}
          series={[
            {
              name: 'Price',
              data: [
                data.quotes.USD.percent_change_1h,
                data.quotes.USD.percent_change_6h,
                data.quotes.USD.percent_change_12h,
                data.quotes.USD.percent_change_24h,
              ],
              color: '#FF5733',
            },
          ]}
          options={{
            theme: {
              mode: 'dark',
            },

            chart: {
              type: 'bar',
              height: 450,
              width: 450,
              background: 'transparent',
              toolbar: {
                show: false,
              },
            },
            yaxis: {
              show: false,
            },
            plotOptions: {
              bar: {
                borderRadius: 4,
                borderRadiusApplication: 'end',
                horizontal: false,
              },
            },
            dataLabels: {
              enabled: false,
            },
            annotations: {},
            xaxis: {
              categories: ['1h', '6h', '12h', '24h'],
              position: 'top',
              axisBorder: {
                show: false,
              },
              axisTicks: {
                show: false,
              },
              crosshairs: {
                fill: {
                  type: 'gradient',
                  gradient: {
                    colorFrom: '#D8E3F0',
                    colorTo: '#BED1E6',
                    stops: [0, 100],
                    opacityFrom: 0.4,
                    opacityTo: 0.5,
                  },
                },
              },
            },
          }}
        />
      )}
    </div>
  );
}

export default Price;

apexcharts 에서 속성을 뽑아다 사용했는데, 속성이 너어어어무 많아서
이것저것 복붙해보고 적용해보다가 딱 깔끔하게 나오는 정도가 이정도였다.

Price 함수 컴포넌트:

  • coinId를 받아와서 해당 코인에 대한 가격 데이터를 가져온다.
  • useQuery 훅을 사용하여 데이터를 비동기적으로 가져온다.
  • 데이터가 로딩 중인지를 확인하고, 로딩 중이면 "Loading chart..."를 표시하고,
    데이터가 로딩되면 ApexCharts를 사용하여 그래프를 렌더링한다.

요렇게 마우스를 표에 가져다대면, price에 대한 정보를 얻을 수 있다.
깔끔하지 .. ?
grid: { show: false } 을 넣어서 선을 없앨까 하다가 그러면 너무 표같지 않을 거 같아서 선은 냅둬보았다. 흠.. 나름 만족한다.

최종 결과 ‼️

후기

타입스크립트를 처음 사용해보고, styled-component를 처음 접해보고,
react-query도 처음 접해보고

아직 배워야 할 것도, 익숙해저야 할 것들도 많다는 것을 새삼 느끼고 간다.

profile
2024. 01. 02 ~ 백앤드 공부 시작, 2024. 04.01 ~ 프론트 공부 시작

0개의 댓글