백엔드 면접 용어 사전
API·REST웹소켓 · RFC 6455

WebSocket

HTTP Upgrade와 101 응답으로 프로토콜을 바꿔 양방향 프레임을 주고받는 통신 방식. 재연결은 직접 구현해야 한다.

하나의 TCP 연결에서 양방향으로 메시지를 주고받는 프로토콜(RFC 6455). HTTP의 "클라이언트가 먼저 묻는다"는 전제를 정면으로 깬다.

시작은 평범한 HTTP다

GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==     ← 무작위 16바이트를 base64로
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols                 ← 200이 아니다
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

101은 "지금부터 이 TCP 연결에서 HTTP를 쓰지 않겠다" 는 뜻이다. 이 응답 이후로는 요청도 응답도 없고, 양쪽이 대등하게 프레임을 주고받는다.

이 설계의 실용적 이점은 기존 인프라를 그대로 쓴다는 것이다 — 80·443 포트, 방화벽, 프록시가 처음에는 평범한 HTTP 요청으로 보기 때문에 별도 포트를 열 필요가 없다.

Sec-WebSocket-Accept는 보안 장치가 아니다

서버는 클라이언트가 보낸 키에 고정된 문자열 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 을 이어 붙이고 SHA-1로 해시한 뒤 base64로 인코딩해 돌려준다.

SHA-1(클라이언트키 + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11") → base64

그 문자열이 규격에 공개돼 있으니 아무나 계산할 수 있다. 목적은 인증이 아니라 확인이다 — 중간의 캐시나 프록시가 이 요청을 평범한 HTTP로 착각해 엉뚱한 응답을 돌려주는 사고를 걸러 낸다.

프레임 구조 — 헤더가 2바이트다

필드
FIN(1비트)메시지의 마지막 조각인가(긴 메시지는 쪼개 보낸다)
opcode(4비트)0x1 텍스트 · 0x2 바이너리 · 0x8 종료 · 0x9 ping · 0xA pong
MASK(1비트)페이로드가 마스킹돼 있는가
길이(7 / 7+16 / 7+64비트)0~125는 그대로, 126이면 뒤 16비트, 127이면 뒤 64비트를 길이로

최소 헤더가 2바이트다. HTTP 요청마다 수백 바이트씩 붙던 헤더와 비교하면, 짧은 메시지를 자주 주고받을 때 차이가 크다.

마스킹 — 이것도 암호화가 아니다

클라이언트가 보내는 프레임은 반드시 마스킹해야 한다(4바이트 키로 페이로드를 XOR). 키가 프레임 안에 그대로 들어 있으니 비밀은 없다. 목적은 공격자가 회선에 나타날 바이트를 마음대로 고르지 못하게 하는 것 — WebSocket을 모르는 중간 프록시가 그 바이트열을 HTTP 요청으로 잘못 읽어 캐시를 오염시키는 공격을 막는다.

실무에서 반드시 걸리는 세 가지

① 유휴 연결이 끊긴다. 로드밸런서·프록시는 대개 유휴 연결을 수 분 뒤 닫는다. ping/pong 제어 프레임을 그 타임아웃보다 짧은 주기로 보내야 조용한 연결이 살아남는다.

② 스케일아웃이 간단하지 않다. 연결이 특정 인스턴스에 묶이므로(stateful), 다른 인스턴스에 붙은 사용자에게 보내려면 Redis Pub/Sub 같은 브로커가 필요하다.

③ 인증에 헤더를 못 쓴다. 브라우저 WebSocket API는 커스텀 헤더를 붙일 수 없어 Authorization을 그대로 못 쓴다. 쿠키·일회용 티켓·연결 직후 첫 메시지 인증 중 하나를 쓴다. 또 동일 출처 정책이 적용되지 않으므로 서버가 Origin을 직접 검증해야 한다(Cross-Site WebSocket Hijacking).

HTTP/2·HTTP/3 위에서는

위 핸드셰이크는 HTTP/1.1의 Upgrade에 기대는데 HTTP/2에는 그 개념이 없다. RFC 8441이 확장 CONNECT로 HTTP/2 위 WebSocket을 정의했고 RFC 9220이 HTTP/3까지 넓혔지만, 2026년 기준 HTTP/3 위 프로덕션 구현은 주요 브라우저·서버에 아직 없다.

면접 함정

  • "실시간이면 무조건 WebSocket" → 단방향이면 SSE가 더 단순하고 재연결이 규격에 있다.
  • "Sec-WebSocket-Accept가 보안을 담당" → 고정 문자열 기반의 확인 절차다.

클라이언트 코드

const ws = new WebSocket("wss://api.example.com/chat");

ws.onopen    = () => ws.send(JSON.stringify({ type: "auth", token }));
ws.onmessage = (e) => render(JSON.parse(e.data));
ws.onclose   = () => setTimeout(connect, backoff());   // 재연결은 직접 만든다

onclose 줄이 핵심이다 — 재연결·지수 백오프·놓친 메시지 복구를 전부 손으로 써야 한다. SSE라면 이 줄이 필요 없다.

헤더를 못 붙인다는 제약

// ❌ 브라우저 WebSocket API 에는 헤더 옵션이 없다
new WebSocket(url, { headers: { Authorization: "Bearer ..." } });

// ✅ 쿠키(같은 도메인) · 짧은 수명의 일회용 티켓 · 연결 후 첫 메시지 인증
new WebSocket(`wss://api.example.com/chat?ticket=${oneTimeTicket}`);

티켓은 한 번 쓰면 폐기되고 수명이 수십 초여야 한다. 장기 토큰을 쿼리 파라미터에 넣으면 액세스 로그·리퍼러에 그대로 남는다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — API·REST 설계 — 멱등성·상태코드·버저닝·GraphQL