문서 › 상호작용과 내보내기
실시간 차트 (스트리밍)
실시간 차트: append() 로 이어 붙이고 오래된 점은 창 밖으로, update() 는 부드럽게 전환. 14가지 차트 모두, SSE·WebSocket 예제.
실시간 차트란 데이터가 들어오는 대로 다시 그려 지금 상태를 보여 주는 차트입니다(접속자 수, 서버 자원, 체결 가격, 민원 접수 현황). 82chart 는 realtime 옵션 하나로 14가지 차트 모두 실시간으로 쓸 수 있습니다. 두 가지 방법이 있습니다.
- 이어 붙이기 —
chart.append(points): 선·영역·막대·산점도(시간 축이나 카테고리 축), 콤보, 달력 히트맵. 새 점은 오른쪽에서 미끄러져 들어오고 오래된 점은 창(window점 개수,duration밀리초) 밖으로 빠집니다. 값 축은 새 범위로 부드럽게 넓어지거나 좁아집니다(yAxis.min/max를 주면 고정). - 바꾸기 —
chart.update(data)나 값 하나만 바꾸는chart.setValue(시리즈, 순서, 값): 모든 종류. 파이·도넛 조각 각도, 게이지 값, 방사형 다각형, 막대·피라미드·퍼널·폭포 길이, 히트맵·달력·트리맵 색과 칸 위치, 가운데 합계와 라벨 숫자가 이전 값에서 새 값으로 이어지며 바뀝니다. 새 항목은 0에서 자라납니다.
움직이는 예제
이 예제의 코드
const chart = createChart('#chart', {
type: 'line',
title: '실시간 접속자',
unit: '명',
xAxis: { type: 'time' },
zoom: true,
realtime: { duration: 60000 },
data: { series: [{ name: '접속자', data: [] }, { name: '로그인', data: [] }] },
});
let users = 1200, logins = 400;
setInterval(() => {
users = Math.max(0, users + Math.round((Math.random() - 0.48) * 80));
logins = Math.max(0, Math.round(users * (0.3 + Math.random() * 0.05)));
chart.append({ series: [[[Date.now(), users]], [[Date.now(), logins]]] });
}, 500);위 차트는 0.5초마다 점을 붙이고 최근 1분(duration: 60000)만 남깁니다. 마우스를 올리면 그림이 멈춰 툴팁이 그 점에 머무르고, 손을 떼면 그동안 들어온 데이터를 따라잡습니다(pauseOnHover). 확대(+)하면 그 구간을 보고 있는 동안에는 따라가지 않고 "최신으로" 단추가 나옵니다.
바꾸기: 모든 차트가 부드럽게
움직이는 예제
이 예제의 코드
const chart = createChart('#chart', {
type: 'donut',
title: '결제 수단 (실시간)',
unit: '건',
realtime: { tween: 400 },
data: { labels: ['카드', '간편결제', '계좌이체', '휴대폰'], series: [{ name: '결제', data: [520, 310, 120, 50] }] },
});
const counts = [520, 310, 120, 50];
setInterval(() => {
counts[Math.floor(Math.random() * 4)] += Math.round(Math.random() * 40);
chart.update({ labels: ['카드', '간편결제', '계좌이체', '휴대폰'], series: [{ name: '결제', data: [...counts] }] });
}, 800);움직이는 예제
이 예제의 코드
const chart = createChart('#chart', {
type: 'gauge',
title: 'CPU 사용률',
unit: '%',
realtime: { tween: 500 },
gauge: { max: 100, target: 80, targetLabel: '경고', bands: [{ to: 60, color: '#1baf7a' }, { to: 80, color: '#eda100' }, { to: 100, color: '#e34948' }] },
data: { series: [{ name: 'CPU', data: [42] }] },
});
let cpu = 42;
setInterval(() => {
cpu = Math.min(100, Math.max(3, cpu + (Math.random() - 0.5) * 18));
chart.setValue(0, 0, Math.round(cpu));
}, 1000);움직이는 예제
이 예제의 코드
const chart = createChart('#chart', {
type: 'treemap',
title: '업종 등락 (실시간)',
colorBy: 'change',
deltaColors: 'korea',
realtime: { tween: 600 },
data: { series: [
{ name: '반도체', data: [{ x: '삼성전자', y: 420, change: 0.8 }, { x: 'SK하이닉스', y: 140, change: -1.2 }] },
{ name: '자동차', data: [{ x: '현대차', y: 52, change: 0.4 }, { x: '기아', y: 41, change: -0.3 }] },
{ name: '인터넷', data: [{ x: 'NAVER', y: 33, change: 1.5 }, { x: '카카오', y: 18, change: -2.1 }] },
] },
});
setInterval(() => {
const d = chart.getOptions().data;
chart.update({ series: d.series.map(s => ({ ...s, data: s.data.map(p => ({ ...p, change: +(p.change + (Math.random() - 0.5) * 0.6).toFixed(2) })) })) });
}, 1500);realtime 옵션
| 옵션 | 기본값 | 설명 |
|---|---|---|
realtime | - | true 또는 아래 객체. 없으면 update() 는 지금처럼 바로 다시 그림 |
window | - | 시리즈마다 남길 최대 점 수 (달력은 날 수) |
duration | - | 시간 축: 최근 이 밀리초만 남김 (예: 60000 = 1분) |
pauseOnHover | true | 마우스가 데이터 위에 있는 동안 그림을 멈춤 (데이터는 계속 받음) |
fps | 30 | 초당 최대 그리기 횟수. 한 프레임 안에 들어온 append·update 는 한 번에 그림 |
tween | 300 | update()·setValue() 가 새 값으로 바뀌는 시간(ms). 0 이면 바로. "동작 줄이기" 설정이면 언제나 바로 |
API
| 메서드 · 이벤트 | 설명 |
|---|---|
append(points, { series }) | 점 하나 { x, y }, 한 시리즈의 목록 [[x, y], …], 또는 여러 시리즈 { labels?, series: [[…], […]] }. 숫자 x 는 xAxis: { type: 'time' } 에서 에포크 밀리초 |
update(data) | realtime 이면 프레임에 맞춰 묶고 부드럽게 바꿈 |
setValue(series, index, y) | 값 하나만 바꿈 (게이지, 조각 하나, 칸 하나) |
clear() | 모든 점을 지움 (시리즈 이름과 옵션은 그대로) |
isLive() / follow() | 최신 데이터를 따라가는 중인지 / 확대를 풀고 다시 따라가기 ("최신으로" 단추와 같음) |
announce() | 지금 요약을 스크린리더에 바로 읽어 줌 |
on('append', ({ added, total }) => …) | append 할 때마다 |
서버에서 받기: SSE 와 WebSocket
// 서버가 보내는 이벤트(SSE): data: {"t": 1792800000000, "v": 1234}
const chart = createChart('#chart', { type: 'line', xAxis: { type: 'time' }, realtime: { duration: 5 * 60000 }, data: { series: [{ name: '요청 수', data: [] }] } });
const es = new EventSource('/api/stream');
es.onmessage = (e) => { const { t, v } = JSON.parse(e.data); chart.append([[t, v]]); };// WebSocket: 한 메시지에 여러 시리즈
const ws = new WebSocket('wss://example.go.kr/metrics');
ws.onmessage = (e) => {
const m = JSON.parse(e.data); // { t, cpu, mem }
chart.append({ series: [[[m.t, m.cpu]], [[m.t, m.mem]]] });
};
// 화면을 떠날 때
// ws.close(); chart.destroy();접근성과 성능
- 스크린리더 알림(
aria-live)은 데이터가 들어올 때마다가 아니라 많아야 10초에 한 번입니다. 키보드·마우스로 옮기면 바로 읽고,chart.announce()로 원할 때 읽힐 수 있습니다. 차트 요약(aria-label)은 1초에 한 번 고칩니다. - 숨긴 데이터 표와
toCSV()는 지금 창의 데이터입니다(표는 1초에 한 번 고침). - 매 프레임 SVG 를 새로 만들지 않고, 구조가 같으면 기존 요소의 속성과 글자만 바꿉니다. 그래서 요소 수가 늘지 않고 툴팁·강조가 끊기지 않습니다.
- 실시간 차트는 점진 렌더링을 하지 않고, 점이 그림 폭의 절반보다 많으면 LTTB 로 줄여 그립니다(CSV·표는 원본).
- "동작 줄이기"(
prefers-reduced-motion)가 켜져 있으면 미끄러짐·전환 없이 바로 바꿉니다. - 측정(노트북, Chrome, 개발 빌드): 4개 시리즈 × 2,000점 창에 초당 60번 붙이면 한 번 그리는 데 평균 약 7ms(p95 11ms), 시리즈 2개 × 120점 선은 약 1.5ms, 파이·게이지·트리맵 같은 바꾸기 차트는 1ms 안팎입니다.