21.6 시스템 모니터링 및 데이터 시각화: TypeScript 연동
방대한 분산 데이터를 인간 관리자가 한눈에 해석하고 통제하기 위해서는 조밀한 시각화 프론트엔드가 요구된다. 공장에 분산된 센서와 자율주행 물류 로봇(AMR)의 상태 데이터를 단일 스크린에 응집력 있게 그려내는 일은 스마트 팩토리의 정점이다.
웹 생태계와의 고성능 결합이 이 절의 핵심 목표다. Zenoh 모니터링 토픽을 백엔드에서 확보하고, WebSocket과 React 혹은 Vue.js 프레임워크를 결합하여 실시간 반응형 대시보드를 구축하라. 로봇 좌표를 2D/3D 지도에 투영하고, 고주파수 진동 센서 데이터를 실시간 차트로 렌더링(Rendering)하는 과정에서 TypeScript 생태계의 비동기 성능이 진가를 발휘할 것이다.
1. 프론트엔드(React/Vue) 기반 실시간 데이터 대시보드 구축
수천 개의 디바이스 트래픽이 웹 브라우저로 쏟아질 때 렌더링 성능이 저하되거나 메모리가 고갈되지 않게 관리하는 것이 최우선이다.
첫째, 상태 관리(State Management) 라이브러리와 WebSocket의 결합이다. 브라우저 클라이언트가 직접 Zenoh 네트워크에 붙는 것보다, Node.js 백엔드의 WebSocket을 경유하여 데이터를 수집하는 구조가 안정적이다.
// React 연동 예시
useEffect(() => {
const socket = new WebSocket('ws://localhost:3000/telemetry');
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
dispatch(updateRobotState(data));
};
return () => socket.close();
}, []);
둘째, 렌더링 격리 최적화다. 초당 다수 인입되는 센서 데이터가 애플리케이션 DOM 트리를 무분별하게 리렌더링시키지 않도록, React.memo와 같은 메모이제이션 기법을 적극 활용하여 타겟 차트만 독립적으로 업데이트되게 조치하라.
셋째, 콜드 스타트(Cold Start) 시나리오 설계다. 관리자가 브라우저를 처음 구동할 때, 백엔드를 통해 분산 쿼리(session.get)를 호출하여 전체 로봇의 마지막 상태와 과거 스토리지 이력을 로드해 바탕을 구성하라. 이후 수신되는 실시간 Pub/Sub 데이터들을 기존 화면에 패치(Patching)하는 하이브리드 로직을 구성하라.
2. 시스템 메트릭 수집 및 모니터링 플러그인 연동
네트워크와 라우터 인프라 자체의 건전성을 파악하는 것이 우선이다. Zenoh는 자체 헬스 체크를 위한 REST API를 지원한다.
클라우드 라우터 설정에서 rest 플러그인을 활성화("http_port": 8000)하고 Admin Space를 호출하라.
접속 경로 http://localhost:8000/@/router/local/** 를 통해 활성 세션 식별자, 에지 라우터 통신 량, 처리 바이트 등의 JSON 메트릭을 확보할 수 있다. 이 데이터들을 Prometheus가 긁어갈 수 있게 Node.js 서버에 익스포터(Metrics Exporter)를 구성하면 Grafana를 통한 인프라 대시보드가 완성된다.
3. 차트 라이브러리를 활용한 센서 시계열 데이터 시각화
AMR의 위치가 공간의 점이라면, 설비들이 뿜어내는 센싱 데이터는 시계열(Time Series) 데이터로 시각화해야 한다. InfluxDB에 적재된 과거 데이터와 실시간 데이터를 Chart.js나 Apache ECharts에 렌더링하라.
첫째, 과거 데이터와 실시간 데이터의 봉합이다. 화면 구동 시 분산 쿼리를 InfluxDB로 전송해 최근 1시간 이력 배열을 가져와 초기 차트를 그린다. 그 후 WebSocket으로 인입되는 실시간 스트리밍 데이터를 배열 뒤에 push()하고 오래된 데이터를 shift()하며 애니메이션 뷰를 갱신하라.
둘째, 윈도우잉(Windowing) 및 다운샘플링(Down-sampling)이다. 1kHz의 계측 데이터를 모두 렌더링하면 브라우저는 OOM(Out of Memory) 크래시를 일으킨다. TypeScript 백엔드에서 LTTB(Largest Triangle Three Buckets) 알고리즘이나 단순 평균을 적용해 1,000개의 점을 10개로 묶어 송출하고, 프론트엔드는 배열 크기를 상시 100개 이하로 제한 제한(slice(-100))하여 DOM 렌더링 병목 현상을 원천 차단해야 한다.
4. 2D/3D Canvas를 이용한 실시간 로봇 위치 맵핑
ROS2와 Zenoh를 거쳐 도착한 오도메트리(amr/+/odom) 데이터를 바탕으로 로봇의 좌표를 2D/3D 공간에 투영하라. 정적 HTML DOM 이동은 성능 제약이 크므로 HTML5 Canvas API나 WebGL 프레임워크를 활용해야 한다.
첫째, 공장 도면을 배경으로 깔고 로봇 픽셀 좌표를 변환하여 렌더링한다.
function drawRobot(ctx: CanvasRenderingContext2D, x: number, y: number, yaw: number) {
ctx.save();
// 맵 배율에 맞게 픽셀(Pixel) 좌표로 스케일링 변환
ctx.translate(x * SCALE_FACTOR, y * SCALE_FACTOR);
ctx.rotate(yaw); // 방향각 적용
ctx.fillStyle = "blue";
ctx.fillRect(-10, -10, 20, 20);
ctx.restore();
}
둘째, 프레임 최적화다. 브라우저의 requestAnimationFrame을 사용하여 모니터 주사율에 맞춰 부드럽게 화면을 갱신해야 시각화가 쾌적해진다. 이는 ROS의 Rviz2를 대체해 클라우드 웹에서 직접 로봇들을 관제할 수 있게 만든다.
5. 이벤트 기반 알람 및 Liveliness UI 업데이트 설계
운영자에게 가장 실리적인 기능은 비정상 징후를 스스로 포착해 경고음을 울리는 알람 시스템이다.
첫째, 에지 단말의 우선순위 알람 퍼블리싱이다. 백엔드가 모든 값을 폴링하여 연산하지 말고, 에지 단의 Rust/C 제어 노드가 임계치를 넘겼을 때 /factory/alarm/battery_low/amr_1과 같은 별도 전용 토픽으로 발포하게 하라.
둘째, 클라이언트 UI 업데이트 로직이다. 웹 화면은 alarm/# 토픽에 바인딩된 글로벌 Context를 유지하여 스트림 트리거에 즉효적으로 반응한다.
if (topic.startsWith('factory/alarm/')) {
toast.error(`긴급 징후 감지: ${payload.message}`);
highlightNodeOnMap(payload.source_id);
}
셋째, Liveliness 유실과 연동된 ‘조용한 장애’ 알람이다. 하드웨어가 파괴되어 전혀 데이터를 보내지 못하는 사태는 일반 송신 토픽으로는 파악할 수 없다. 라우터가 발동하는 Liveliness 토큰 Drop 이벤트를 백엔드가 수신하면, 강력한 연결 유실 팝업을 발생시키도록 설계하여 시각화 관제 시스템의 무결성을 확보하라.