Skip to content

๐ŸŽซ ์‚ฌ์šฉ์ž์˜ ์‹œ์  ๋ณ€ํ™” ์—†๋Š” ๊ทธ๋ž˜ํ”„ ์Šคํฌ๋กค ๊ตฌํ˜„ํ•˜๊ธฐ

baegyeong edited this page Dec 2, 2024 · 2 revisions
๋ถ„์•ผ ์ž‘์„ฑ์ž ์ž‘์„ฑ์ผ
FE ์กฐ๋ฐฐ๊ฒฝ 24๋…„ 12์›” 03์ผ

์œ ์ง€๋˜์ง€ ์•Š๋Š” ๊ทธ๋ž˜ํ”„์˜ ํ˜„์œ„์น˜

graph

์ด์ „์œผ๋กœ ์Šคํฌ๋กคํ•˜๋ฉฐ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€์ ์œผ๋กœ ๋ฐ›์•„์˜ฌ ๋•Œ, ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ด์–ด์ง€์ง€ ์•Š๊ณ  ๊ทธ๋ž˜ํ”„์˜ ์œ„์น˜๊ฐ€ ๋ณ€ํ™”ํ•œ๋‹ค.

์›์ธ์€ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ๊ณผ ๊ทธ๋ž˜ํ”„ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ ๋กœ์ง์ด ํ•˜๋‚˜์˜ useEffect์•ˆ์— ๋“ค์–ด๊ฐ€์žˆ๋Š”๋ฐ, ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๊ทธ๋ž˜ํ”„ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค. ์ฆ‰ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€ํ™”ํ•˜๋ฉด ๊ทธ๋ž˜ํ”„๋ฅผ ๋‹ค์‹œ ์ƒ์„ฑํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๊ทธ๋ž˜ํ”„๋ฅผ ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋Š” ๊ณ„์†ํ•ด์„œ ์—…๋ฐ์ดํŠธํ•˜๋„๋ก ๋กœ์ง์„ ๋ณ€๊ฒฝํ–ˆ๋‹ค.

ํ•˜๋‚˜์˜ useEffect์•ˆ์— ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ, ํ…Œ๋งˆ ๋ณ€๊ฒฝ, ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ๊ฐ€ ์ „๋ถ€ ์ด๋ค„์กŒ๋Š”๋ฐ ์ด๋ฅผ ๊ฐ๊ฐ useEffect๋กœ ๋‚˜๋ˆด๋‹ค.

๊ธฐ์กด ์ฝ”๋“œ
    interface UseChartProps {
      containerRef: RefObject<HTMLDivElement>;
      priceData: StockTimeSeriesResponse['priceDtoList'];
      volumeData: StockTimeSeriesResponse['volumeDtoList'];
    }
    
    const TransformPriceData = PriceSchema.transform((item) => ({
      time: new Date(item.startTime).toISOString().slice(0, 10),
      open: parseFloat(item.open),
      high: parseFloat(item.high),
      low: parseFloat(item.low),
      close: parseFloat(item.close),
    }));
    
    const TransformVolumeData = VolumeSchema.transform((item) => ({
      time: new Date(item.startTime).toISOString().slice(0, 10),
      value: parseFloat(item.volume),
    }));
    
    export const useChart = ({
      containerRef,
      priceData,
      volumeData,
    }: UseChartProps) => {
      const chart = useRef<IChartApi>();
      const containerInstance = containerRef.current;
    
      const { data: theme } = useGetUserTheme();
      const graphTheme = theme === 'light' ? lightTheme : darkTheme;
    
      useEffect(() => {
        if (!containerInstance) return;
    
        chart.current = createChart(containerInstance, {
          width: containerInstance.clientWidth,
          height: containerInstance.clientHeight,
          ...createChartOptions(graphTheme),
        });
    
        const volumeSeries = chart.current.addHistogramSeries(
          createVolumeOptions(),
        );
        volumeSeries.priceScale().applyOptions({
          scaleMargins: {
            top: 0.93,
            bottom: 0,
          },
        });
    
        const transformedVolumeData = volumeData.map((item) =>
          TransformVolumeData.parse(item),
        );
        const histogramData = getHistogramColorData(transformedVolumeData);
        volumeSeries.setData(histogramData);
    
        const candleSeries = chart.current.addCandlestickSeries(
          createCandlestickOptions(graphTheme),
        );
    
        const transformedPriceData = priceData.map((item) =>
          TransformPriceData.parse(item),
        );
        candleSeries.setData(transformedPriceData);
    
        return () => {
          chart.current?.remove();
        };
      }, [containerInstance, graphTheme, priceData, volumeData]);
    
      return chart;
    };

0๏ธโƒฃ useRef ์„ค์ •

const chart = useRef<IChartApi>();
const candleSeries = useRef<ReturnType<IChartApi['addCandlestickSeries']>>();
const volumeSeries = useRef<ReturnType<IChartApi['addHistogramSeries']>>();
const containerInstance = containerRef.current;

๊ธฐ์กด ์ฝ”๋“œ์—์„œ๋Š” volume, candle ๊ทธ๋ž˜ํ”„๋ฅผ ์ƒ์ˆ˜๋กœ ์ƒ์„ฑํ•˜์—ฌ ์ปค์Šคํ…€ํ•ด์คฌ๋Š”๋ฐ, ๋ฆฌ๋ Œ๋”๋ง ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด useRef๋กœ ์„ ์–ธํ•ด์คฌ๋‹ค.


