문제 해결

차트 통합 실패에서 자주 발생하는 문제에 대한 복사 가능한 검사 및 수정 방법.

이 실패 모드는 diagnose_chart가 사용하는 동일한 버전 지식에서 생성됩니다. 애플리케이션과 가장 유사한 증상부터 시작하여 진단 검사를 적용하고, 문서화된 라이프사이클 및 타입화된 배열 소유권 계약을 보존하는 수정만 적용하세요.

일치하는 항목이 없으면, 관련 심볼을 중첩된 API 참조에서 검사하거나 로컬 MCP 서버에 집중 진단을 요청하세요. 재현 범위는 진단 단계로서만 renderMode: "main"animated: false로 축소하고 자동 생산 구성으로 사용하지 마세요.

증상 색인

캔버스가 비어 있거나 크기가 0입니다

해당: line, stock.

증상. 차트가 눈에 띄는 오류 없이 초기화되지만 플롯이 나타나지 않거나 캔버스 높이가 0픽셀입니다.

가능한 원인. 렌더러가 초기화될 때 캔버스 호스트에 명확한 높이가 지정되지 않았습니다.

확인하기

  • 호스트와 캔버스를 getBoundingClientRect()로 검사하세요; 너비와 높이 모두 0보다 커야 합니다.
  • 차트 행이 flex 또는 grid 상위 요소에 의해 축소되었는지 확인하세요.

수정

  • initialize()가 실행되기 전에 호스트에 구체적인 height 또는 min-height를 지정하세요.
  • 내재된 캔버스 크기에 의존하지 말고 캔버스가 해당 호스트를 채우도록 하세요.

차트에 측정 가능한 호스트 요소를 제공하세요

.chart-host {
  position: relative;
  width: 100%;
  height: clamp(20rem, 55vh, 34rem);
}

.chart-host > canvas {
  display: block;
  width: 100%;
  height: 100%;
}

차트가 준비되기 전에 데이터가 설정되어 있습니다

해당 대상: line, stock.

증상: 첫 번째 데이터셋이 나타나지 않고 이후 업데이트나 다시 마운트하면 정상 동작합니다.

가능한 원인. 일반 통합 방식이 initialize()가 완료되기 전에 데이터 메서드를 호출했거나 동일한 캔버스를 두 번 이상 초기화했습니다.

확인

  • 각 차트 인스턴스에 대해 initialize()가 정확히 한 번만 await되는지 확인하세요.
  • 뷰를 프레임워크가 관리하는 경우, 마운트 및 언마운트 순서를 처리해 주는 공식 어댑터를 사용하는 것이 좋습니다.

수정

  • setData() 또는 setMultiSeriesData()를 호출하기 전에 initialize()를 대기하세요.
  • 다른 인스턴스에 동일한 캔버스를 재사용하기 전에 기존 차트를 파기하세요.

기본 수명 주기(vanilla lifecycle)를 사용하세요

const chart = new LineChart(canvas, { renderMode: "auto" });
await chart.initialize();
chart.setData(data);

// Call this when the view is permanently removed.
export function disposeChart() {
  chart.destroy();
}

Worker 렌더링이 메인 스레드로 대체됩니다

적용 대상: line, stock.

증상. 차트는 동작하지만 메인 스레드 렌더링으로 해결되거나, 프로덕션에서만 Worker 생성이 실패합니다.

가능한 원인. Worker, OffscreenCanvas, transferControlToOffscreen 또는 배포된 콘텐츠 보안 정책(Content Security Policy)이 Worker 렌더러의 시작을 차단했습니다.

확인

  • 일시적으로 setStatsCallback()를 사용하여 통계를 검사하세요.renderMode.
  • 브라우저 콘솔과 worker-src Content Security Policy 지시자를 확인하세요.

수정

  • 진단을 위해 모드를 강제할 필요가 없다면 renderMode 을(를) 자동으로 유지하세요.
  • 애플리케이션에서 생성한 Worker URL을 worker-src에 허용하고 메인 스레드 폴백을 유지하세요.

해결된 렌더러를 확인하세요.

chart.setStatsCallback((stats) => {
  console.info("Sixtyfold renderer:", stats.renderMode);
  chart.setStatsCallback(null);
});

타입화된 배열은 setData 이후 분리(detached)됩니다.

적용 대상: line, stock.

증상: 소스 타입 배열이 byteLength 0이거나 데이터가 Worker 기반 차트로 전달된 후 더 이상 읽을 수 없습니다.

가능한 원인: 대용량 차트 데이터는 Worker 모드에서 렌더러로 전달되며, 전달된 ArrayBuffers는 호출자에서 사라집니다.

확인하세요

  • 대량 데이터 호출 직전과 직후에 byteLength를 검사하십시오.
  • 다른 하위 시스템이 실제로 읽을 수 있는 사본을 보관해야 하는지 확인하십시오.

