본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

REST와 WebSocket의 경계

~10 min · streaming-async, websocket, rest-vs-ws

Level 0HTTP 입문자
0 XP0/46 lessons0/12 achievements
0/120 XP to next level120 XP to go0% complete
"WebSocket은 REST를 대체하는 상위 버전이 아니야. 양쪽이 하나의 연결에서 서로 독립적으로 메시지를 보내야 한다는 요구에 답하는 별도의 도구지. 요청과 응답, 단방향 스트림, 전이중 채널의 경계를 알면 필요 이상의 복잡성도 부족한 설계도 피할 수 있어."

요청과 응답에서 전이중 연결까지

통신 패턴을 단순한 쪽부터 나열하면 다음과 같아:

  1. REST 요청과 응답 — 클라이언트가 요청하고 서버가 응답하면 교환이 끝나. 리소스 생성·조회·수정·삭제와 일회성 명령에 잘 맞아.
  2. 고정 주기 폴링 — 같은 조회를 일정 간격으로 반복해. 몇 초의 지연을 허용할 수 있고 단순성이 중요할 때 유용해.
  3. 롱 폴링 — 서버가 이벤트나 시간 만료까지 응답을 보류해. 낮은 알림 지연을 얻는 대신 오래 대기하는 요청을 관리해야 해.
  4. SSE — HTTP 응답 하나에 서버 이벤트를 계속 기록하는 단방향 스트림이야. AI 응답, 알림, 실시간 로그에 잘 맞아.
  5. WebSocket — 하나의 장기 연결에서 양쪽이 서로 기다리지 않고 메시지를 보낼 수 있는 전이중 채널이야.

뒤로 갈수록 무조건 더 좋은 것이 아니라 연결 상태와 복구, 용량 계획 같은 운영 책임이 늘어나. 필요한 통신 모양을 만족하는 가장 단순한 방식을 고르는 게 핵심이야.

WebSocket을 검토할 판단 기준

1. 트래픽 방향. 클라이언트가 보통의 HTTP 요청과 별개로 서버에 자주 실시간 메시지를 보내야 하는지 살펴봐. 입력 중 표시, 멀티플레이어 입력, 협업 커서 위치는 양방향 채널의 이점이 커. 반면 사용자가 요청 하나를 보내고 서버 응답만 길게 받는 AI 채팅은 일반 POST와 SSE 조합으로 충분할 수 있어.

2. 지연과 전달 방식. 특정 숫자 하나로 프로토콜이 결정되지는 않아. SSE도 서버에서 클라이언트로 낮은 지연의 이벤트를 보낼 수 있어. 양쪽이 독립적으로 빠르게 반응해야 하고, 매번 새 HTTP 요청을 만드는 왕복과 헤더 비용이 실제 병목이 되는지를 측정해야 해.

3. 메시지 빈도와 연결 상태. 클라이언트가 초당 여러 번 작은 상태 변화를 보내는 게임이나 공동 편집은 WebSocket에 잘 맞아. 드문 알림이나 상태 변경은 SSE나 폴링으로 더 단순하게 처리할 수 있어. 순서, 재연결 후 복구, 느린 소비자에 대한 정책도 함께 설계해야 해.

세 기준은 체크박스 하나로 결론 내리는 시험이 아니야. 방향과 빈도, 요구되는 전달 의미, 프록시 지원, 운영 인력을 함께 따져 WebSocket의 이점이 연결 관리 비용보다 큰지 판단해야 해.

고전적인 HTTP/1.1 WebSocket 핸드셰이크

널리 쓰이는 HTTP/1.1 방식에서는 클라이언트가 업그레이드 헤더를 담은 요청을 보내고, 서버가 이를 수락하면 101 Switching Protocols로 응답해. 그 뒤 같은 TCP 연결에서 HTTP 메시지 대신 WebSocket 프레임을 주고받아:

GET /ws/chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

101 응답 뒤에는 양쪽이 작은 이진 헤더와 페이로드로 이루어진 WebSocket 프레임을 교환해. 한쪽이 닫기 핸드셰이크를 시작하거나 네트워크가 끊길 때까지 연결이 유지돼. HTTP/2에서는 확장 CONNECT를 사용하는 별도 부트스트랩 방식도 있으므로 모든 환경이 101 응답만 쓰는 것은 아니야.

WebSocket은 전이중 통신을 얻는 대신 연결 수명 주기와 애플리케이션 전달 상태를 직접 관리하는 선택이야. 인증 갱신, 하트비트, 재연결, 누락 데이터 복구, 느린 소비자, 연결별 메모리와 관측을 설계해야 해. 양쪽이 독립적으로 자주 보내야 한다는 요구가 없다면 REST나 SSE가 더 작은 운영 표면을 제공해.