1๏ธโƒฃ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ

useEffect(() => {
  if (!containerInstance) return;

  // chart.current์—๋Š” ์ฐจํŠธ ์ƒ์„ฑ ๊ฐ’ ์ €์žฅ
  chart.current = createChart(containerInstance, {
    width: containerInstance.clientWidth,
    height: containerInstance.clientHeight,
    ...createChartOptions(graphTheme),
  });
	
  // volumeSeries.current์—๋Š” volume ์ฐจํŠธ ์ƒ์„ฑ ๊ฐ’ ์ €์žฅ
  volumeSeries.current = chart.current.addHistogramSeries(
    createVolumeOptions(),
  );
  volumeSeries.current.priceScale().applyOptions({
    scaleMargins: {
      top: 0.93,
      bottom: 0,
    },
  });

  // candleSeries.current์—๋Š” ์บ”๋“ค ์ฐจํŠธ ์ƒ์„ฑ ๊ฐ’ ์ €์žฅ
  candleSeries.current = chart.current.addCandlestickSeries(
    createCandlestickOptions(graphTheme),
  );

  // unmount ๋œ๋‹ค๋ฉด ์ฐจํŠธ๋ฅผ remove ํ•ด์ค€๋‹ค.
  return () => {
    chart.current?.remove();
  };
}, [containerInstance]);
  • .current๋Š” useRef์˜ ์ฐธ์กฐ๊ฐ’์„ ์ €์žฅํ•˜๊ณ  ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ณต๊ฐ„์ด๋‹ค.
  • chart, volumeSeries, candleSeries์— ๊ฐ๊ฐ ์ฐจํŠธ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ €์žฅํ•œ๋‹ค.

2๏ธโƒฃ ํ…Œ๋งˆ ๋ณ€๊ฒฝ ์ฒ˜๋ฆฌ

ํ…Œ๋งˆ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์€ ๋‹ค๋ฅธ ์‚ฌ์ดํด์ด๊ธฐ ๋•Œ๋ฌธ์— ๋˜ ๋‹ค๋ฅธ useEffect๋กœ ์ฒ˜๋ฆฌํ•ด์คฌ๋‹ค.

useEffect(() => {
  if (!chart.current || !candleSeries.current) return;

  // ์ฐจํŠธ์™€ candleSeries์— ํ…Œ๋งˆ ์ ์šฉ
  chart.current.applyOptions(createChartOptions(graphTheme));
  candleSeries.current.applyOptions(createCandlestickOptions(graphTheme));
}, [graphTheme]);

volumeSeries๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์„ธํŒ…ํ•  ๋•Œ ๋”ฐ๋กœ color๋ฅผ ๋„ฃ์–ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๊ธฐ์„œ ๋”ฐ๋กœ ์ง€์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค.


3๏ธโƒฃ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ

useEffect(() => {
  if (!candleSeries.current || !volumeSeries.current) return;

  // api๋กœ ๋ฐ›์•„์˜ค๋Š” volumeData๋ฅผ ๊ทธ๋ž˜ํ”„ ํ˜•์‹์— ๋งž๊ฒŒ transform
  const transformedVolumeData = volumeData.map((item) =>
    TransformVolumeData.parse(item),
  );

  // api๋กœ ๋ฐ›์•„์˜ค๋Š” PriceData๋ฅผ ๊ทธ๋ž˜ํ”„ ํ˜•์‹์— ๋งž๊ฒŒ transform
  const transformedPriceData = priceData.map((item) =>
    TransformPriceData.parse(item),
  );

  // histogram์€ ๋ฐ์ดํ„ฐ์— color ๊ฐ’ ์ถ”๊ฐ€
  const histogramData = getHistogramColorData(transformedVolumeData);
	
  // candleSeries.current์— ๋ฐ์ดํ„ฐ ์ €์žฅ
  candleSeries.current.setData(transformedPriceData);
  
  // volumeSeries.current์— ๋ฐ์ดํ„ฐ ์ €์žฅ
  volumeSeries.current.setData(histogramData);
}, [priceData, volumeData]);
  • ์—ฌ๊ธฐ์„œ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋์„ ๋•Œ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋กœ์ง๋งŒ ๋ฌถ์–ด๋‘”๋‹ค.

๊ฒฐ๊ณผ

graph_smooth

์Šคํฌ๋กคํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ๋ถˆ๋Ÿฌ์™€๋„ ๊ทธ๋ž˜ํ”„์˜ ํ˜„์œ„์น˜๊ฐ€ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค!

๐Ÿœ ํŒ€ ๊ฐœ๋ฏธ

๐Ÿ›๏ธ ํŒ€ ๋ฌธํ™”

๊ฐœ๋ฐœ ์œ„ํ‚ค

FE

BE

Infra

๐Ÿ—ฃ๏ธ ๋ฐœํ‘œ

๐Ÿ“š ํšŒ์˜๋ก

๐Ÿ”ด ์ธํ„ฐ๋ฏธ์…˜
๐ŸŸ  1์ฃผ์ฐจ
๐ŸŸก 2์ฃผ์ฐจ
๐ŸŸข 3์ฃผ์ฐจ
๐Ÿ”ต 4์ฃผ์ฐจ
๐ŸŸฃ 5์ฃผ์ฐจ
๐ŸŸค 6์ฃผ์ฐจ

๐Ÿ’ญ ํšŒ๊ณ 

๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ ๋ฉ˜ํ† ๋ง

Clone this wiki locally