WebSocket으로 HTML을 보내 SPA를 만드는 방식, HN에서 SSE·캐싱·운영 복잡성 논쟁
- 글은 HTML over WebSockets를 서버가 JSON 대신 렌더링한 HTML을 지속적인 양방향 WebSocket 채널로 보내고, 브라우저 JavaScript는 수신 HTML을 DOM의 지정 위치에 넣는 방식으로 설명함
- Phoenix LiveView의 Chris McCord는 ElixirConf 2019에서 렌더링용 JavaScript 프레임워크 없이 15분 만에 실시간 Twitter 클론을 만들었고, 이후 다른 언어의 구현체에도 영향을 줬다고 소개함
- 글은 HTTP 기반 htmx와 Unicorn, 단방향 연속 채널인 SSE 기반 Datastar, 양방향 지속 채널인 WebSocket 기반 Phoenix LiveView와 Django LiveView를 HTML over the wire의 세 유형으로 구분함
- WebSocket 방식에서는 연결과 인증을 한 번 수행한 뒤 클라이언트가 경로를 보내면 서버가 DB를 조회하고 템플릿으로 HTML을 렌더링해 반환하며, 서버가 요청 없이 변경 사항을 푸시할 수도 있음
- 글은 클라이언트별 서버 프로세스가 상태를 기억하고 렌더링 엔진과 상태를 백엔드에 모아 API 계약과 JSON 중간 계층을 없앨 수 있다고 주장함
Hacker News opinions
Rails/Turbo나 htmx로 해보면 작은 화면 갱신에는 체감이 좋더라. API 없이 DB를 시스템 기록으로 두고, 새 레코드를 테이블 위에 스트리밍하는 정도는 Ruby 몇 줄이면 됨. 다만 갱신 의존 관계는 계속 머릿속에 들고 있어야 함.
양방향 저지연이 필요한 채팅, 협업, 게임이면 WebSocket이 맞지만, 서버에서 밀어주기만 하면 SSE가 운영도 더 단순하고 저렴함. 보통 클릭에 반응하는 요청이라면 Fetch와 HTTP/2 다중화로도 충분하고, WebSocket 위에 자체 요청 프로토콜을 만들 필요가 없음.
HTTP/2 다중화가 있어도 클라이언트가 통제 못 하는 구현 세부 사항에 기대는 셈임. HTTP 스택 전체를 거치는 것보다 길이와 마스크 정도인 WebSocket 프레이밍이 단순하고, HTTP/1 지속 WebSocket이 지연 시간에서는 더 나을 수 있음.
2026년인데 WebTransport 언급이 없어서 글을 신뢰하기 어려움. 브라우저가 모두 지원하고 WebSocket보다 지연 시간이 낮은 양방향 전송 수단임.
SSE 쪽이 골칫거리가 적고 확장도 쉬움. 팬아웃 프록시를 쓰지 않는다면 SSE가 WebSocket보다 나을 이유도 별로 없다고 봄.
WebSocket 서버가 공격받아 멈추면 서비스 전체가 멈출 수 있는데, 글은 이를 너무 가볍게 다룸.
정적 HTML은 Apache나 Nginx로 요청에 응답하면 됨. WebSocket 서버를 따로 두면 웹 서버와 소켓 서버의 버전 및 상태를 둘 다 추적해야 하고, 장애 감시와 페일오버도 복잡해짐. 기업이나 국가 DPI 방화벽이 WebSocket을 조용히 막는 경우도 있음.
글은 캐싱, 혹은 캐시 부재를 다뤘어야 함.
이건 DHTML, ASP.NET Ajax, JSF Ajax가 계속 다시 등장하는 모습 같음. Django 템플릿을 쓰면서 폼 POST와 전체 새로고침을 피하던 방식은 2010년쯤에도 했음.
SPA로 간 데는 이유가 있었음.
htmx에 SSE, DOM 교체와 모핑을 붙이면 이미 이 패턴을 구현함. 내가 만드는 웹 앱도 실시간 받은편지함과 알림이 곧 필요해져서 처음부터 그렇게 넣는 편임.
datastar를 보면 됨. 더 작고 빠르며 확장하기 쉬운 쪽이라고 봄.
htmx는 GET만 처리하고 지수 백오프도 없어서 확장 기능이 더 필요함. 직접 써보고 이미 해결됐다고 말하는 게 좋겠음.
지속 연결로 TCP 핸드셰이크와 HTTP 헤더를 매번 피한다는 주장에는 동의 못 함. 캐시 헤더를 잘 쓰면 큰 응답은 많이 줄고, 브라우저 TTL을 1분으로 둬도 대부분의 경우 충분함.
PHP에서는 Laravel Livewire 외에 Symfony Live Components도 있음. Livewire가 기존 방식보다 단순하게 잘 작동한다는 점은 더 알려질 만함.