21.6.3.1 InfluxDB 분산 쿼리와 WebSocket 실시간 스트리밍 버퍼의 타임 시계열(Time Series) 병합
관제 대시보드 프론트엔드(React)가 켜지는 순간, 과거의 거대 덤프 덩어리(Cold Fetch)를 InfluxDB에서 긁어오고, 곧바로 그 뒤를 이어 라이브 웹소켓(WebSocket) 스트림 데이터가 스토어(Zustand Map)위로 쏟아지게 덮어쓰는 기동술을 갈파했다(21.6.1.3장 참조).
하지만 단순한 “현재 최신 좌표(Latest Value Map)” 덮어쓰기 렌더링이 아니라, 과거 1시간 동안 로봇이 이동한 “궤적 선(Trajectory/Line Chart)” 과 같은 시계열 배열(Time Series Array) 전체를 화면에 이어붙여 그려야 하는 미션이라면?
여기서 과거(InfluxDB)의 선과 현재(WebSocket)의 선이 바통 터치를 하는 0.1초의 틈새 사이에 치명적인 데이터 누락(Gap)이나 동일 데이터가 두 번 겹쳐 그려지는 버블 중복(Overlap) 크래시가 터져 나온다.
본 절에서는 과거의 고립된 저장망(Storage)과 현재의 무한 속도 푸시망(Streaming)이 충돌하는 이음매를, 단 1ms 타임스탬프의 어긋남 없이 무결점의 시계열 자크로 결속시켜버리는 시간 쐐기(Time-wedge) 기반 버퍼 병합 런북을 갈파한다.
1. 틈새(Gap)와 중복(Overlap): 분산 시간계의 시차 폭력
클라이언트가 T=10s 시점에서 부팅하며 아래 두 가지 짓을 동시에 벌인다.
Sub(웹소켓) 연결 시도: 10s 부터 날아오는 데이터들을 받기 시작!Get(InfluxDB 쿼리) 요청: “과거부터 방금 10s 까지 데이터 좀 줘!”
문제망 1 (데이터 증발): Get 은 날렸고 아직 쿼리 응답은 안 왔는데(1초 기다림), Sub 도 네트워크 딜레이(2초 기다림)로 아직 안 붙었다면? 10.5초 경에 로봇이 쏜 중요한 크래시 데이터는 과거DB 덤프에도 없고 실시간 푸쉬망에도 탑승하지 못한 채 영원한 허공으로 증발(Gap)한다.
문제망 2 (시간 역전 덮어쓰기): Sub 웹소켓 망이 먼저 열려서 11s 짜리 최신 데이터를 화면에 찍었다! 그런데 뒤늦게 느려터진 Get InfluxDB 응답 데이터가 10초 치 과거 데이터 1MB 덩어리를 들이부어서 방금 그린 11s 최신 데이터 배열의 꼬리를 끊어먹고 타임라인을 뒤집어엎는다!
2. 시간 쐐기(Time-Wedge): 절단면 타임스탬프 격리
이 참형을 틀어막기 위해, 프론트엔드 Zustand 상태 관리자는 데이터의 원천(Source)이 DB인지 WebSocket 인지 구별하지 않는다. 오직 오만한 잣대, 타임스탬프(Unix Epoch ms) 라는 절대시간 위상 하나만으로 데이터를 필터링하고 걸러낸다(Dedup).
// [시계열 불변성(Time-series Invariance) 확보를 위한 병합 알고리즘 런북]
// Zustand Store 의 궤적 보관소
let trajectory = [];
// 콜드 스타트를 시작한 그 찰나의 시간 (절대 시간 쐐기!)
const WEDGE_TIME = Date.now();
// 1. [실시간 WebSocket의 행동 지침]
ws.onmessage = (data) => {
// 쐐기 시간(WEDGE_TIME) 이후에 발생한 새파란 신생아 데이터들만 무조건 꼬리에 붙인다!
// 만약 과거 찌꺼기가 밀려 들어와도 시간 방어막에 튕겨 컷오프(Drop)시킨다.
if (data.timestamp >= WEDGE_TIME) {
trajectory.push(data);
}
};
// 2. [InfluxDB Cold Fetch 응답의 행동 지침]
fetchFromDB().then((historyData) => {
// 쐐기 시간(WEDGE_TIME) 이전의 순수한 과거 조상 데이터들만 추출한다.
const pureHistory = historyData.filter(d => d.timestamp < WEDGE_TIME);
// 이 과거 데이터를 웹소켓이 한창 쌓아 올리고 있는 배열의 "머리통(Front)" 에
// 통째로 갖다 붙인다(Prepend)!
trajectory = [...pureHistory, ...trajectory];
});
3. 오버랩핑 배열 정렬의 승리와 무결점 궤적
이 WEDGE_TIME 이라는 시간의 기둥 하나를 땅에 박음으로써 프론트엔드 상태관리는 모든 레이턴시와 비동기 네트워킹의 혼돈(Chaos)을 벗어난다.
웹소켓(Sub)은 기둥 시간 이후의 미래(Future) 영토만 관리하며 배열 뒤편(Push)을 차곡차곡 채운다.
느려 터진 데이터베이스 쿼리(Get) 비동기가 5초 뒤에 도착하건 10초 뒤에 도착하건 신경 쓰지 않는다. 도착하는 순간 그놈들은 기둥 시간 이전의 과거(History) 파편임이 보증되었으므로, 그냥 배열 맨 앞(Unshift 혹은 Prepend)에 통째로 덧대어 밀어 넣어 버리면 그만이다.
두 갈래의 이기종 프로토콜이 가져온 비동기 파편들이, 오직 Timestamp 매직 넘버 하나를 축으로 하여 프론트엔드 메모리 배열(Array)에서 완벽히 머리와 꼬리로 접합하며 하나의 뱀(시계열 라인)으로 소생하는 구조. 네트워크의 변덕이 가져오는 파편화(Fragmentation)를 클라이언트의 시간적 정렬술(Temporal Ordering)로 단숨에 폭압하고 무결점 그래프 렌더링에 도달하는 최정예 상태 병합 기교다.