21.6.1.1 Node.js 기반 WebSocket 중계 및 상태 관리(State Management) 라이브러리 결합
Zenoh 아키텍처 세계관에서 수천 대의 로봇(IoT) 엣지들이 초당 수백만 개의 위치 정보 센서 토픽(telemetry/robot_*/pose)을 퍼블리싱하며 클라우드로 쏠 때, 이 압도적인 난류(Stream)를 인간이 눈으로 보는 프론트엔드 대시보드 화면상으로 안전하게 착륙시켜야 하는 치열한 후반전이 시작된다.
프론트엔드(React 브라우저 탭)로 수십만 개의 멀티캐스트 네트워크 트래픽을 무식하게 직접 때려 박는 짓은 브라우저 탭 V8 엔진을 3초 만에 뻗게(OOM Chrome Crash) 만드는 브라우저 테러 행위다 (21.6.2.1장 참조).
본 절에서는 거친 야생의 Zenoh 메쉬망과 나약한 브라우저 클라이언트 사이를 격리시키는 장대한 L7 방파제, 즉 Node.js (Express/NestJS) 기반의 Websocket 중계 백엔드 관문과 Redux/Zustand 를 결속시키는 상태 관리(State Management) 브릿지 런북을 갈파한다.
1. 프론트엔드 직결(Direct Pub/Sub) 타격의 허상과 백엔드 필터링
Zenoh-TypeScript 환경을 구축하는 자바스크립트 뉴비들이 가장 많이 저지르는 패착이 바로 크롬(Chrome) 브라우저 탭의 React.useEffect 안에다가 냅다 zenoh.open() 세션을 찌르고 declare_subscriber 를 박아버리는 짓이다.
수만 대의 로봇이 0.1초마다 좌표를 쏘아댈 때 (1만 개 패킷/초), 브라우저 렌더러는 화면을 그리기(Paint) 는커녕 DOM 에 도달하기도 전에 Event Loop 에 수만 개의 I/O 메시지 트립을 처리하느라 즉각적인 마비(Freezing) 발작을 벌인다.
이 참사를 끊어내기 위해, V8 엔진보다 훨씬 네트워크 I/O 동시성에 강한 중간 완충 지대, Node.js Gateway Server 가 투입되어야만 한다.
2. Node.js 백엔드단 Zenoh 세션 흡수 및 WebSocket 스로틀링(Throttling)
백엔드 서버(Node.js)는 클라이언트 어플리케이션(React)이 감당할 수 있는 만큼만 데이터를 압축하고 버려(Throttling)주어야 하는 절대적인 ‘정수 기’ 역할을 수행한다.
// [Node.js Gateway의 무자비한 스로틀링 및 Websocket 중계 런북]
import * as zenoh from '@eclipse-zenoh/zenoh';
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
const session = await zenoh.open();
// 1. 거친 Zenoh 야생 메쉬망의 트래픽을 이 튼튼한 서버의 메모리로 다 빨아들인다!
let robotStates = new Map();
session.declareSubscriber("telemetry/robot_*/pose", (sample) => {
const robotId = extractId(sample.keyExpr);
// 2. [가장 중요] 브라우저에 바로 쏘지 않는다! 서버의 1차원 상태 맵(State Map)에
// 로봇의 가장 최신(Latest) 좌표 하나로만 무식하게 덮어 써버린다(Overwrite).
// (과거 궤적의 변화 이력 따윈 이 라우팅 단계에서 가차 없이 버려버림)
robotStates.set(robotId, JSON.parse(sample.payload.toString()));
});
// 3. 나약한 React 브라우저 놈들을 위해, 매 1초(1000ms)마다 딱 1번씩만
// 모아뒀던 전체 상태(State) 스냅샷 덩어리를 WebSocket으로 젠틀하게 쏴준다! (Throttling)
setInterval(() => {
const snapshot = JSON.stringify(Object.fromEntries(robotStates));
wss.clients.forEach(client => client.send(snapshot));
}, 1000);
클라우드와 연결된 5G 안테나는 노드(Node.js)의 밑단에서 초당 수십 기가비트의 폭풍을 일으키고 있지만, 브라우저가 보게 되는 건 단지 “1초에 딱 1번씩 날아오는 조용하고 평화로운 Map 덩어리” 가 된다. L7 레벨에서 레이턴시 정밀도(60FPS)를 조금 희생하고 시스템 처리 한계치 마진벽(DOM Crash)을 완벽하게 우회해 낸 트레이드오프(Trade-off) 전략이다.
3. React 상태 라이브러리(Zustand)의 동기화 착륙장 결합
이제 평화로워진 1초 간격의 WebSocket 덩어리가 React 브라우저에 도착한다.
이 데이터를 컴포넌트의 로컬 useState 에 무식하게 박아 넣으면 거대한 프롭 드릴링(Prop Drilling) 지옥이 열린다.
백엔드 중계기에서 날아오는 구조화된 단일 Map 데이터는 전역 상태 관리 저장소인 Zustand (또는 Redux) Store 한가운데 핵으로 폭격 시켜 단방향 플로우(Uni-directional Data Flow)를 사수해야 한다.
// [React Frontend: Zustand 글로벌 옵저버버러 강제 착륙 런북]
import create from 'zustand';
// 전 지구 로봇들의 좌표 정보를 통관하는 절대 권력 Store!
const useRobotStore = create((set) => ({
robots: {}, // Node.js 가 보내주는 Map 이 적재될 영구 보관소
updateRobots: (newSnapshot) => set({ robots: newSnapshot })
}));
// WebSocket 커넥션은 오직 이 Store 만을 찔러 좌표를 밀어 넣고 사라진다.
ws.onmessage = (event) => {
const snapshot = JSON.parse(event.data);
useRobotStore.getState().updateRobots(snapshot); // 단 한방 타격 파동!
};
이렇게 하면, 거친 Zenoh C++ 파이프라인 \rightarrow Zenoh 멀티캐스트 로우레벨 망 \rightarrow Node.js WebSock 스로틀링 중계기 \rightarrow React Zustand 전역 스토어 로 이어지는 엔드-투-엔드(E2E) 이기종 단방향 데이터 통제 구조가 결성된다. 엣지 기기의 하드 리얼타임 폭도들을 가장 우아한 자바스크립트 객체의 웅덩이 단 하나로 모아들여 격리시키는 화면 통치의 기본이다.