frontend 성능 개선

This commit is contained in:
Macbook
2026-06-08 15:25:44 +09:00
parent a84a11e21c
commit 011059f5ed
14 changed files with 258 additions and 37 deletions
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { memo, useMemo } from 'react';
import type { StrategyDto } from '../../utils/backendApi';
import {
resolveVirtualTargetNames,
@@ -11,6 +11,7 @@ import type { ChartRealtimeSource } from '../../hooks/useChartRealtimeData';
import { buildConditionMetrics } from '../../utils/virtualSignalMetrics';
import { resolveStrategyPrimaryTimeframe } from '../../utils/strategyToChartIndicators';
import type { Timeframe } from '../../types';
import { useIntersectionVisible } from '../../hooks/useIntersectionVisible';
import VirtualLiveBadge from './VirtualLiveBadge';
import type { VirtualLiveStatus } from '../../hooks/useVirtualTargetLiveStatus';
import type { VirtualCardViewMode } from '../../utils/virtualTradingStorage';
@@ -128,9 +129,12 @@ const VirtualTargetCard: React.FC<Props> = ({
}, [running, lastTickAt, snapshot?.updatedAt]);
const receiving = useLiveReceiveFlash(receiveSignal, running);
const highlightReceiving = chartLiveReceiveHighlight && receiving;
const { ref: cardRef, visible: cardInView } = useIntersectionVisible();
const chartStreamEnabled = isChart && cardInView;
return (
<div
ref={cardRef}
className={[
'vtd-card',
'vtd-card--signal',
@@ -234,6 +238,7 @@ const VirtualTargetCard: React.FC<Props> = ({
chartSeriesPriceLabels={chartSeriesPriceLabels}
chartPaneSeparator={chartPaneSeparator}
chartTimeframe={chartTimeframe}
chartStreamEnabled={chartStreamEnabled}
/>
) : (
<VirtualTargetSignalPanel
@@ -257,4 +262,4 @@ const VirtualTargetCard: React.FC<Props> = ({
);
};
export default VirtualTargetCard;
export default memo(VirtualTargetCard);
@@ -37,6 +37,8 @@ interface Props {
fillHeight?: boolean;
/** 미지정 시 전략 DSL 대표 분봉 사용 */
chartTimeframe?: Timeframe;
/** false — 뷰포트 밖 등: STOMP·히스토리 구독 중지 (기본 true) */
chartStreamEnabled?: boolean;
}
const noop = () => {};
@@ -52,6 +54,7 @@ const VirtualTargetCardChart: React.FC<Props> = ({
chartPaneSeparator,
fillHeight = false,
chartTimeframe,
chartStreamEnabled = true,
}) => {
const { getParams, getVisualConfig } = useIndicatorSettings();
const tfOptions = useMemo(() => resolveStrategyTimeframes(strategy), [strategy]);
@@ -120,6 +123,7 @@ const VirtualTargetCardChart: React.FC<Props> = ({
}, [applyRealtimeBar]);
const useUpbit = isUpbitMarket(market);
const streamActive = useUpbit && chartStreamEnabled;
const { bars, barsMarket, isLoading } = useChartRealtimeData(
market,
@@ -127,15 +131,15 @@ const VirtualTargetCardChart: React.FC<Props> = ({
useMemo(() => ({
onTickUpdate: handleTickUpdate,
onNewCandle: handleNewCandle,
enabled: useUpbit,
enabled: streamActive,
source: chartRealtimeSource,
}), [handleTickUpdate, handleNewCandle, useUpbit, chartRealtimeSource]),
}), [handleTickUpdate, handleNewCandle, streamActive, chartRealtimeSource]),
);
const { isLoadingMore } = useHistoryLoader({
symbol: market,
timeframe,
isUpbit: useUpbit,
isUpbit: streamActive,
managerRef,
});
@@ -199,12 +203,12 @@ const VirtualTargetCardChart: React.FC<Props> = ({
}, [barsReady, applyPaneHeights, requestChartReload]);
useEffect(() => {
if (!useUpbit) return;
if (!streamActive) return;
void pinCandleWatch(market, timeframeToCandleType(timeframe));
if (timeframeToCandleType(timeframe) !== '1m') {
void pinCandleWatch(market, '1m');
}
}, [market, timeframe, useUpbit, running]);
}, [market, timeframe, streamActive, running]);
return (
<div className={`vtd-card-chart-panel${fillHeight ? ' vtd-card-chart-panel--fill' : ''}`}>
@@ -239,7 +243,7 @@ const VirtualTargetCardChart: React.FC<Props> = ({
)}
<TradingChart
key={`${market}-${timeframe}-${chartReloadTick}`}
chartVisible
chartVisible={chartStreamEnabled}
bars={bars}
barsMarket={barsMarket}
market={market}
@@ -74,8 +74,8 @@ const VirtualTargetGrid: React.FC<Props> = ({
}, [viewMode, globalDisplayMode]);
const gridRemeasureKey = useMemo(
() => `${viewMode}:${globalDisplayMode}:${targets.map(t => t.market).join(',')}:${Object.values(snapshots).map(s => s?.updatedAt ?? 0).join('|')}`,
[viewMode, globalDisplayMode, targets, snapshots],
() => `${viewMode}:${globalDisplayMode}:${targets.map(t => t.market).join(',')}`,
[viewMode, globalDisplayMode, targets],
);
const [cardDisplayOverrides, setCardDisplayOverrides] = useState<Record<string, VirtualCardDisplayMode>>({});
const [focusMarket, setFocusMarket] = useState<string | null>(null);