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

Stateless

· 수정 · 📖 약 3분 · 1,160자/단어 #architecture #network #scaling
stateless, 무상태, Stateless 서버, stateless server

정의

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

같은 요청이라면 어느 서버 인스턴스가 처리하든 결과가 같다는 게 핵심 속성. 이게 수평 확장을 단순하게 만든다.

전체 비교는 Stateless vs Stateful 페이지를 참조.

핵심 특성

측면동작
세션 상태서버 메모리에 없음
수평 확장무제한, round-robin LB
장애 복원서버 교체 시 사용자 무영향
인증 정보매 요청마다 토큰/키 첨부
캐싱URL 기반 캐시 매우 효율적

수평 확장 아키텍처

flowchart LR
    C1[Client 1] --> LB[Load Balancer]
    C2[Client 2] --> LB
    C3[Client N] --> LB
    LB --> S1[Server 1]
    LB --> S2[Server 2]
    LB --> S3[Server N]
    S1 & S2 & S3 --> Redis[(Redis)]
    S1 & S2 & S3 --> DB[(Database)]

모든 서버가 동일한 Redis 와 DB 를 바라본다. LB 가 어느 서버로 요청을 보내도 같은 결과가 나온다. 서버 추가는 인스턴스 수만 늘리면 끝이다.

Health check 로 고장 난 서버를 풀에서 제거해도 나머지 서버가 즉시 트래픽을 흡수한다. 사용자에게는 무영향이다.

어떻게 stateless 를 유지하는가

상태가 정말 필요 없는 경우는 드물다. 보통은 “상태를 서버 메모리에 두지 않는다” 가 핵심이다. 어디에 두는가가 선택이다.

1. 외부 store 위임

세션·캐시·잠금 등을 Redis, DynamoDB, DB 에 둔다. 모든 서버 인스턴스가 같은 store 에 접근.

요청 → 어느 서버든 OK → Redis 에서 세션 조회 → 응답

비용: 매 요청마다 store I/O (보통 1-3 ms). 운영 단순성을 위한 합리적 trade-off.

상태를 클라이언트가 들고 다님. 서버는 검증만.

로그인 → JWT 발급 → 클라이언트가 보관
이후 요청 → Authorization: Bearer <JWT> → 서명 검증으로 사용자 식별

3. URL/Query 에 상태 인코딩

페이지네이션 커서, 필터 조건 등을 URL 에 담는다.

GET /products?cursor=eyJpZCI6MTIzfQ&limit=20

서버는 cursor 만 읽고 응답.

JWT 인증 플로우

sequenceDiagram
    autonumber
    participant C as Client
    participant S as Server
    participant DB

    C->>S: POST /login, credentials
    S->>DB: SELECT user
    DB-->>S: user found
    S-->>C: 200 OK, JWT signed
    Note over C: 클라이언트 JWT 보관
    C->>S: GET /api/profile, Authorization Bearer JWT
    S->>S: JWT 서명 검증
    S-->>C: 200 OK, profile

서버는 요청 사이에 아무것도 기억하지 않는다. JWT 서명 키만 있으면 어느 인스턴스든 동일한 검증이 가능하다.

실무 예시

REST API

GET /api/users/42
Authorization: Bearer eyJhbGc...

→ 서버: JWT 검증, DB 에서 user 42 조회, 응답
   메모리에 세션 X

CDN / 정적 자원

GET /static/main.css
→ 어느 edge 서버든 같은 응답
→ 캐시 적중률 매우 높음

Serverless 함수 (AWS Lambda 등)

함수 실행마다 컨테이너가 새로 만들어질 수 있음 → stateless 가 강제됨. 세션은 DynamoDB / Redis 등에 위임 필수.

Redis 세션 스토어 구현

JWT 대신 서버 측 세션 ID 를 유지해야 할 때도, 외부 store 에 세션을 두면 서버 자체는 stateless 로 운영할 수 있다.

import uuid, json
import redis

r = redis.Redis(host="redis-host", port=6379)
TTL = 3600  # 1 hour

def create_session(user_id: str) -> str:
    sid = str(uuid.uuid4())
    r.setex(f"session:{sid}", TTL, json.dumps({"user_id": user_id}))
    return sid

def get_session(sid: str) -> dict | None:
    raw = r.get(f"session:{sid}")
    return json.loads(raw) if raw else None

def delete_session(sid: str) -> None:
    r.delete(f"session:{sid}")

Redis Sentinel 또는 Redis Cluster 로 단일 장애점을 제거해야 한다. Redis 다운 = 전체 서비스 인증 불능이 되기 때문이다.