수정

  • 대량 데이터 호출을 소유권 이전으로 처리하십시오.
  • 다른 하위 시스템이 유지해야 하는 열만 복제하고 기본적으로 수백만 개 값의 데이터셋을 중복하지 마십시오.

의도적인 애플리케이션 복사본을 유지하십시오.

const retainedX = sourceX.slice();
const retainedY = sourceY.slice();

chart.setData({
  x: sourceX,
  y: sourceY,
  length: sourceX.length,
});

// retainedX and retainedY remain readable.

시간 축이 압축되었거나 비어 있거나 먼 미래에 있습니다.

적용 대상: line, stock.

증상. 눈금에 터무니없는 날짜가 표시되거나, 표시 범위가 축소되거나, 팬 동작이 예측 불가능하게 나타납니다.

가능한 원인. X 열이 유한한 정렬된 epoch 밀리초 대신 초 단위나 문자열을 사용했거나, 관측값이 정렬되지 않았습니다.

확인하기

  • 첫 번째와 마지막 타임스탬프를 검사하고 new Date(value)로 변환하세요.
  • 각 타임스탬프가 유한하며 비감소(증가하지 않음)인지 확인하십시오.

수정

  • Float64Array를 생성하기 전에 epoch 초를 밀리초로 변환하십시오.
  • 수집 중에 ISO 날짜를 한 번만 파싱하고 열로 분할하기 전에 전체 관측값을 정렬하십시오.

수집 과정에서 타임스탬프 정규화

const timestamp = Float64Array.from(rows, (row) => {
  const value = Date.parse(row.recordedAt);
  if (!Number.isFinite(value)) throw new TypeError("Invalid recordedAt value");
  return value;
});

열 길이가 다르거나 숫자 값이 유효하지 않음

적용 대상: line, stock.

증상. 차트가 데이터셋을 거부하거나 예기치 않게 잘리거나 값이 잘못된 타임스탬프에 렌더링됩니다.

가능한 원인. 열형 데이터가 서로 다른 필터나 매핑에서 생성되어 시간 열과 값 열이 더 이상 동일한 관측값을 설명하지 않습니다.

확인하세요

  • 차트를 호출하기 전에 모든 열의 길이를 비교하세요.
  • 버퍼를 전송하기 전에 수치 변환을 검증하세요.

수정

  • 완전한 원시 관측값을 먼저 필터링하고 정렬한 다음 정렬된 타입별 열로 분할하세요.
  • 문서화된 선(라인) 계열의 간극을 나타낼 때만 명시적 NaN을 사용하세요; OHLCV 값은 유효한 캔들로 유지되어야 합니다.

전송 전에 정렬되지 않은 열은 거부하세요

const lengths = [timestamp.length, open.length, high.length, low.length, close.length, volume.length];
if (!lengths.every((length) => length === lengths[0])) {
  throw new RangeError(`OHLCV columns are misaligned: ${lengths.join(", ")}`);
}

SSR 또는 하이드레이션 과정에서 브라우저 전용 차트 API에 접근함

적용 대상: line, stock.

증상. 서버 렌더링 시 window, document, Worker 또는 HTMLCanvasElement를 사용할 수 없어 예외가 발생하거나 하이드레이션 결과 트리가 달라짐.

가능한 원인. 인터랙티브 브라우저 차트가 클라이언트 마운트 이후가 아니라 서버 평가 중에 생성됨

확인

  • 차트 생성 코드를 모듈 범위 또는 서버 컴포넌트 내부에 둡니다.
  • 브라우저 상호작용과 @sixtyfold/ssr를 통한 서버 이미지 생성을 분리합니다.

수정

  • 클라이언트 소유 컴포넌트 내부에서 공식 프레임워크 어댑터를 사용하세요.
  • SSR 중에는 안정적인 호스트를 렌더링하고 마운트된 후에만 대화형 차트를 초기화하세요.

클라이언트 경계 뒤에 Next.js 차트를 유지하세요

"use client";

import type { MultiSeriesData, TimeSeriesData } from "@sixtyfold/core";
import { SixtyfoldLineChart } from "@sixtyfold/react/line";

type LineData = TimeSeriesData | MultiSeriesData;

export function ChartPanel({ data }: { data: LineData }) {
  return <SixtyfoldLineChart data={data} options={{ renderMode: "auto" }} />;
}

네비게이션 후에도 Worker나 메모리가 남아 있음

적용 대상: line, stock.

증상. 뷰를 반복해서 열면 메모리가 증가하거나 이전 차트가 내비게이션 후에도 계속 렌더링됩니다.

