본문 바로가기

react

빗썸 open API로 코인 리스트 만들기 [코인 리스트 패칭 최적화]..2

import { useQuery } from "@tanstack/react-query";
import { fetchTickerData } from "../api";
import useCoinList from "./useCoinList";
import { useMemo } from "react";

interface Ticker {
  coinCode: string;
  price: string;
  changeRate: number;
  tradeVolume: string;
}

export default function useCoinTicker() {
  const { markets } = useCoinList();

  //  markets 전체 대신 coinCode 목록으로만 변경 확인. queryKey에 추가하여 markets가 바뀔 때 패칭
  const coinCodesKey = useMemo(
    () => markets.map(({ coinCode }) => coinCode).join(","),
    [markets]
  );

  // useQuery를 사용하여 ticker 데이터를 패칭하고, 1분마다 자동으로 새로 받아오도록 설정
  const {
    data: tickerData,
    isLoading,
    error,
  } = useQuery({
    queryKey: ["tickerData", coinCodesKey],
    queryFn: fetchTickerData, // fetch function
    refetchInterval: 60000, // 1분마다 자동으로 새로 패치
    enabled: markets.length > 0, // markets 데이터가 있을 때만 실행되도록 설정
  });

  // 데이터가 로드되면 tickerData를 markets 배열의 순서대로 정렬, tickerData가 변경될 때만 tickerList를 재계산
  const tickerList = useMemo(() => {
    return markets
      .map(({ coinCode }) => {
        if (!tickerData) return null;
        const ticker = tickerData.data[coinCode];
        return ticker
          ? {
              coinCode,
              price: ticker.closing_price,
              changeRate: parseFloat(ticker.fluctate_rate_24H),
              tradeVolume: (ticker.acc_trade_value_24H / 1_000_000).toFixed(2),
            }
          : null;
      })
      .filter((item): item is Ticker => item !== null);
  }, [tickerData]);

  if (isLoading) {
    return { tickerList: [], isLoading };
  }

  if (error) {
    console.error("Error fetching ticker data:", error);
    return { tickerList: [], error };
  }

  return { tickerList, isLoading, error };
}

 

 문제: 1분이 채 지나지 않았는데, sidebar가 on/off 될때나, 정렬이 바뀔때마다 새로 api가 받아와 지고 있다. 

 

처음에는 sortList 값에 따라 세 가지의 정렬 상태를 각각 캐싱한 후 sortList값 변경 시(좋아요 <->기본<->rsi순)에 캐싱된 값을 계속 보여 줄 생각이었다. 

 

조건: 1분이 지나지 않으면 무슨 일이 있더라도 api를 받아와서는 안됌.

조건2: 마켓 순서가 바뀔 경우 이에 따라 ticker또한 순서가 바뀌면 됨. 또한 해당 순서는 3가지로 고정되어 있음. 매 변경시마다 굳이 비교하는게 아니라 특정 정렬 형태를 기억해 둔 후 그 형태로 변환하는게 나을 듯.

조건3: 마캣 정렬 순서에 따라 고정된 " ticker 데이터의 순서 3가지"를 기억해 뒀다가 마켓 순서가 변경시 이에 해당하는 순서를 찾아와 이에 맞춰 리스트를 정렬해야 함. 

 

 

배열 자체를 저장하는 건 몰라도, 순서를 저장하는건 나로선 불가능했기에,

캐싱이 유지되는 한, coinCode 순서대로 (즉, sortList별) 정렬된 데이터를 각각 저장하고,

  const ticker = tickerDataByCode[coinCode];

api 호출 기간이 채워지기 전 (그냥 호출 안하고 정렬만 바꿀때)  저장된 데이터를 화면에 띄우는 식으로 바꿨다. 

또한 loading과 error를 없엤는데, ui에서 이미 tickerdata가 없을 경우 값들을 지정해뒀기 때문이다. .

 

    staleTime: 5000, // 5초 동안 캐시된 데이터 사용
    gcTime: 300000, // 5분동안 캐시 유지
    refetchInterval: 6000,

gcTime은 설정한 시간 내에 화면 리렌더링이 있을 경우 캐시된 데이터를 보여준다. 

statleTime은 화면을 띄워뒀을 때의 캐시 저장 기간이다. 해당 시간 내에 패칭이 발생할 경우 이를 막는다. 물론

이 시간이 지난다고 해서 새로 패칭이 받아와지는 건 아니고..

refetchInterval만큼 경과해야 패칭이 발생한다. 

 

이상하게 5분 이내 화면을 켰다 꺼도 계속  console.log("ticker 재정렬"); 가 찍힌다. 

ref로 기존 데이터를 저장해서 비교하거나 useQuery의 select문을 사용해서 데이터를 저장해봐도 해결할 수 없었다.. 

 

일단은 최적화 한 코드이다. 

import { useQuery } from "@tanstack/react-query";
import { fetchTickerData } from "../api";
import useCoinList from "./useCoinList";
import { useMemo } from "react";

interface Ticker {
  coinCode: string;
  price: string;
  changeRate: number;
  tradeVolume: string;
}

export default function useCoinTicker() {
  const { markets } = useCoinList();

  // useQuery를 사용하여 ticker 데이터를 패칭하고, 6초초마다 자동으로 새로 받아오도록 설정
  const { data: tickerData } = useQuery({
    queryKey: ["tickerData"],
    queryFn: fetchTickerData,
    refetchInterval: 6000,
    staleTime: 5000, // 5초 동안 캐시된 데이터 사용
    gcTime: 300000, // 5분동안 캐시 유지
    enabled: markets.length > 0,
  });

  // 데이터가 로드되면 tickerData를 markets 배열의 순서대로 정렬, tickerData가 변경될 때만 tickerList를 재계산
  const tickerList = useMemo(() => {
    if (!tickerData) {
      console.log("ticker 데이터 없음");
      return [];
    }
    const tickerDataByCode = tickerData.data;
    console.log("ticker 재정렬");
    return markets
      .map(({ coinCode }) => {
        const ticker = tickerDataByCode[coinCode];
        return ticker
          ? {
              coinCode,
              price: ticker.closing_price,
              changeRate: parseFloat(ticker.fluctate_rate_24H),
              tradeVolume: (ticker.acc_trade_value_24H / 1_000_000).toFixed(2),
            }
          : null;
      })
      .filter((item): item is Ticker => item !== null);
  }, [tickerData]);

  return { tickerList };
}