-
Notifications
You must be signed in to change notification settings - Fork 1
๐ซ ์ฌ์ฉ์์ ์์ ๋ณํ ์๋ ๊ทธ๋ํ ์คํฌ๋กค ๊ตฌํํ๊ธฐ
๋ถ์ผ | ์์ฑ์ | ์์ฑ์ผ |
---|---|---|
FE | ์กฐ๋ฐฐ๊ฒฝ | 24๋ 12์ 03์ผ |
์ด์ ์ผ๋ก ์คํฌ๋กคํ๋ฉฐ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐ์ ์ผ๋ก ๋ฐ์์ฌ ๋, ๋ถ๋๋ฝ๊ฒ ์ด์ด์ง์ง ์๊ณ ๊ทธ๋ํ์ ์์น๊ฐ ๋ณํํ๋ค.
์์ธ์ ๊ทธ๋ํ ์์ฑ๊ณผ ๊ทธ๋ํ ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ ๋ก์ง์ด ํ๋์ 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;
};
const chart = useRef<IChartApi>();
const candleSeries = useRef<ReturnType<IChartApi['addCandlestickSeries']>>();
const volumeSeries = useRef<ReturnType<IChartApi['addHistogramSeries']>>();
const containerInstance = containerRef.current;
๊ธฐ์กด ์ฝ๋์์๋ volume, candle ๊ทธ๋ํ๋ฅผ ์์๋ก ์์ฑํ์ฌ ์ปค์คํ ํด์คฌ๋๋ฐ, ๋ฆฌ๋ ๋๋ง ๋ฐฉ์ง๋ฅผ ์ํด useRef๋ก ์ ์ธํด์คฌ๋ค.
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์ ๊ฐ๊ฐ ์ฐจํธ๋ฅผ ์์ฑํ์ฌ ์ ์ฅํ๋ค.
ํ ๋ง๊ฐ ๋ณ๊ฒฝ๋๋ ๊ฒ์ ๋ค๋ฅธ ์ฌ์ดํด์ด๊ธฐ ๋๋ฌธ์ ๋ ๋ค๋ฅธ useEffect๋ก ์ฒ๋ฆฌํด์คฌ๋ค.
useEffect(() => {
if (!chart.current || !candleSeries.current) return;
// ์ฐจํธ์ candleSeries์ ํ
๋ง ์ ์ฉ
chart.current.applyOptions(createChartOptions(graphTheme));
candleSeries.current.applyOptions(createCandlestickOptions(graphTheme));
}, [graphTheme]);
volumeSeries๋ ๋ฐ์ดํฐ๋ฅผ ์ธํ ํ ๋ ๋ฐ๋ก color๋ฅผ ๋ฃ์ด์ฃผ๊ธฐ ๋๋ฌธ์ ์ฌ๊ธฐ์ ๋ฐ๋ก ์ง์ ํ์ง ์๋๋ค.
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]);
- ์ฌ๊ธฐ์๋ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋์ ๋ ์ ๋ฐ์ดํธํ๋ ๋ก์ง๋ง ๋ฌถ์ด๋๋ค.
์คํฌ๋กคํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ณ์ ๋ถ๋ฌ์๋ ๊ทธ๋ํ์ ํ์์น๊ฐ ๋ฐ๋์ง ์๋๋ค!
- ๐ฉ FE ๊ธฐ์ ์ ํ์ด์
- โจ ์ฐจํธ์ ๋ฐ์ํ ๊ตฌํ๊ณผ useRef ํ์ ๋ฌธ์
- ๐ฃ ๋ถ๋ชจ ์์์ ์ํ์ ๋ฐ๋ผ ์์ ์์๋ ์คํ์ผ ๋ณํ ๋ถ์ฌํ๊ธฐ
- ๐ zod ๋์ ํ๊ธฐ
- ๐ useInfiniteQuery๋ฅผ ์ฌ์ฉํ ๊ทธ๋ํ ๋ฌดํ์คํฌ๋กค ๊ตฌํ
- ๐ซ ์ฌ์ฉ์์ ์์ ๋ณํ ์๋ ๊ทธ๋ํ ์คํฌ๋กค ๊ตฌํํ๊ธฐ
- ๐งช ์๋ง์ ๊ทธ๋ํ ๋ฐ์ดํฐ ์์ฒญ์ ์ด๋ป๊ฒ ์ค์ผ๊น
- ๐ ๋คํฌ๋ชจ๋์์ ์๋ก๊ณ ์นจ ์ ๋ผ์ดํธ๋ชจ๋๊ฐ ์ ๊น ๋ณด์ด๋ ๋ฌธ์
- ๐ ์น์์ผ์ ์ฑํ ๋ฐ์ดํฐ์ REST API์ ์ฑํ ๋ฐ์ดํฐ๋ฅผ ํจ๊ป ๊ด๋ฆฌํ๊ธฐ
- ๐ก BE ๊ธฐ์ ์ ํ ์ด์
- โ๏ธ Node WebSocket ํ๊ณ ๋ค๊ธฐ
- โ๏ธ TypeORM Datasource mock ๋ง๋ค๊ธฐ
- โ๏ธ oauth ID range ๋ฌธ์
- ๐ custom pipe์์ Nan์ด ๋ฐ์์ง๋ ๋ฌธ์
- ๐ช nest Websocket์ ์ธ์ ์ด ์๋๋ค๊ณ ?
- ๐ด nginx websocket ์ฐ๊ฒฐ ์ ๋ฌธ์ ๋ฐ์
- ๐ WebPush ๊ตฌํ
- ๐ง ์ฐ์ ์์ ํ๋ก ์์ฒญ ์ ์ดํ๊ธฐ
- ๐ websocket์ด ๋ฆ๊ฒ ํ ๋น๋์ด ๋ฐ์๋๋ ๋ฌธ์
- ๐ฅณ typeorm์ ์ด์ฉํ FCM ์๋ฆผ ์๋น์ค
- ๐ฆ ๋ค์ค ์ ์ ๋์์ฑ ์ ์ด โ ์ฑ๊ธํค, ๋ฎคํ ์ค
- ๐ ๊ทธ๋ํ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ ๊ณตํ๊ธฐ์ํ ์ ๋ต
- ๐ ๏ธ ์ธํ๋ผ ๊ธฐ์ ์คํ ์ ํ ์ด์
- ๐ Ncloud ์ค์ ๊ณผ์
- ๐ ORM ๊ธฐ์ ์คํ ๋น๊ต
- ๐ค RabbitMQ๋ก ๋ถ์ฐ ์๋ฒ์๊ฒ ๋ฉ์์ง๋ฅผ ๋ถ๋ฐฐํ๊ธฐ
- ๐ข private DB ์๋ฒ์ ์ ์ํ์ง ๋ชปํ๋ ํ์
- ๐ 1์ฃผ์ฐจ ๋ฐํ
- ๐ 2์ฃผ์ฐจ ๋ฐํ
- ๐ 3์ฃผ์ฐจ ๋ฐํ
- ๐ 4์ฃผ์ฐจ ๋ฐํ
- ๐ 5์ฃผ์ฐจ ๋ฐํ
- ๐ ์ต์ข ๋ฐํ