상태 API와 실시간 채널을 함께 쓰기

많은 서비스는 REST와 WebSocket을 역할별로 조합해. 로그인, 기록 조회, 초안 저장처럼 재조회 가능한 상태와 명령은 REST 리소스로 다루고, 채팅 이벤트, 공동 편집, 접속 상태처럼 즉시 전파해야 하는 변화는 WebSocket으로 전달해. 영구 상태를 실시간 연결에만 가두지 않으면 재연결한 클라이언트가 REST로 기준 상태를 다시 가져오고 이후 이벤트를 이어 받을 수 있어.

cwkPippa의 혼합 구성

cwkPippa는 대화 생성·조회·이름 변경·삭제 같은 리소스 작업에 REST를 사용하고, /api/chat 응답의 AI 토큰은 SSE 형식으로 스트리밍해. Cinder Sidekick 채널은 Photoshop UXP 플러그인과 cwkPippa 백엔드가 캔버스 상태와 제어 메시지를 서로 독립적으로 보내야 하므로 WebSocket을 사용해. 서버에서 Cinder로 한 방향의 갱신만 필요했다면 SSE를 검토할 수 있지만, 현재 요구 사항은 실제 양방향 채널이야.

Code

FastAPI WebSocket — 양방향 수신과 연결 전체 브로드캐스트·python
# FastAPI — WebSocket endpoint (양방향)
from fastapi import FastAPI, WebSocket, WebSocketDisconnect

app = FastAPI()
_connections: set[WebSocket] = set()

@app.websocket('/ws/chat')
async def chat_socket(ws: WebSocket):
    await ws.accept()
    _connections.add(ws)
    try:
        while True:
            # Client 에서 메시지 받음 (언제든 가능)
            msg = await ws.receive_json()
            # 모든 연결된 client 한테 echo (broadcast)
            for conn in _connections:
                await conn.send_json({'from': msg.get('from'), 'text': msg.get('text')})
    except WebSocketDisconnect:
        _connections.discard(ws)
브라우저 WebSocket — 독립적인 전이중 송수신·javascript
// 브라우저 client — WebSocket API (열기에 한 줄)
const ws = new WebSocket('ws://localhost:8000/ws/chat');

ws.onopen = () => console.log('연결됨');
ws.onmessage = (e) => {
  const data = JSON.parse(e.data);
  appendToChatUi(data);
};
ws.onclose = () => console.log('연결 끊김 — 재연결 로직 구현');

// 언제든 메시지 보냄, request/response 사이클 안 묶임
document.getElementById('send').onclick = () => {
  ws.send(JSON.stringify({ from: 'me', text: '안녕 모두' }));
};
판단표 — REST, SSE, WebSocket, 폴링·text
# 결정 매트릭스 — 맞는 도구 선택

| 필요                                              | 패턴                |
|---------------------------------------------------|---------------------|
| Fetch / 저장 / list — request/response            | REST                |
| Server 가 event push; client 가 push 안 함        | SSE                 |
| 지연 tolerant (초-OK); server 에서 push           | Polling / long-poll |
| 양 측이 언제든 메시지 push; 저-지연                | WebSocket           |
| 멀티플레이어 게임, live editor, 트레이딩          | WebSocket           |
| Streaming response 가진 AI 채팅                   | REST POST + SSE     |
| 분 당 몇 알림                                      | SSE (혹은 long-poll)|
| Live 커서 추적                                     | WebSocket           |
| 진행 event 가진 파일 download                      | 진행에 SSE          |
| One-shot upload                                    | REST POST           |

External links

Exercise

채팅, 알림, 멀티플레이어 카운터, 실시간 대시보드 가운데 기능 하나를 골라 통신 요구를 적어 봐. (1) 양쪽이 같은 채널에서 독립적으로 보내야 하는가, (2) 허용 가능한 지연은 얼마인가, (3) 메시지 빈도와 크기는 어느 정도인가, (4) 재연결 뒤 어떤 상태를 복구해야 하는가를 정리해. 그런 다음 REST, SSE, WebSocket 가운데 하나 또는 조합을 선택하고 두 문장으로 근거를 설명해. 보너스로 두 방식을 구현해 연결 관리와 복구 절차를 비교해.
Hint
‘실시간’이라는 이름만으로 WebSocket을 고르지 말고 실제 트래픽 방향부터 확인해. 서버에서 클라이언트로만 자주 보내면 SSE가 충분할 수 있고, 양쪽의 독립적인 고빈도 송신이 핵심이면 WebSocket의 이점이 커져. 두 구현을 비교할 때는 최초 연결뿐 아니라 재연결, 메시지 순서, 누락 상태 복구, 느린 소비자 처리까지 기록해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고
💛 by 피파warm

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.