실시간 차트 갱신오류 수정

This commit is contained in:
Macbook
2026-05-25 03:22:46 +09:00
parent 67324ded9d
commit 3b67fca4ee
2 changed files with 42 additions and 21 deletions
+38 -20
View File
@@ -7,6 +7,20 @@ import { DISPLAY_COUNT } from '../hooks/useUpbitData';
/** Upbit 실시간 — 초기 히스토리가 이 개수 미만이면 setData 하지 않음 (종목 전환 직후 1~2봉만 로드되는 깨짐 방지) */
const MIN_BARS_FOR_FULL_RELOAD = Math.min(50, Math.floor(DISPLAY_COUNT / 4));
/** 종목 전환 직후 stale/partial 데이터 차단 — fetch 완료(barsMarket 확정) 후 sparse history도 렌더 허용 */
function canApplyChartBars(
barData: OHLCVBar[],
barsMarket: string | null | undefined,
market: string,
): boolean {
if (barData.length === 0) return false;
if (barsMarket === undefined) return true;
if (barsMarket == null || barsMarket !== market) return false;
if (!market.startsWith('KRW-')) return true;
if (barData.length >= MIN_BARS_FOR_FULL_RELOAD) return true;
return barData.length >= 2;
}
import DrawingCanvas, { hitTestDrawing } from './DrawingCanvas';
import PaneLegend, { type PaneLegendProps } from './PaneLegend';
import ChartHoverToolbar from './ChartHoverToolbar';
@@ -167,14 +181,9 @@ const TradingChart: React.FC<TradingChartProps> = ({
const [candleOnlyMode, setCandleOnlyMode] = useState(false);
useEffect(() => {
const ready = barsMarket == null
? false
: barsMarket === undefined
? true
: barsMarket === market;
if (ready) {
if (barsMarket === market) {
barsRef.current = bars;
} else if (barsMarket != null && barsMarket !== market) {
} else {
barsRef.current = [];
}
}, [bars, barsMarket, market]);
@@ -296,8 +305,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
const barData = barsRef.current;
const inds = indicatorsRef.current;
if (!m || barData.length === 0) return;
if (barsMarket !== undefined && barsMarket !== market) return;
if (market.startsWith('KRW-') && barData.length < MIN_BARS_FOR_FULL_RELOAD) return;
if (!canApplyChartBars(barData, barsMarket, market)) return;
prevBarsKey.current = '';
reloadAllRef.current?.(
m, barData,
@@ -315,7 +323,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
const barData = barsRef.current;
const inds = indicatorsRef.current;
if (!m || barData.length === 0) return;
if (barsMarket !== undefined && barsMarket !== market) return;
if (!canApplyChartBars(barData, barsMarket, market)) return;
const expected = countExpectedVisibleIndicators(inds);
if (expected === 0) return;
@@ -699,8 +707,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
// 데이터가 이미 barsRef에 있지만 차트에 아직 세팅되지 않은 경우 (예: 멀티레이아웃 초기 렌더) 재로드
if (barsRef.current.length > 0 && !m.hasMainSeries()) {
const bm = barsMarketRef.current;
if (bm != null && bm !== marketRef.current) return;
if (marketRef.current.startsWith('KRW-') && barsRef.current.length < MIN_BARS_FOR_FULL_RELOAD) return;
if (!canApplyChartBars(barsRef.current, bm, marketRef.current)) return;
void reloadAll(
m,
barsRef.current,
@@ -723,6 +730,22 @@ const TradingChart: React.FC<TradingChartProps> = ({
});
ro.observe(containerRef.current);
const bootstrapIfReady = () => {
const m = managerRef.current;
if (!m || m.hasMainSeries()) return;
const data = barsRef.current;
if (!canApplyChartBars(data, barsMarketRef.current, marketRef.current)) return;
void reloadAllRef.current(
m,
data,
prevChartType.current,
prevTheme.current,
prevLogScale.current,
indicatorsRef.current,
);
};
requestAnimationFrame(() => requestAnimationFrame(bootstrapIfReady));
return () => {
unsub();
unsubClick();
@@ -772,12 +795,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
useEffect(() => {
const mgr = managerRef.current;
if (!mgr || !chartMgr || bars.length === 0) return;
const barsReady = barsMarket === undefined
? true
: barsMarket === market;
if (!barsReady) return;
if (market.startsWith('KRW-') && bars.length < MIN_BARS_FOR_FULL_RELOAD) return;
if (!canApplyChartBars(bars, barsMarket, market)) return;
// manager 준비 전 bars 가 먼저 도착한 경우(로그인 직후 등) 메인 시리즈 누락 방지
if (!mgr.hasMainSeries()) {
@@ -887,7 +905,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
useEffect(() => {
const mgr = managerRef.current;
if (!mgr || !chartMgr || bars.length === 0) return;
if (barsMarket !== undefined && barsMarket !== market) return;
if (!canApplyChartBars(bars, barsMarket, market)) return;
const expected = countExpectedVisibleIndicators(indicators);
if (expected === 0) return;
@@ -897,7 +915,7 @@ const TradingChart: React.FC<TradingChartProps> = ({
window.setTimeout(() => {
const m = managerRef.current;
if (!m || !m.hasMainSeries()) return;
if (barsMarket !== undefined && barsMarket !== market) return;
if (!canApplyChartBars(bars, barsMarket, market)) return;
const exp = countExpectedVisibleIndicators(indicatorsRef.current);
if (exp === 0 || m.getLoadedIndicatorCount() >= exp) return;
prevBarsKey.current = '';
+4 -1
View File
@@ -141,7 +141,7 @@ export function useStompChartData(
.then(newBars => {
if (cancelled) return;
barsRef.current = newBars;
historyReadyRef.current = newBars.length > 0;
historyReadyRef.current = true;
setBarsMarket(market);
setBars(newBars);
setLatestBar(newBars[newBars.length - 1] ?? null);
@@ -269,6 +269,9 @@ export function useStompChartData(
last1mVolRef.current = 0;
last1mTimeRef.current = 0;
void pinChartWatch(market, STOMP_TICK_CANDLE_TYPE);
if (candleType !== STOMP_TICK_CANDLE_TYPE) {
void pinChartWatch(market, candleType);
}
const topic = `/sub/charts/${market}/${STOMP_TICK_CANDLE_TYPE}`;
let alive = true;