WebSocket vs SSE, 실시간 기능 삽질기
실시간 알림 기능을 만들면서 WebSocket과 SSE 사이에서 삽질한 과정. 결론은 의외였다.
실시간 알림이 필요했다
사내 대시보드에 실시간 알림 기능을 붙여야 했다. 주문이 들어오면 담당자 화면에 바로 표시되는 거. "이거 WebSocket 쓰면 되지 않나?" 싶어서 바로 socket.io를 설치했다.
여기서부터 삽질이 시작됐다.
WebSocket, 생각보다 귀찮은 녀석
socket.io 연결 자체는 금방 됐다. 10분이면 기본 연결이 되고, 이벤트 주고받는 것도 간단하다. 근데 프로덕션에 올리려니까 문제가 쏟아졌다.
첫 번째, 로드밸런서. WebSocket은 HTTP와 다르게 지속적인 연결이라 로드밸런서에서 sticky session을 설정해야 했다. AWS ALB 설정을 만지는데 2시간이 날아갔다.
두 번째, 재연결 로직. 네트워크가 끊겼다 다시 연결될 때 놓친 메시지를 어떻게 처리할 건지. socket.io가 자동 재연결은 해주는데, 그 사이에 빠진 이벤트는 알아서 처리해야 한다.
(여기서 벌써 견적이 나오기 시작했다.)
잠깐, 이거 양방향 통신이 필요한가?
냉정하게 돌아보니까 우리 기능은 서버에서 클라이언트로 알림을 보내는 게 전부였다. 클라이언트에서 서버로 실시간 데이터를 보낼 일이 없었다. 채팅이 아니라 알림이었으니까.
이럴 때 SSE(Server-Sent Events)가 더 적합하다는 걸 뒤늦게 깨달았다. SSE는 HTTP 기반이라 로드밸런서 설정이 필요 없고, 브라우저가 자동으로 재연결을 해준다. EventSource API도 WebSocket보다 단순하다.
SSE로 갈아탔다
socket.io 코드를 싹 걷어내고 SSE로 다시 구현했다. 서버 쪽 코드가 절반으로 줄었다. 프론트도 new EventSource('/api/notifications') 한 줄이면 연결이 됐다.
배포 후 3주간 돌려보니 연결이 끊기는 일도 거의 없었고, 알림 지연도 평균 340ms로 충분했다. WebSocket으로 삽질한 3일이 허무했다.
근데 SSE에도 한계가 있긴 하다
브라우저당 동시 연결 수 제한이 있다. HTTP/1.1 기준으로 도메인당 6개. 탭을 여러 개 열면 연결이 부족해질 수 있다. HTTP/2에서는 이 제한이 사실상 없어지긴 하는데, 구형 환경을 고려하면 완전히 무시할 수는 없다.
그리고 바이너리 데이터를 보내야 하거나, 양방향 통신이 필요하면 당연히 WebSocket을 써야 한다.
정리하면 이렇다
채팅, 게임, 실시간 협업 도구처럼 양방향이 필요하면 WebSocket. 알림, 피드 업데이트, 대시보드 갱신처럼 서버에서 푸시만 하면 SSE. 이게 전부다.
근데 나처럼 "실시간 = WebSocket"이라는 고정관념 때문에 돌아가는 사람이 꽤 있을 것 같다. 3일 삽질 안 하려면 먼저 양방향이 필요한지부터 물어보는 게 맞다.