이전 편에서 우리는 Polling, Long Polling, SSE 같은 방식들을 살펴보았다.
위의 방법들은 HTTP 기반 통신이여서 기본적으로 요청-응답 구조형태로 서버가 먼저 클라이언트에게 말을 거는 것이 불가능했다. 그래서 위와 같은 방법으로 실시간처럼 보이게 만들었다. 이번 편에서는 Websocket을 사용하여 지속적인 연결을 유지하면서, 서버와 클라이언트가 양방향으로 메시지를 주고받을 수 있는 통신 채널을 알아보자.
웹소켓(WebSocket)은 하나의 TCP(Transmission Control Protocol) 연결 위에서 동시에 양방향 통신을 할 수 있게 해주는 프로토콜이다.
2011년 RFC 6455로 표준화되었다. 웹소켓 프로토콜은 기존의 HTTP의 요청(request),응답(response) 모델이 가진 근본적인 한계를 극복하기 위해 설계되었다. 즉 웹소켓 프로토콜이 양방향성을 해결 한 것이다.😱
HTTP와 웹소켓의 차이를 간략하게 알아보자.
친구와 내가 의사소통을 한다고 가정해보자.
먼저 HTTP를 사용하는 경우를 생각해보면, 마치 우편으로 소통하는 것과 비슷하다.
내가 편지를 써서 보내면 우체부가 배달하고, 친구의 답장이 올 때까지 기다려야 한다. 만약 새로운 얘기를 하고 싶다면, 또 다른 편지를 써서 보내야 한다. 우체부도 편지를 전달만하고 답장을 기다려 주지 않는다. 결국 클라이언트가 먼저 요청하지 않으면 서버는 응답할 수 없다.
이번에는 Websocket을 사용하는 경우를 생각해보자. 웹소켓을 사용한다면 친구와 전화통화를 하는 것과 비슷하다. 친구와 전화를 한 번 연결하면, 전화가 연결된 동안 서로 자유롭게 대화를 주고 받을 수 있다. 내가 먼저 말하지 않아도 친구가 "야 뭐해??"라고 물어볼수도 있다. 즉 웹소켓을 사용하면 연결이 유지되는 동안 양방향 통신이 가능하다.
웹소켓과 HTTP는 사용하는 주소(URL)가 다르다.
WebSocket
HTTP
여기서 중요한 포인트가 있다. 웹소켓과 HTTP가 똑같은 포트 80(HHTP), 443(HTTPS)를 사용한다. "어? 그럼 같은 포트를 사용하는데 어떻게 구분할까?"라는 의문이 들었다. 여기서 포트는 어느 문으로 들어갈지 정하는 것이고, 프로토콜은 들어가서 어떻게 대화할지를 정하는 것이다. 같은 101호 집으로 들어가도 내부에서 전화 통화를 할 수도 있고 편지를 주고 받을 수 있는 것과 비슷하다. 즉 웹소켓과 HTTP의 포트는 같지만 프로토콜은 완전히 다르다.
HTTP는 요청에 대한 응답을 받고 끝내버리는 무상태(Stateless) 프로토콜이지만, 웹소켓은 한 번 연결되면 지속적인 상태를 유지하는 (Persistent Connection) 프로토콜이다.
웹소켓 연결은 처음에는 HTTP 요청으로 시작한다. 그렇다면 HTTP에서 WebSocket으로 변경되는 것일까? 맞다.
1단계 : 평범한(?) HTTP인척 요청하기
아래는 클라이언트가 HTTP 요청을 보낸 헤더 값이다.
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: 16byte nonce, base64 encoded
Sec-WebSocket-Version: 13
하지만 여기에 특별한 헤더들이 숨어있다.
웹 소켓 요청에는 아래와 같은 규칙이 있다.
2단계 : 서버가 동의하여 웹소켓 업그레이드
아래는 서버가 HTTP 응답을 보낸 헤더값이다.
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: 20-byte ND5 hash in base64
서버가 "101 Switching Protocols"라는 특별한 상태 코드로 답한다. 이건 "알겠어, 이제부터 HTTP 아니고 WebSocket이야!"라는 뜻이다. Sec-WebSocket-Accept는 요청에서의 Key값을 계산한 값으로 신원 인증에 필요한 헤더이다.
이 과정을 거치면, 이제부터는 HTTP의 요청-응답 패턴이 아니라 서로 자유롭게 메시지를 주고받을 수 있따. 클라이언트가 먼저 말하지 않아도 서버가 먼저 메시지를 보낼 수 있으며 동시에 양방향으로 데이터가 전송될 수 있다.
"처음부터 웹소켓으로 연결하면 안 되나?"라고 생각할 수 있다. 하지만 아래와 같은 이유들 때문에 HTTP에서 시작해서 웹소켓으로 업그레이드 하는 방식을 채택한다.
마치 기존 전화선을 이용해서 인터넷을 연결하는 것과 비슷하다. 새로운 선을 깔 필요 없이 기존 인프라를 활용하기 위해 HTTP로 시작해서 웹소켓으로 업그레이드하는 방식을 사용한다.
http와 https 차이처럼 ws와 wss는 보안 방식 차이이다.
ws://는 평문으로 통신
wss://는 암호화 통신
웹소켓에서 데이터 교환은 HTTP와는 전혀 다른 방식으로 동작한다.
HTTP vs Websocket
먼저 HTTP가 어떤 방식으로 데이터를 교환하는지 간략하게 알아보자.
HTTP는 메시지 하나 보내기 위해서는 아래와 같은 무거운 헤더와 함께 같이 보내야한다.
POST /api/chat HTTP/1.1
Host: chat.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)...
Accept: application/json
Content-Type: application/json
Content-Length: 45
Cookie: session=abc123; user_id=456
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
반면 Websocket 프레임은 필요한 데이터만 담아서 헤더와 함께 보낸다.
[FIN=1][Opcode=1][MASK=1][Length=15] + "안녕하세요!"
1 4 1 7 + 실제 메시지
= 총 약 20바이트 정도
헤더의 구조를 살펴보면 아래와 같다.
주요 필드
FIN : 전체 메시지의 끝인지 표시 (1은 끝을 나타내고, 0은 더 있음을 나타낸다.)
Opcode : 데이터 타입을 지정
- 0x1 = 텍스트 ("안녕하세요!")
- 0x2 = 바이너리 (이미지, 파일)
- 0x8 = 연결 종료
- 0x9/0xA = Ping/Pong (생존 확인)
MASK: 암호화 여부 (클라이언트→서버는 필수)
Length: 데이터 크기 (0-125는 직접, 그 이상은 확장 필드 사용)
RSV1,2,3 : 프로토콜별로 사용되거나 사용하지 않는다.
이러한 방식으로 웹소켓은 가볍고 효율적인 프레임구조로 실시간 양방향 통신을 가능하게한다.
WebSocket의 특징들을 정리해보면 다음과 같다.
HTTP 기반 시작, WebSocket으로 전환된다.
기존 인프라 활용한다.
프레임 기반 통신이다.
단순한 데이터 타입을 정의한다.
이번 편에서는 WebSocket이 무엇인지, HTTP와 어떻게 다른지, 그리고 어떤 방식으로 동작하는지 알아봤다.
웹소켓의 구조와 동작 방식을 이해했으니 다음 편에서는 실제 웹소켓을 사용해서 채팅 앱을 만들어보면서 어떤식으로 사용해야하는지 알아 볼 예정이다.
https://velog.io/@codingbotpark/Web-Socket-%EC%9D%B4%EB%9E%80