본문으로 건너뛰기
김신건의 로그

Stateless vs Stateful

· 수정 · 📖 약 3분 · 1,349자/단어 #architecture #network #scaling
Stateless vs Stateful, stateless-vs-stateful, 무상태와 상태유지

정의

Stateless (무상태): 서버가 클라이언트 요청 간 상태를 기억하지 않음. 각 요청은 완전히 독립적이며 필요한 모든 정보는 요청에 담겨 있다.

Stateful (상태유지): 서버가 클라이언트와의 세션 정보를 메모리에 유지. 후속 요청은 이 컨텍스트를 활용해 처리된다.

위 애니메이션이 같은 인프라(Client + LB + 3 서버)에서 두 모드가 어떻게 다르게 동작하는지, 그리고 장애 시 어떤 차이가 나는지를 한눈에 보여준다.

기본 비교

측면StatelessStateful
서버 메모리요청 처리에만 필요활성 세션마다 메모리
수평 확장매우 쉬움, 어느 서버든 OK어려움, Sticky Session 필요
장애 복원서버 교체 무영향세션 손실 (외부 store 시 완화)
로드밸런서단순 round-robinclient → server 일관 라우팅 필요
캐싱URL 기반 캐시 효율적캐시 효율 낮음
인증 정보매 요청마다 보냄 (JWT, API key)세션 ID 1회 → 서버가 검증

네트워크 계층의 stateless / stateful

각 프로토콜이 명세상 어떤 상태성을 가지는지.

프로토콜상태성비고
IPStateless패킷 단위 라우팅, 연결 개념 없음
UDPStateless비연결
TCPStateful연결, sequence number, window 등 모두 상태
TLSStateful세션 키, sequence number, certificate 검증 결과
HTTP/1.0Stateless매 요청마다 새 TCP
HTTP/1.1 (keep-alive)Stateless (HTTP 의미)TCP 자체는 stateful
HTTP/2Stateless (HTTP 의미)TCP + HPACK 동적 테이블은 stateful
HTTP/3 (QUIC)Stateless (HTTP 의미)QUIC connection 자체는 stateful
WebSocketStateful지속 연결 + 컨텍스트
Server-Sent EventsStateful지속 연결, 단방향
WebRTCStatefulP2P 세션, DTLS 키

“HTTP 는 stateless 다” 라는 익숙한 말은 HTTP 메시지 의미 차원의 진실이다. 하부의 TCP·TLS·HPACK 동적 테이블은 모두 stateful 이지만, 응용 계층의 HTTP 가 세션 상태를 기억하지 않는다는 게 핵심 의미.


서버에서 상태를 어떻게 관리하나

Stateless 서버의 구조

요청 도착

요청 헤더 검증 (인증 토큰 등)

필요시 외부 store 조회 (DB, Redis)

응답 생성 → 송신

모든 메모리 즉시 해제

서버 프로세스가 가진 상태:

  • TCP 연결 풀 (네트워크 계층, 응용은 무관)
  • 캐시 (선택)
  • 설정 정보

세션·사용자 데이터 같은 응용 상태는 0. 어떤 인스턴스도 동일하게 동작한다.

Stateful 서버의 구조

연결 수립 (WebSocket / SSE / 로그인)

세션 객체 생성 (메모리)
  - user_id, permissions, last_active
  - WebSocket 연결 핸들
  - 미발신 메시지 큐

이후 요청·이벤트마다 세션 컨텍스트 조회 (메모리 lookup)

연결 종료 시까지 객체 유지

서버 메모리에 활성 세션 수만큼의 객체.

활성 세션 객체의 일반적 크기

종류일반적 크기비고
HTTP 인메모리 세션1~5 KBuser_id, role, 일부 캐시
WebSocket 세션 (raw ws)~3.5 KBframe buffer + state
WebSocket 세션 (Socket.IO)6090 KB룸, 어댑터, ACK 추적
SSE 세션~1.2 KBHTTP response stream
WebRTC 세션큼, 미디어 코덱 상태 포함수 백 KB ~ MB

(출처: Ark Protocol 2026, Wolf-tech 2026 측정값)

Stateful 서버의 수평 확장 전략

  1. Sticky Session: LB 가 같은 클라이언트를 같은 서버로
  2. 외부 세션 store: Redis 등에 위임 → 사실상 Stateless 화
  3. Pub/Sub bus: 서버 간 메시지 라우팅 (Socket.IO + Redis Adapter)
WebSocket 클러스터 + Redis Adapter:
  Server A 가 Client X 보유
  Server B 가 Client Y 보유
  → Client X 에게 'B 로 가야할 메시지' 가 오면
    Server A → Redis publish "for-Y" → Server B subscribe → Client Y 에게 전달

브라우저(클라이언트)에서 상태를 어떻게 관리하나

서버가 stateless 면 누군가 상태를 보관해야 한다. 보통 클라이언트.

1. Cookies

서버가 Set-Cookie 헤더로 발급, 브라우저가 자동으로 후속 요청에 첨부.

Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=86400
  • 장점: 자동 첨부, HttpOnly 로 JS 접근 차단 가능
  • 단점: 매 요청에 헤더 추가 (페이로드 오버헤드), CSRF 위험

2. LocalStorage / SessionStorage

JavaScript 가 명시적으로 읽고 쓰는 클라이언트 측 키-값 저장소.

localStorage.setItem('token', 'abc123');  // 영구
sessionStorage.setItem('temp', 'xyz');    // 탭 닫으면 사라짐
  • 장점: 5~10 MB 까지 저장, JS 자유 접근
  • 단점: XSS 에 취약, 자동 첨부 안 됨 (직접 헤더 추가 필요)

3. IndexedDB