가능한 원인. 기본 차트(vanilla chart) 또는 임시 데이터 Worker가 소유 뷰가 제거될 때 파기되지 않았습니다.

확인하세요

  • 여러 번의 마운트/언마운트 사이클 전후에 브라우저 힙 스냅샷을 기록하세요.
  • 유지되는 차트 인스턴스, Worker, 옵저버 또는 디코딩된 소스 배열을 찾으세요.

수정 방법

  • vanilla 차트 인스턴스마다 destroy()를 정확히 한 번 호출하세요.
  • 임시 fetch/decode Worker는 최종 전송 후 종료해야 합니다; 공식 프레임워크 어댑터는 차트 Worker를 자동으로 파기합니다.

차트 및 수집(ingestion) Worker를 해제하세요

return () => {
  dataWorker?.terminate();
  chart.destroy();
};

상호작용이 느리거나 프레임이 떨어집니다

적용 대상: line, stock.

증상. 확대/축소, 팬 이동, 초기 디코딩 또는 스트리밍 업데이트가 눈에 띄게 차단되거나 목표 프레임률을 맞추지 못함.

가능한 원인. 메인 스레드가 너무 많은 데이터를 디코딩하거나 재형성하고 있거나, 차트가 폴백 렌더러로 해석되었거나, 보이는 작업량이 대상 장치가 감당할 수 있는 그리기 패스 수를 초과함.

확인하십시오

  • 통계 검사.renderMode, frameTime, 보이는 작업량 및 실제 상호작용 순서 동안의 상세도(LOD) 준비 상태.
  • 프로파일 가져오기, 디코딩, 변환, 전송 및 렌더링을 분리하십시오.

수정

  • renderMode auto를 사용하고, 전용 데이터 Worker에서 열 기반 타입화 배열을 준비한 다음 한 번만 전송하고 해당 수집(ingestion) Worker를 종료하십시오.
  • 대표적인 브라우저, DPR, 시리즈 수 및 제스처를 벤치마크하십시오. 60 FPS는 장치와 무관한 보장이 아니라 설계 목표입니다.

활성 렌더링 경로 측정

chart.setStatsCallback((stats) => {
  console.table({
    mode: stats.renderMode,
    fps: stats.fps,
    frameTime: stats.frameTime,
    ready: stats.lodReady,
  });
}, { intervalMs: 250 });

확대/축소가 안정된 후 선 세부 사항이 갑자기 변경됨

해당 대상: line.

증상. 뷰포트는 부드럽게 애니메이션되지만 제스처가 끝날 때 선 텍스처가 다시 변경됩니다.

가능한 원인. 프레젠테이션 밀도 또는 그리드 재기준화가 워크로드에 비해 너무 공격적이거나, 애니메이션 완료 후 애플리케이션 코드가 데이터나 외형을 교체합니다.

확인하세요

  • 뷰포트 시퀀스를 동일하게 재생하면서 프레젠테이션 밀도, 그리드 델타 및 쿼리 방문을 관찰하세요.
  • 애니메이션이 안정된 후 애플리케이션 측에서 setData(), setAppearance() 또는 뷰포트 복원 여부를 확인하세요.

수정

  • 적응형 모드, 밀도 0.75, rebaseRatio 1.25 및 quantizationStep 0.25로 시작하세요.
  • 비용이 많이 드는 범위 채우기나 특히 큰 다중 시리즈 작업에는 밀도 0.25를 사용하고, 계수를 하나씩 조정하려면 setLODOptions()를 사용하세요.

차트를 재생성하지 않고 프레젠테이션을 조정하세요.

chart.setLODOptions({
  mode: "adaptive",
  density: 0.75,
  rebaseRatio: 1.25,
  quantizationStep: 0.25,
});

선이 누락된 관측값 구간을 가로질러 연결됩니다

적용 대상: line.

증상. 선이 측정값이 존재하지 않는 구간을 시각적으로 가로지릅니다.

가능한 원인. 누락된 관측값이 정렬된 NaN 간격으로 표현되지 않고 제거되었거나 0으로 대체되었습니다.

확인

  • 표시된 중단이 필요할 때 누락된 구간을 포함하도록 공유된 X 열이 맞는지 확인하세요.
  • 수집 과정에서 null 값이 0으로 변환되었는지 검사하세요.

수정

  • X 위치를 정렬 상태로 유지하고 누락된 선 값은 Number.NaN으로 인코딩합니다.
  • OHLCV 캔들을 합성하거나 시장 휴장 구간을 연결하지 마십시오; 주식의 시간 스케일 동작은 관찰된 캔들에 의해 결정됩니다.

실제 선 시리즈의 갭을 표시합니다.

const data = {
  x: new Float64Array([0, 1, 2, 3]),
  y: new Float64Array([12, Number.NaN, Number.NaN, 18]),
  length: 4,
};