"자기 socket 을 여는 모든 컴포넌트는 일어나길 기다리는 작은 누수야. 연결은 한 번 소유하고; 나머지는 그냥 듣게 해."
스트림은 컴포넌트별이 아니라 앱 레벨이야
Keep 은 live 가격을 원하는 workspace 여럿을 가져 — 포트폴리오 view, market pulse, focused ticker. 유혹적인 React 패턴은 그 컴포넌트마다 필요한 WebSocket 을 여는 거야. Keep 은 그걸 금지해. 단일 앱 레벨 hook(useMarketStream)이 유일한 WebSocket 을 소유해. 모든 workspace 는 자기 연결을 여는 대신 broadcast 이벤트(keep-market-update 이벤트)를 들어. 소유자 하나, listener 여럿.
왜 컴포넌트별 socket 이 엉망인가
컴포넌트마다 자기 socket 을 열게 하면 문제 더미에 등록하는 거야. 이제 세 컴포넌트가 같은 provider hub 로 세 연결을 쥐어, 부하 세 배. 각각 자기 mount/unmount 라이프사이클이라, 컴포넌트가 정리 없이 unmount 하면 socket 이 누수되거나, view 가 remount 하면 reconnect-storm 이 나. 컴포넌트들이 현재 가격을 두고 의견 갈릴 수 있어, socket 이 살짝 다른 순간에 tick 을 받았으니까. 그리고 'pause' 가 악몽이 돼 — 하나 대신 모든 컴포넌트의 socket 을 찾아 멈춰야 해. 컴포넌트별 연결은 깔끔한 관심사 하나를 엉킨 N 개로 바꿔.
bridge 와 overlay
밑에서 흐름은 깔끔한 모양이야. broadcast tick 이 먼저 thread-safe 하고 process-local 한 price bridge — 최신 값의 작은 in-memory store — 를 업데이트해. 그다음 REST projection(포트폴리오, watchlist, focus, pulse, export)이 렌더할 때 그 새 값을 durable 데이터 위에 overlay 해. 그래서 live 와 durable 은 UI 에서 싸우는 두 경쟁 source 가 아냐; live 는 읽을 때 안정된 base 위에 칠해진 얇고 fresh 한 레이어야. socket 소유자가 bridge 를 최신으로 유지하고; 나머지 모두는 bridge-over-durable 합성을 읽어.