시작 하며...
이번에 진행중인 프로젝트의 웹소켓 관련 부하테스트를 진행하며 웹소켓 연결을 직접 진행해야 했고, 이 과정에서 내가 웹소켓, socket.io 에 대한 개념을 혼동하고 있었다는 것을 발견했다. 둘다 그냥 웹소켓 프로토콜을 총칭하는 말인줄 알고 있었는데, 둘이 아예 다른 역할을 가진 용어임을 알게되어 각 개념을 정리하고자 포스팅을 쓰게 되었다.
Websocket
TCP 접속에 이중 통신 채널을 제공하는 컴퓨터 통신 프로토콜. (RFC 6455)
기존의 전통적인 방식에서 양방향 통신을 구현하기 위해서는 HTTP 프로토콜을 남발해야만 했다.
기존 HTTP/1.1 이하의 버전들은 클라이언트의 요청에 대한 응답(단방향 통신)만이 가능했고, 이는 다양한 기능들(채팅, 게임 서비스 등...)을 매우 비효율적으로 개발하게 만드는 한계점이었다.
즉, 이러한 한계점들을 해결하고자 하는 방법이 바로 양방향 통신을 가능케하는 Websocket 프로토콜인 것이다.
Websocket 연결 (Handshake Opening)
다른 통신 프로토콜들과 유사하게, Websocket 프로토콜로 통신을 하기위해서는 먼저 Handshake 과정을 거쳐야 한다.
1. HTTP Upgrade 요청 전송 (Client)
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
클라이언트는 위처럼 생긴 HTTP 요청을 서버로 보낸다.
- Upgrade : HTTP 연결을 다른 프로토콜(HTTP2.0 혹은 Websocket)으로 변경할 것임을 알리는 헤더.
- Connection : 현재 HTTP 전송이 완전히 끝나고 나서 네트워크 연결을 유지 할지 말지를 결정하는 헤더. Upgrade 값을 사용해 프로토콜을 업그레이드 할 것임을 알린다.
- Sec-WebSocket-Key : 클라이언트가 랜덤하게 생성한 Base64로 인코딩된 키. 서버는 이 키를 사용해서 새로운 키를 생성하고, 서로는 이 값을 검증하며 요청이 유효한지 확인한다. (서버가 신뢰할 수 있는 곳인지 확인한다.)
- Sec-WebSocket-Protocol : 클라이언트가 사용가능한 하위 프로토콜들을 나열한다. 서버는 이중에 하나를 선택하여 응답을 하여 "그 프로토콜이 선택되었음" 이라고 알려줄 수 있다.
2. HTTP 101 Switching Protocols 응답
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
- Upgrade : HTTP 연결을 다른 프로토콜(HTTP2.0 혹은 Websocket)으로 변경할 것임을 알리는 헤더.
- Connection : 서버가 Websocket 프로토콜로의 전환을 수락했음을 명시함.
- Sec-WebSocket-Accept : 클라이언트가 전달한 Sec-Websocket-Key를 사용해 만들어낸 응답 키. Handshake 과정이 정상적으로 이루어졌음을 보장한다.
이 handshake는 HTTP를 사용해 이루어진다. 최초에 HTTP를 통해서 handshake가 성공적으로 이루어지고 나면, 그 이후 부터는 더이상 HTTP 프로토콜이 아닌 Websocket 프로토콜을 사용해 소통을 한다. (http or https -> ws or wss)
Websocket 통신
이렇게 정상적으로 웹소켓 커넥션이 성사되고 나면, 이제부터 클라이언트와 서버는 양방향 통신이 가능하다.
웹소켓 프로토콜은 HTTP 통신에 사용되는 헤더보다 작은 크기의 헤더를 사용하고, 오버헤드가 적어 보다 효율적인 통신이 가능하다.
기본적으로 Websocket은 기존에 진행하던 HTTP 혹은 HTTPS 통신과 동일한 포트(주로 80번 이나 443번)을 그대로 공유한다. 웹소켓을 위한 전용 포트를 별도로 관리할 수 있긴하나, 보통은 그렇게 한다. (연결 정책이 복잡해지면 분리하는것을 권장한다.)
웹소켓이 열리면, 위와 같은 메시지(프레임)을 주고 받을 수 있다. 프레임은 데이터의 크기에 따라 형태도 조금씩 달라진다.
프레임에 포함된 다양한 필드들은 데이터의 구분, 메시지의 제어, 마스킹 등 다양한 역할을 수행하여 데이터의 무결성을 유지한다.
Websocket 연결 종료 (Handshake Closing)
웹소켓 연결을 Handshake를 통해 시작했다면, 닫을 때도 Handshake가 필요하다.
Handshake의 종료는 시작보다 훨씬 간단하다.
서버나 클라이언트 둘중 연결을 종료하고 싶은 쪽에서 연결 종료를 의미하는 특정 데이터 순서쌍을 포함한 프레임(FIN)을 보낸다. 이를 받은 쪽에서는 연결 종료 요청을 이해했다는 Close frame(ACK)을 전달한다.
이렇게 한번 Close Frame을 수신하면, 더이상 데이터 전송을 하지 않는다. 만약 남아있던 버퍼데이터나 아직 보내지 못한 데이터가 있다면 버리거나 별도로 처리하여 보관한다. 이후 별도로 데이터가 수신되더라도 무시한다.
Socket.io
클라이언트 - 서버 간 낮은 지연율 기반 양방향 통신이 가능하도록 해주는 이벤트기반 라이브러리
socket.io는 양방향 통신을 구현하기 위해 다양한 기술들을 활용할 수 있도록 도와주는 라이브러리. 즉, 도구이다.
Socket.io 는 다음과 같은 방식들 중 하나를 선택해 양방향 통신을 구현한다.
- HTTP 기반 long-polling
- Websocket
- WebTransport
Socket.io는 브라우저의 호환성, 네트워크 상황등을 보고 자동으로 위 세가지 방법 중 최선의 선택지를 골라 양방향 통신을 진행한다.
여기서 중요한 점은, Socket.io는 Websocket 기술의 구현체가 아니다.
Websocket 을 양방향 통신을 구현하기 위한 방법중 하나로 사용할 뿐이지, Websocket의 순수 구현체가 아니라는 말이다.
Socket.io의 기능들
- 자동 전송 방식 선택
현시대와 달리, 옛날 브라우저들은 Websocket 프로토콜을 지원하지 않는 경우가 꽤 있었다. 그럼 양방향 통신을 구현하기 위해 전통적인 방식인 long-polling으로 돌아가야 하는데, 바로 이때 Socket.io가 빛을 발휘한다.
개발자는 양방향 통신을 처리하기 위한 구현체에는 관심이 없고, Socket.io를 사용해 비즈니스 로직만 구현해두면 쉽게 양방향 애플리케이션을 개발 할 수 있는것이다.
- 이벤트 기반 통신
개발자는 이벤트 기반 개발방식을 통해 직관적으로 이벤트를 정의하고, 이에 대한 핸들러를 구현하여 복잡한 상호작용을 쉽게 구현할 수 있다.
- 방 및 네임스페이스 관리
클라이언트의 그룹화를 통해 논리적으로 연결을 나누어 관리할 수 있도록 기능을 제공한다. 이는 대규모 실시간 애플리케이션이 여러명의 사용자가 다양한 곳에 나뉘어 관리되는 상황을 쉽게 구현할 수 있도록 도와준다. (채팅방, 게임 매치 등...)
결론
- Websocket : 실시간 양방향 통신을 효율적으로 하기 위해 탄생한 전통적인 웹 표준 프로토콜
- Socket.io : Websocket 등을 포함하여 다양한 전송 방식을 지원하는 라이브러리
결국, Socket.io는 Websocket 보다 상위 개념의 고수준 라이브러리이다.
Socket.io는 필요한 기능을 구현하는데 용이한 Broadcasting 기능 등 다양한 기능들을 제공한다. 이러한 기능들을 활용하여 사용자들을 쉽게 관리할 수 있다. (방 개념, 소켓 실패 시나리오 관리 등...)
하지만 socket.io는 그만큼 날 것의 Websocket에 비해 무겁다. 복잡한 내용이 들어가있지 않은 정말 단순한 수준의 데이터 양방향 교환이 중요한 경우 (거래소 실시간 데이터 제공 등...) Websocket을 직접 활용하여 불필요한 데이터 없이 빠른 통신을 구현할 수 있을 것이다.
'Computer Science' 카테고리의 다른 글
Typescript 데코레이터 알아보기 (feat. 클로저) (0) | 2025.03.14 |
---|---|
자바스크립트의 구조와 실행 방식 (Ignition, TurboFan, EventLoop) (1) | 2024.09.23 |