본격적인 클라이언트 DB. 큰 데이터, 인덱싱, 트랜잭션 지원.

  • 오프라인 PWA, 캐싱 라이브러리 (Apollo cache 등)에 사용

4. JWT (JSON Web Token)

서명된 토큰에 사용자 정보를 담아 클라이언트가 들고 다님. 서버는 토큰만 검증.

header.payload.signature
eyJhbGciOiJI...  .eyJzdWIiOiI...  .Tjw7XYZ...
  • 장점: 완벽한 stateless 서버, 어느 인스턴스도 같은 검증 가능
  • 단점: 토큰 즉시 취소 어려움 (블랙리스트 필요), 토큰 크기

5. WebSocket Open Connection 자체

WebSocket / SSE 가 열려 있는 동안은 TCP 연결 자체가 상태 컨텍스트.

  • 연결 ID 가 곧 사용자 식별자
  • 끊기면 재인증 필요

클라이언트 상태 보관 비교

방법자동 첨부XSS 안전만료 제어크기
Cookie (HttpOnly)서버 제어~4KB
LocalStorage영구5-10MB
SessionStorage탭 단위5-10MB
IndexedDB✗ (XSS 영향)영구GB 단위
JWT in Cookieexp 클레임~1-2KB
JWT in localStorageexp 클레임~1-2KB

실제 예시

예시 1: 인증

Stateless (JWT):

1. POST /login { user, pass }
2. Server: 검증 후 JWT 발급
3. Response: { token: "eyJ..." }
4. Client: LocalStorage 저장
5. 이후 요청마다: Authorization: Bearer eyJ...
6. Server: 토큰 서명 검증만으로 사용자 식별, 메모리에 세션 X

Stateful (Server Session):

1. POST /login { user, pass }
2. Server: 검증 후 메모리에 세션 생성, { sid: "abc123" → user_data }
3. Response: Set-Cookie: session=abc123
4. Client: 쿠키 자동 보관
5. 이후 요청마다: Cookie: session=abc123
6. Server: 메모리에서 sid 로 세션 조회, 서버에 데이터 있음

예시 2: 채팅, 두 가지 설계

Stateful 만으로 (Socket.IO):

- Server 가 모든 활성 연결 보유
- "user-1 이 user-2 에게" 메시지: 서버가 메모리에서 user-2 의 socket 찾아 전달
- 수평 확장 시: Redis Adapter 로 서버 간 라우팅

Stateless + Stateful 혼합 (메시지 큐):

- Stateless 서버가 메시지 수신 → DB 저장 + 큐에 발행
- 별도 Push 서버 (Stateful) 가 활성 연결 보유 → 큐 구독 → 사용자에게 전달
- 메시지 영속화는 Stateless 쪽이 책임, 전달은 Stateful 쪽이 책임

예시 3: 장바구니

Stateless (LocalStorage):

const cart = JSON.parse(localStorage.getItem('cart') || '[]');
cart.push({ productId: 42, qty: 1 });
localStorage.setItem('cart', JSON.stringify(cart));
// 서버는 결제 시점에만 알면 됨

Stateful (Server Session):

POST /cart/add { productId: 42, qty: 1 }
// 서버: 메모리의 사용자 세션에 cart 추가
// 다른 디바이스에서 로그인해도 같은 cart 표시됨 (장점)
// 비로그인 시 작동 안 함, sticky session 필요 (단점)

“Stateless vs Stateful” 의 결정 트리

질문 1: 서버가 클라이언트 간 사이에 기억해야 할 데이터가 있나?
  ├─ NO  → Stateless (REST API)
  └─ YES → 질문 2

질문 2: 그 데이터를 서버 메모리에 두는 게 본질적인가?
  ├─ NO  → Stateless + 외부 store (Redis, DB)
  └─ YES → 질문 3

질문 3: 지속 연결(WebSocket 등) 이 본질적인가?
  ├─ NO  → 재고 (외부 store 로 더 단순해질 수 있음)
  └─ YES → Stateful 수용 + Sticky Session 또는 Pub/Sub bus

IMPORTANT

“Stateful 이 필요하다” 라는 판단은 신중해야 한다. 외부 store + Stateless 의 조합이 거의 항상 가능하고 운영이 단순하다. WebSocket 같은 지속 연결이 정말 본질적인 경우만 진짜 Stateful 을 받아들이는 게 좋다.

관련

이 글의 용어 (5개)
[Network] HTTP/3: QUIC 위의 HTTP, 0-RTT, connection migrationnetwork
정의 HTTP/3 (2022, RFC 9114) 는 HTTP 의 전송 레이어를 TCP → QUIC 으로 교체한 버전. UDP 위에 QUIC, QUIC 위에 HTTP. 핵심 동기:…
Sticky Sessionconcurrency
정의 Sticky Session (= Session Affinity)은 로드밸런서가 같은 클라이언트의 요청을 항상 같은 백엔드 서버로 라우팅하도록 하는 동작이다. 서버, 즉 세션…
TCPnetwork
정의 TCP (Transmission Control Protocol)는 신뢰성 있는 연결 지향 전송 계층 프로토콜이다. RFC 9293 으로 정의되어 있다. HTTP/1.1·2,…
TLS / SSLnetwork
정의 TLS (Transport Layer Security)는 (또는 위 ) 위에서 동작하는 암호화·인증 프로토콜이다. SSL 은 TLS 의 전신 (Netscape 1995, 더…
UDPnetwork
정의 UDP (User Datagram Protocol)는 비연결·비신뢰 전송 계층 프로토콜이다. RFC 768 (1980) 로 정의되었다. 와 대비된다. "보내고 잊는다(fir…

이 개념을 다룬 위키 페이지 (2)

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기