# Redis Sentinel docker-compose 간략 예시
services:
  redis-master:
    image: redis:7
  redis-replica:
    image: redis:7
    command: redis-server --replicaof redis-master 6379
  redis-sentinel:
    image: redis:7
    command: redis-sentinel /etc/sentinel.conf

Kubernetes에서의 Stateless

Stateless 서버는 Kubernetes Deployment 로 운영한다. k8s-statefulset 이 필요 없다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api-server
  template:
    spec:
      containers:
        - name: api-server
          image: myapp:latest
          env:
            - name: REDIS_URL
              valueFrom:
                secretKeyRef:
                  name: redis-secret
                  key: url

HPA 로 CPU / RPS 기반 자동 스케일도 가능하다.

kubectl autoscale deployment api-server --cpu-percent=70 --min=2 --max=20

pod 에 순서나 안정적인 네트워크 ID 가 없어도 상관없다. stateless 서버에는 이게 오히려 장점이다.

언제 Stateless를 선택하는가

상황권장
REST API, 배치 처리Stateless
퍼블릭 마이크로서비스Stateless
Serverless / LambdaStateless
실시간 채팅, 게임 서버Stateful 고려
비디오 스트리밍 세션Stateful 고려
P2P 협업 도구Stateful 고려

수평 확장·배포 단순성이 목표면 stateless 가 기본 선택이다. 저지연 양방향 통신이 핵심이면 Stateful 을 고려한다.

함정과 한계

1. “Stateless 인데 외부 store 가 단일 장애점”

Redis 다운 → 모든 서버가 세션 조회 실패. Stateless 한 서버를 만들어놓고 외부 store 에 의존하면 그게 SPOF 가 된다. 고가용성 Redis 클러스터, fallback 전략 필요.

2. JWT 즉시 취소 어려움

JWT 는 만료 전까지 유효. 사용자 권한 변경/로그아웃을 즉시 반영하려면 블랙리스트 같은 별도 store 가 필요 → 일부 stateful 화.

3. Per-request 토큰 검증 비용

매 요청마다 JWT 서명 검증 (CPU) + 외부 store 조회 (I/O). 트래픽이 많으면 누적된다.

통신 프로토콜과의 관계

프로토콜Stateless 친화도
HTTP/1.1, 2, 3✓ 완벽 (메시지 의미상)
Short Polling
Long Polling✓ (HTTP 의미상, 운영상은 연결 점유)
Server-Sent Events✗ Stateful (지속 연결)
WebSocketStateful
WebRTC✗ Stateful

대부분의 REST API · CDN · 정적 사이트는 자연스럽게 stateless.

관련 위키

이 글의 용어 (8개)
[Auth] JWT: 구조, 서명, 만료, refresh tokenauth-security
정의 JWT (JSON Web Token) (RFC 7519) 은 base64url 인코딩된 JSON + 서명 형태의 self-contained token. 서버가 세션 저장 없…
[Concurrency] Connection Pool: DB, HTTP, gRPC 의 공통 패턴concurrency
정의 Connection Pool = 연결을 미리 만들어 두고 재사용. 매 요청마다 TCP + TLS + auth 비용을 회피. Pool 의 동작 = producer (reque…
[Concurrency] Rate Limiting: token bucket, sliding windowconcurrency
정의 Rate Limiting = 시간당 요청 수 제한. API 보호, fair use, 비용 제어, DDoS 완화. 5가지 알고리즘 1. Fixed Window - 단순. - …
[DB Internals] Redis 8 / Valkey 9: 라이센스 분기, 신 데이터 구조, 실전 운영database-internals
정의 Redis 는 in-memory key-value 데이터 구조 서버. 2009년 Salvatore Sanfilippo (antirez) 가 Lua Manuscripts 의 …
[K8s] StatefulSet: 순서 보장, 고유 ID, 영속 스토리지kubernetes
정의 StatefulSet = 상태 있는 워크로드를 위한 컨트롤러. Deployment 와의 차이: | 항목 | Deployment | StatefulSet | |---|---|…
Statefulconcurrency
정의 Stateful (상태유지) 서버는 클라이언트와의 세션·연결 컨텍스트를 메모리에 유지한다. 후속 요청·이벤트는 이 컨텍스트를 활용해 처리된다. 같은 클라이언트의 요청은 같은…
Stateless vs Statefulconcurrency
정의 Stateless (무상태): 서버가 클라이언트 요청 간 상태를 기억하지 않음. 각 요청은 완전히 독립적이며 필요한 모든 정보는 요청에 담겨 있다. Stateful (상태유…
Sticky Sessionconcurrency
정의 Sticky Session (= Session Affinity)은 로드밸런서가 같은 클라이언트의 요청을 항상 같은 백엔드 서버로 라우팅하도록 하는 동작이다. 서버, 즉 세션…

💬 댓글

사이트 검색 / 명령어

검색

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