에듀테크 프로젝트 대시보드
웹소켓 (WebSocket)
HTTP는 매번 문을 두드려야 답을 준다 — 웹소켓은 문을 한 번만 열어두고 양쪽 다 아무 때나 말을 건다
채팅, 실시간 알림, 주식 시세, 온라인 게임처럼 서버가 먼저 클라이언트에게 데이터를 보내야 하는 서비스가 있다. 하지만 기본 HTTP는 클라이언트가 요청해야만 서버가 응답하는 구조라, 서버 쪽에서 스스로 먼저 말을 걸 수 없다.
가장 단순한 해결책은 폴링(Polling) — "새 소식 있어요?"를 몇 초마다 계속 물어보는 것이다. 대부분은 "없어요"라는 빈 대답만 돌아온다. 웹소켓은 연결을 한 번 맺어두고, 그 안에서 서버든 클라이언트든 할 말이 생기면 즉시 상대에게 밀어넣는(push) 방식이다.
양쪽 다 같은 속도로 "새 메시지"가 서버에 무작위로 생긴다고 가정한다. 왼쪽은 1초마다 클라이언트가 직접 물어보고, 오른쪽은 연결을 한 번 맺어두고 서버가 생기는 즉시 밀어준다. 10초 실행을 눌러 요청 수와 낭비를 비교해보자.
웹소켓도 처음엔 평범한 HTTP 요청으로 시작한다. 다만 "이 연결을 웹소켓으로 업그레이드해달라"는 헤더를 붙인다.
서버가 동의하면 101 Switching Protocols 로 응답하고, 그 순간부터 같은 TCP 연결이
HTTP가 아닌 웹소켓 프레임을 주고받는 통로로 바뀐다.
# 클라이언트 → 서버 (여전히 HTTP 요청처럼 보임) GET /ws/chat HTTP/1.1 Host: offset0xf.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13 # 서버 → 클라이언트 (동의하면 101로 응답, 이후 계속 열려 있음) HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Key에
고정 문자열 258EAFA5-E914-47DA-95CA-C5AB0DC85B11을 붙여 SHA-1 해시한 뒤 Base64로 인코딩한다.
아무나 만든 값이 아니라는 걸 증명해, HTTP 프록시가 실수로 업그레이드를 흉내내는 걸 막기 위한 장치다.
| Opcode | 의미 | 비고 |
|---|---|---|
| 0x1 | 텍스트 프레임 | 보통 JSON 문자열 |
| 0x2 | 바이너리 프레임 | 이미지·파일 조각 등 |
| 0x8 | 연결 종료(Close) | 양쪽 다 보낼 수 있음 |
| 0x9 / 0xA | Ping / Pong | 연결이 살아있는지 주기적으로 확인 |
클라이언트가 보내는 프레임은 마스킹(XOR)이 의무다. 캐시 프록시가 "이건 그냥 이미지 데이터야"라고 오해해 내용을 캐싱하거나 변형하는 걸 막기 위한 보안 장치다.
아래 두 창은 같은 웹소켓 연결을 공유하는 두 사용자라고 가정한다. 한쪽에서 메시지를 보내면 폴링과 달리 새로고침이나 재요청 없이 즉시 반대편에 나타난다.
from fastapi import FastAPI, WebSocket app = FastAPI() connections: list[WebSocket] = [] @app.websocket("/ws/chat") async def chat(ws: WebSocket): await ws.accept() # 101 응답 + 연결 유지 시작 connections.append(ws) try: while True: msg = await ws.receive_text() # 도착할 때까지 대기(블로킹 아님) for conn in connections: await conn.send_text(msg) # 접속자 전원에게 즉시 push except Exception: connections.remove(ws) # 연결 끊기면 목록에서 제거
| 상황 | 추천 | 이유 |
|---|---|---|
| 일반 API (목록 조회 등) | 일반 HTTP | 요청-응답 1회성이면 굳이 연결을 유지할 필요 없음 |
| 몇 분에 한 번 갱신되면 충분 | 폴링 / 롱폴링 | 구현이 단순하고 기존 인프라(캐시·LB) 그대로 활용 |
| 채팅, 실시간 알림, 게임, 시세 | 웹소켓 | 지연 없이 즉시 push, 서버→클라이언트 방향도 가능 |
HTTP는 "물어봐야 답한다", 웹소켓은 "연결을 열어두고 아무 때나 밀어넣는다".
한 번의 Upgrade 핸드셰이크로 같은 TCP 연결을 계속 재사용해,
폴링이 반복하던 헛수고(빈 응답, 매번 새 요청)를 없애는 것이 핵심이다.