"자기 socket 을 여는 컴포넌트 하나하나가 터지길 기다리는 작은 누수야. 연결은 한 곳이 소유하고, 나머지는 듣기만 해."
스트림은 앱 레벨이야, 컴포넌트 레벨이 아니라
Keep 엔 live 가격을 원하는 workspace 가 여럿 있어. 포트폴리오 view, market pulse, 종목 하나를 파고드는 Focus 까지. React 에서 제일 끌리는 패턴은 컴포넌트마다 필요한 WebSocket 을 알아서 여는 거지. Keep 는 그걸 금지했어. 앱 레벨 hook 하나(useMarketStream)가 유일한 WebSocket 들을 소유해. workspace 들은 자기 연결을 여는 대신 broadcast 이벤트(keep-market-update)를 듣기만 해. 주인은 하나, 듣는 쪽은 여럿.
컴포넌트별 socket 이 만드는 난장판
컴포넌트마다 socket 을 열게 두면 문제를 묶음으로 사들이는 거야. 컴포넌트 셋이 같은 provider hub 에 연결 셋을 걸어서 부하가 세 배가 돼. mount 와 unmount 라이프사이클도 제각각이라, 정리를 빼먹고 unmount 하면 socket 이 새고 view 가 다시 mount 되면 재연결 폭풍이 몰아쳐. 심지어 컴포넌트끼리 현재 가격을 두고 딴소리를 할 수도 있어. 각자의 socket 이 tick 을 받은 순간이 미묘하게 다르니까. 그리고 pause 는 악몽이 돼. 연결 하나를 멈추는 게 아니라 모든 컴포넌트의 socket 을 찾아다니며 꺼야 하거든. 컴포넌트별 연결은 깔끔한 관심사 하나를 뒤엉킨 N 개로 바꿔 놔.
bridge 그리고 overlay
그 아래 흐름도 모양이 깔끔해. broadcast 된 tick 은 먼저 thread-safe 한 process-local price bridge, 그러니까 최신 값들을 담는 작은 in-memory 저장소를 갱신해. 그다음 포트폴리오, watchlist, Focus, pulse, export 같은 REST projection 들이 렌더링할 때 그 최신 값을 durable 데이터 위에 overlay 해서 내보내. 그래서 live 와 durable 은 UI 에서 다투는 두 출처가 아니야. live 는 읽는 순간 안정된 바닥 위에 얹히는 얇고 신선한 막일 뿐이지. socket 주인이 bridge 를 최신으로 유지하고, 나머지 전부는 bridge 를 durable 위에 겹친 합성본을 읽는 거야.