백엔드 면접 용어 사전
API·RESTsticky session · 세션 어피니티

스티키 세션

같은 클라이언트의 요청을 늘 같은 서버 인스턴스로 보내는 로드밸런서 설정. 연결이 인스턴스에 묶이는 실시간 통신에 필요하다.

로드밸런서가 같은 클라이언트의 요청을 늘 같은 서버 인스턴스로 보내도록 하는 설정. 세션 어피니티(session affinity)라고도 한다.

왜 필요해지나 — 무상태 전제가 깨질 때

HTTP는 무상태라서 어느 인스턴스가 요청을 받아도 상관없다. 그래서 스케일아웃이 쉽다. 그런데 이 전제가 깨지는 경우가 있다.

서버 메모리에 세션을 저장한 경우 WebSocket · SSE 연결처럼 연결 자체가 인스턴스에 묶인 경우

사용자 A ──→ 로드밸런서 ──→ 서버 1 (A의 WebSocket 연결 보유)
사용자 B ──→ 로드밸런서 ──→ 서버 2 (B의 연결 보유)

서버 1이 B에게 메시지를 보내야 한다면?
  → 서버 1에는 B의 연결이 없다. 보낼 방법이 없다.

스티키 세션이 푸는 것과 못 푸는 것

푸는 것 — 핸드셰이크 요청과 이후 트래픽이 같은 인스턴스로 가게 한다. WebSocket 업그레이드가 서버 1로 갔는데 이후 패킷이 서버 2로 가면 연결 자체가 성립하지 않으므로, 이건 반드시 필요하다.

못 푸는 것 — 위 그림의 문제, 즉 인스턴스 간 메시지 전달이다. 이건 Redis Pub/Sub이나 메시지 브로커를 두고 모든 인스턴스가 구독해야 풀린다. 스티키 세션은 필요조건이지 충분조건이 아니다.

어떻게 붙이나

  • 쿠키 기반 — 로드밸런서가 인스턴스를 가리키는 쿠키를 심는다 (가장 흔함)
  • IP 해시 — 클라이언트 IP를 해시해 인스턴스를 고정 (NAT 뒤에서는 편향된다)

대가

① 부하가 고르지 않게 된다. 특정 인스턴스에 오래 붙어 있는 사용자가 몰리면 그쪽만 무거워진다.

② 배포가 까다롭다. 인스턴스를 내리면 거기 묶인 연결이 전부 끊긴다. 클라이언트 재연결 로직이 없으면 사용자가 체감한다.

③ 오토스케일링의 효과가 준다. 새 인스턴스를 띄워도 기존 연결은 옮겨 가지 않아 즉시 부하가 분산되지 않는다.

그래서 원칙은

상태를 서버 밖으로 빼는 것이 먼저다. 세션이라면 Redis 같은 외부 저장소로 옮기면 스티키 세션 자체가 필요 없어진다. 스티키 세션은 연결처럼 원리상 옮길 수 없는 것에만 쓰는 게 맞다.

면접 함정

  • "스티키 세션을 걸면 WebSocket 스케일아웃이 해결된다" → 연결 유지만 되고 인스턴스 간 전달은 브로커가 필요하다.
  • "세션 저장에는 스티키 세션이 정답" → 외부 세션 저장소가 먼저다.

로드밸런서 설정 예

upstream app {
    ip_hash;                 # 클라이언트 IP 기준 고정 (NAT 뒤에서는 편향된다)
    server app1:8080;
    server app2:8080;
}

location /ws {
    proxy_pass http://app;
    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;    # 업그레이드를 통과시킨다
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;                     # 유휴 타임아웃을 늘린다
}

Upgrade·Connection 헤더를 넘기지 않으면 핸드셰이크가 101까지 가지 못한다. WebSocket이 "로컬에서는 되는데 배포하면 안 된다"의 가장 흔한 원인이다.

무엇을 먼저 시도할 것인가

1순위  상태를 서버 밖으로  (세션 → Redis)  → 스티키 자체가 불필요해진다
2순위  옮길 수 없는 것만 스티키로  (WebSocket·SSE 연결)
3순위  인스턴스 간 전달은 브로커로  (스티키로는 절대 안 풀린다)

함께 보면 좋은 용어

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