실시간 차트 오류 수정

This commit is contained in:
Macbook
2026-05-25 04:18:30 +09:00
parent ac2e6a1dae
commit 0cfe7fc84c
3 changed files with 96 additions and 27 deletions
+18 -13
View File
@@ -8,17 +8,18 @@ import { DISPLAY_COUNT } from '../hooks/useUpbitData';
/** Upbit 실시간 — 초기 히스토리가 이 개수 미만이면 setData 하지 않음 (종목 전환 직후 sparse 봉 → 캔들 폭 비정상) */
const MIN_BARS_FOR_FULL_RELOAD = Math.min(50, Math.floor(DISPLAY_COUNT / 4));
/** 종목 전환 직후 stale/partial 데이터 차단 — fetch 완료(barsMarket 확정) + 충분한 히스토리만 렌더 */
/** 종목 전환 직후 stale/partial 데이터 차단 — barsMarket 확정 후 렌더 허용 */
function canApplyChartBars(
barData: OHLCVBar[],
barsMarket: string | null | undefined,
market: string,
): boolean {
if (barData.length === 0) return false;
if (barData.length < 2) return false;
if (barsMarket === undefined) return true;
if (barsMarket == null || barsMarket !== market) return false;
if (!market.startsWith('KRW-')) return barData.length >= 2;
return barData.length >= MIN_BARS_FOR_FULL_RELOAD;
if (!market.startsWith('KRW-')) return true;
if (barData.length >= MIN_BARS_FOR_FULL_RELOAD) return true;
return true;
}
import DrawingCanvas, { hitTestDrawing } from './DrawingCanvas';
import PaneLegend, { type PaneLegendProps } from './PaneLegend';
@@ -172,6 +173,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
const prevSortedPKRef = useRef<string>(''); // 순서 무관 paramKey (reorder 감지용)
const prevMarket = useRef<string>(market);
const prevMarketTf = useRef<Timeframe>(timeframe);
const lastAppliedMarketRef = useRef<string>('');
const prevChartType = useRef<ChartType>(chartType);
const prevTheme = useRef<Theme>(theme);
const prevLogScale = useRef<boolean>(logScale);
@@ -381,19 +383,19 @@ const TradingChart: React.FC<TradingChartProps> = ({
return;
}
barsRef.current = newBars;
reloadInFlightRef.current = true;
try {
do {
reloadCoalesceRef.current = false;
const gen = reloadGenRef.current;
if (newBars.length === 0) break;
const barData = barsRef.current.length >= 2 ? barsRef.current : newBars;
if (barData.length < 2) break;
reloadSafetyTimers.current.forEach(clearTimeout);
reloadSafetyTimers.current = [];
barsRef.current = newBars;
const barData = newBars;
barsRef.current = barData;
mgr.setData(barData, ct, th);
if (gen !== reloadGenRef.current) {
@@ -405,6 +407,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
mgr.setDisplayTimezone(displayTimezone, timeframe);
commitCandleLayout(mgr);
onCandlesReady?.();
lastAppliedMarketRef.current = market;
let stale = false;
for (const ind of inds) {
@@ -449,6 +452,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
prevBarsKey.current = barsKey(barData, market);
prevIndKey.current = indKey(inds);
prevSortedPKRef.current = sortedParamKey(inds);
lastAppliedMarketRef.current = market;
await new Promise<void>(resolve => requestAnimationFrame(() => requestAnimationFrame(() => {
applyPaneLayout(mgr);
@@ -818,6 +822,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
prevMarketTf.current = timeframe;
prevBarsKey.current = '';
prevIndKey.current = '';
lastAppliedMarketRef.current = '';
barsRef.current = [];
reloadRetryRef.current = 0;
reloadGenRef.current += 1;
@@ -832,11 +837,11 @@ const TradingChart: React.FC<TradingChartProps> = ({
// ── 데이터/인디케이터 동기화 ─────────────────────────────────────────────
useEffect(() => {
const mgr = managerRef.current;
if (!mgr || !chartMgr || bars.length === 0) return;
if (!mgr || !chartMgr || bars.length < 2) return;
if (!canApplyChartBars(bars, barsMarket, market)) return;
// manager 준비 전 bars 가 먼저 도착한 경우(로그인 직후 등) 메인 시리즈 누락 방지
if (!mgr.hasMainSeries()) {
// manager 준비 전·종목 미적용 시 reloadAll 강제
if (!mgr.hasMainSeries() || lastAppliedMarketRef.current !== market) {
void reloadAll(mgr, bars, chartType, theme, logScale, indicators);
return;
}
@@ -942,7 +947,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
// 데이터는 준비됐으나 메인 시리즈가 없는 빈 차트 자동 복구 (종목 전환·레이아웃 전환 직후)
useEffect(() => {
const mgr = managerRef.current;
if (!mgr || !chartMgr || bars.length === 0) return;
if (!mgr || !chartMgr || bars.length < 2) return;
if (!canApplyChartBars(bars, barsMarket, market)) return;
if (mgr.hasMainSeries()) return;
@@ -969,7 +974,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
// 종목 전환 직후 reloadAll 이 중단되어 캔들만 남은 경우 자동 복구
useEffect(() => {
const mgr = managerRef.current;
if (!mgr || !chartMgr || bars.length === 0) return;
if (!mgr || !chartMgr || bars.length < 2) return;
if (!canApplyChartBars(bars, barsMarket, market)) return;
const expected = countExpectedVisibleIndicators(indicators);