-
Notifications
You must be signed in to change notification settings - Fork 3
⛏️ Node WebSocket 파고들기
분야 | 작성자 | 작성일 |
---|---|---|
BE | 김민수 | 24년 11월 05일 |
이번 프로젝트에서 Websocket
을 사용하게 되었다. Websocket
을 사용하는 기능은 채팅, 실시간 주가, 알림이다. 프로젝트를 기획하면서 3가지 기능에서 성능 상의 병목이 발생할 가능성이 크다고 생각했다. 따라서 이를 효율적으로 줄일 수 있는 방법을 고민하였고, Node에서 Websocket
이 어떻게 동작하는지 명확하게 알아야지 적합한 해결책을 찾는데 도움이 될 것이라는 생각이 들어 파고들었다.
WebSocketServer
는 websocket
을 관리하는 서버 객체이다. 초기화 과정은 다음과 같다.
- 매개변수로 전달받은 옵션을 체크
- 설정 port가 null이 아닐 때
http
라이브러리 서버를 생성한다.- 만약 port가 null이면 option으로 전달받은 서버로 진행
- 서버가 등록되면 이벤트 등록 작업을 진행
- 이때 upgrade 이벤트를 등록하여 WS 작업을 진행할 수 있도록 한다.
-
perMessageDeflate
옵션을 확인한다.- 해당 옵션은 클라이언트와 서버간 메시지를 압축하여 전달하기 때문에 네트워크 부하가 줄어들 수 있다는 장점이 있다.
- 하지만 메모리 낭비나 높은 CPU 사용률이 나타날 수 있는 단점이 있다.
- 이번에 진행하는 프로젝트는 메시지가 크지 않으므로 필요없는 옵션
-
clientTracking
옵션을 확인한다.-
clientTracking
은 웹소켓과 연결하는 클라이언트의 소켓을 추적하는지 설정하는 옵션으로 default는 true이다. - 이때 클라이언트는
Set
에 저장된다.
-
- 옵션과 상태값을 저장한다.
server에서 upgrade 이벤트가 발생한 후 req
와 socket
, head
를 전달받는다.
handleUpgrade
는 클라이언트 요청을 확인하여 검증 진행 및 Websocket
과 연관된 헤더를 추출하는 작업을 진행한다.
- 에러 이벤트가 발생하면 소켓의 연결을 끊도록 설정
- request로 부터
sec-websocket-key
, upgrade,sec-websocket-version
을 받는다.-
sec-websocket-key
- 핸드쉐이크 과정에서 연결하고자하는 클라이언트 -
upgrade
- HTTP/1.1 버전에 사용되는 헤더로 프로토콜을 변경하기 위한 헤더 -
version
- websocket 버전
-
- 요청 메서드를 확인한다.
GET
이 아니면 예외 메시지 전달 -
upgrade
헤더 체크 -
sec-websocket-key
체크 -
version
이 8 또는 13인지 확인 - 해당 요청의 경로가 설정된 경로가 맞는지 확인
-
sec-websocket-protocol
체크- 서브 프로토콜을 확인하여 저장한다.
-
sec-websocket-extensions
체크- 메시지 압축과 같은 여러가지 확장 옵션을 저장한다.
- 제한된 클라이언트 옵션을 확인
- 만약 검증 옵션이 있으면 주어진 조건으로 클라이언트를 필터링한다.
- 업그레이드 작업 완료
- 소켓의 연결이 끊겼는지 확인
- 소켓이 이미 업그레이드가 되었는지 서버 상태를 확인
-
sec-websocket-key
으로Sec-WebSocket-Accept
을 생성-
Sec-WebSocket-Accept
는 응답 서버가websocket
으로 업그레이드 할 의향이 있다는 의미를 나타낸다.
-
- 응답 값 초기 설정
- 101 상태 값을 설정
-
upgrade
,connection
,Sec-WebSocket-Accept
헤더를 설정
-
WebSocket
클래스 객체 생성 -
WebSocket
객체에 요청으로부터 전달된 서브 프로토콜과extentions
설정 - 소켓에 start line과 헤더를 write 이후 이전에 설정한 에러 리스너를 삭제
-
WebSocket
에 전달 받은 소켓을 주입, 이때 3가지 옵션도 설정-
allowSynchronousEvents
-message
,ping
,pong
이벤트를 한 틱에 여러 개를 emit 할 수 있는지에 대한 옵션, default는 false -
maxPayload
- 메시지 최대 크기, default는 104857600 -
skipUTF8Validation
- UTF-8 형식으로 올바르게 전달되었는지 확인하는 옵션, default는 false
-
- 설정된
WebSocket
을 서버에 저장한다.- 이때
WebSocket
의 연결이 끊기는 이벤트가 발생하면 자동으로 삭제하도록 설정
- 이때
해당 부분은 공식 문서에 설정하는 방법이 있으므로 생략한다.
우선 클라이언트로부터 메시지를 받으면 WebSocket
내부의 Receiver
에서 메시지 전처리 작업을 진행한다. 이때 이전에 WebSocket
에서 소켓을 설정할 때 전달 받은 옵션의 작업을 진행한다. Receiver
는 Duplex
라는 양방향 스트림이므로 tcp 소켓에 메시지를 전달하면 일정 길이만큼 버퍼를 전처리하는 작업을 계속 진행한 후 모든 메시지를 받게 된다면, WebSocket
이 message
라는 이벤트를 호출하여 프로그래머가 미리 설정한 리스너 이벤트를 진행하도록 한다.
WebSocket
내부의 Sender
를 사용한다. 먼저 WebSocket
이 메시지를 전달할 수 있는 상태인지 확인한 후 다음의 작업을 진행한다.
- 데이터가 숫자인지 확인 후 문자로 변환
- 웹 소켓이 열려있지 않으면
sendAfterClose
진행 - 압축 옵션을 확인 후 압축이 설정되면 압축 진행
- sender에 전달
이후 sender
가 클라이언트 소켓에 메시지를 전달한다.
close 이벤트가 발생하여 연결이 끊기고 클라이언트를 저장한 Set에 소켓을 삭제하도록한다.
Websocket
라이브러리를 파고들면서 해당 라이브러리는 정말 기본적인 옵션을 제공한다는 것을 느꼈다. 이렇게 생각한 이유는 프로토콜에서 명시한 헤더 값으로 소켓을 설정하는 작업과 메시지 전달을 주고받는 작업 이외에 어떤 기능도 제공하지 않기 때문이다. 클라이언트로부터 연결이 끊기면 연결을 재시도 하지 않고, 클라이언트 소켓을 저장하지만 채팅 룸에 대한 기능이 없다. 따라서 WebSocket
은 node net
과 같이 웹 소켓에 대한 기본적인 기능만 제공하고, Socket.io
는 nestjs
와 같이 다양한 기능이 제공되는 라이브러리라고 생각하게 되었다.
- 🚩 FE 기술 선택이유
- ✨ 차트의 반응형 구현과 useRef 타입 문제
- 🐣 부모 요소의 상태에 따라 자식 요소도 스타일 변화 부여하기
- 📁 zod 도입하기
- 🔖 useInfiniteQuery를 사용한 그래프 무한스크롤 구현
- 🎫 사용자의 시점 변화 없는 그래프 스크롤 구현하기
- 🧪 수많은 그래프 데이터 요청을 어떻게 줄일까
- 🌚 다크모드에서 새로고침 시 라이트모드가 잠깐 보이는 문제
- 👊 웹소켓의 채팅 데이터와 REST API의 채팅 데이터를 함께 관리하기
- 📡 BE 기술 선택 이유
- ⛏️ Node WebSocket 파고들기
- ✏️ TypeORM Datasource mock 만들기
- ☁️ oauth ID range 문제
- 📖 custom pipe에서 Nan이 받아지는 문제
- 🪒 nest Websocket에 세션이 안된다고?
- 🏴 nginx websocket 연결 시 문제 발생
- 🆘 WebPush 구현
- 🧊 우선순위 큐로 요청 제어하기
- 🔌 websocket이 늦게 할당되어 발생되는 문제
- 🥳 typeorm을 이용한 FCM 알림 서비스
- 🚦 다중 유저 동시성 제어 ‐ 싱글톤, 뮤텍스
- 🍙 그래프 데이터를 실시간으로 제공하기위한 전략