frontend 속도 개선
This commit is contained in:
@@ -2,6 +2,9 @@ import React, { createContext, useContext } from 'react';
|
||||
import type { ChartMode, IndicatorConfig } from '../types';
|
||||
import type { TickerData } from '../hooks/useMarketTicker';
|
||||
import type { SettingsPageChartBindings } from './useChartWorkspace';
|
||||
import { DEFAULT_DISPLAY_TIMEZONE } from '../utils/timezone';
|
||||
import { DEFAULT_CHART_TIME_FORMAT } from '../utils/chartTimeFormat';
|
||||
import { DEFAULT_TRADE_ALERT_TIME_FORMAT } from '../utils/tradeAlertTimeFormat';
|
||||
|
||||
export interface ChartWorkspaceSharedState {
|
||||
symbol: string;
|
||||
@@ -21,12 +24,66 @@ export interface ChartWorkspaceSharedState {
|
||||
settingsPageProps: SettingsPageChartBindings;
|
||||
}
|
||||
|
||||
export interface ChartWorkspaceContextValue extends ChartWorkspaceSharedState {
|
||||
/** 실시간 차트 화면 — `.app` flex 레이아웃 안에서 렌더해야 좌·우 패널·메뉴가 정상 표시됨 */
|
||||
chartScreen: React.ReactNode;
|
||||
}
|
||||
export type ChartWorkspaceContextValue = ChartWorkspaceSharedState;
|
||||
|
||||
const ChartWorkspaceContext = createContext<ChartWorkspaceContextValue | null>(null);
|
||||
const NULL_SETTINGS_PAGE_PROPS: SettingsPageChartBindings = {
|
||||
magnetMode: 'off',
|
||||
onMagnetMode: () => {},
|
||||
liveStrategies: [],
|
||||
onClearLiveMarkers: () => {},
|
||||
liveSettings: { market: '', strategyId: null, isLiveCheck: false, executionType: 'CANDLE_CLOSE', positionMode: 'LONG_ONLY' },
|
||||
onLiveSettingsChange: () => {},
|
||||
watchlistCount: 0,
|
||||
monitoredMarkets: [],
|
||||
virtualDriven: false,
|
||||
chartIndicators: [],
|
||||
onAddIndicatorToChart: () => {},
|
||||
onRemoveIndicatorFromChart: () => {},
|
||||
onIndicatorListOrderChange: () => {},
|
||||
chartCandleAreaPriceLabels: false,
|
||||
onChartCandleAreaPriceLabels: () => {},
|
||||
chartSeriesPriceLabels: false,
|
||||
onChartSeriesPriceLabels: () => {},
|
||||
chartVolumeVisible: true,
|
||||
onChartVolumeVisible: () => {},
|
||||
chartLiveReceiveHighlight: true,
|
||||
onChartLiveReceiveHighlight: () => {},
|
||||
chartLegendOptions: undefined,
|
||||
onChartLegendOptionsChange: () => {},
|
||||
chartCrosshairInfoVisible: true,
|
||||
onChartCrosshairInfoVisible: () => {},
|
||||
chartHoverToolbarVisible: true,
|
||||
onChartHoverToolbarVisible: () => {},
|
||||
chartPaneSeparator: undefined,
|
||||
onChartPaneSeparatorChange: () => {},
|
||||
onPaperAccountReset: async () => {},
|
||||
onDisplayTimezoneChange: () => {},
|
||||
onChartTimeFormatChange: () => {},
|
||||
onTradeAlertTimeFormatChange: () => {},
|
||||
displayTimezone: DEFAULT_DISPLAY_TIMEZONE,
|
||||
chartTimeFormat: DEFAULT_CHART_TIME_FORMAT,
|
||||
tradeAlertTimeFormat: DEFAULT_TRADE_ALERT_TIME_FORMAT,
|
||||
};
|
||||
|
||||
export const NULL_CHART_WORKSPACE_CONTEXT: ChartWorkspaceContextValue = {
|
||||
symbol: 'KRW-BTC',
|
||||
indicators: [],
|
||||
goToMarketChart: () => {},
|
||||
refreshPaperAccount: async () => {},
|
||||
paperCashBalance: 0,
|
||||
paperCoinQty: 0,
|
||||
handlePaperOrderFilled: () => {},
|
||||
paperRefreshKey: 0,
|
||||
mode: 'chart',
|
||||
priorityMarkets: [],
|
||||
marketPanelOpen: false,
|
||||
marketTickers: new Map(),
|
||||
marketLoading: false,
|
||||
usdRate: 0,
|
||||
settingsPageProps: NULL_SETTINGS_PAGE_PROPS,
|
||||
};
|
||||
|
||||
const ChartWorkspaceContext = createContext<ChartWorkspaceContextValue>(NULL_CHART_WORKSPACE_CONTEXT);
|
||||
|
||||
export function ChartWorkspaceProvider({
|
||||
value,
|
||||
@@ -43,9 +100,5 @@ export function ChartWorkspaceProvider({
|
||||
}
|
||||
|
||||
export function useChartWorkspaceContext(): ChartWorkspaceContextValue {
|
||||
const ctx = useContext(ChartWorkspaceContext);
|
||||
if (!ctx) {
|
||||
throw new Error('useChartWorkspaceContext must be used within ChartWorkspaceProvider');
|
||||
}
|
||||
return ctx;
|
||||
return useContext(ChartWorkspaceContext);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { forwardRef, useImperativeHandle, useMemo } from 'react';
|
||||
import React, { forwardRef, useImperativeHandle, useMemo, useEffect, useRef } from 'react';
|
||||
import { LiveSignalNotifier } from '../components/LiveSignalNotifier';
|
||||
import { ChartWorkspaceProvider } from './ChartWorkspaceContext';
|
||||
import { ChartWorkspaceView } from './ChartWorkspaceView';
|
||||
@@ -7,12 +7,14 @@ import type { ChartWorkspaceBridge } from './chartWorkspaceBridge';
|
||||
|
||||
export interface ChartWorkspaceRootProps extends UseChartWorkspaceParams {
|
||||
visible: boolean;
|
||||
children: React.ReactNode;
|
||||
/** 외부에서 이동 요청된 마켓 (알림·대시보드 등에서 차트로 이동 시) */
|
||||
pendingChartMarket?: string | null;
|
||||
onPendingChartMarketConsumed?: () => void;
|
||||
}
|
||||
|
||||
export const ChartWorkspaceRoot = forwardRef<ChartWorkspaceBridge, ChartWorkspaceRootProps>(
|
||||
function ChartWorkspaceRoot(props, ref) {
|
||||
const { visible, children, ...workspaceParams } = props;
|
||||
const { visible, pendingChartMarket, onPendingChartMarketConsumed, ...workspaceParams } = props;
|
||||
const {
|
||||
bridge,
|
||||
contextValue,
|
||||
@@ -23,19 +25,31 @@ export const ChartWorkspaceRoot = forwardRef<ChartWorkspaceBridge, ChartWorkspac
|
||||
|
||||
useImperativeHandle(ref, () => bridge, [bridge]);
|
||||
|
||||
// 외부에서 마켓 이동 요청 처리
|
||||
const pendingRef = useRef(pendingChartMarket);
|
||||
pendingRef.current = pendingChartMarket;
|
||||
const consumedRef = useRef(onPendingChartMarketConsumed);
|
||||
consumedRef.current = onPendingChartMarketConsumed;
|
||||
|
||||
useEffect(() => {
|
||||
if (pendingChartMarket) {
|
||||
bridge.goToMarket(pendingChartMarket);
|
||||
consumedRef.current?.();
|
||||
}
|
||||
// bridge.goToMarket is stable (useCallback), pendingChartMarket is the trigger
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pendingChartMarket]);
|
||||
|
||||
const providerValue = useMemo(() => ({
|
||||
...contextValue,
|
||||
chartScreen: (
|
||||
<div style={{ display: visible ? 'contents' : 'none' }}>
|
||||
<ChartWorkspaceView {...viewProps} />
|
||||
</div>
|
||||
),
|
||||
}), [contextValue, visible, viewProps]);
|
||||
}), [contextValue]);
|
||||
|
||||
return (
|
||||
<ChartWorkspaceProvider value={providerValue}>
|
||||
<LiveSignalNotifier ref={liveNotifierRef} {...liveNotifierProps} />
|
||||
{children}
|
||||
<div style={{ display: visible ? 'contents' : 'none' }}>
|
||||
<ChartWorkspaceView {...viewProps} />
|
||||
</div>
|
||||
</ChartWorkspaceProvider>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import '../styles/tradeRightPanel.css';
|
||||
import React from 'react';
|
||||
import DrawingToolbar from '../components/DrawingToolbar';
|
||||
import BottomBar from '../components/BottomBar';
|
||||
|
||||
@@ -1175,6 +1175,8 @@ export function useChartWorkspace({
|
||||
|
||||
// ── Watchlist 가격 맵 ──────────────────────────────────────────────────
|
||||
const priceMap = useMemo(() => {
|
||||
// 시세 패널이 열려 있고 ticker가 활성 상태일 때만 계산 (데모 심볼용)
|
||||
if (!showMarketPanel || !tickerFeedActive) return {} as Record<string, { price: number; changePct: number }>;
|
||||
const map: Record<string, { price: number; changePct: number }> = {};
|
||||
for (const sym of watchlist) {
|
||||
const b = isUpbitMarket(sym) ? [] : generateBars(sym, '1D');
|
||||
@@ -1183,7 +1185,7 @@ export function useChartWorkspace({
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}, [watchlist]);
|
||||
}, [watchlist, showMarketPanel, tickerFeedActive]);
|
||||
|
||||
|
||||
// ── DB 워크스페이스 동기화 ─────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user