HTTP의 Stateless·Connectionless와 쿠키, 세션
HTTP가 상태와 연결을 유지하지 않는 이유와 지속 연결, 쿠키와 세션으로 상태를 다루는 방법, 다중 서버의 세션 관리를 정리한다.
시리즈 · 네트워크10 / 12
- OSI 7 Layer와 계층별 역할, ARP
- IP 주소: IPv4와 IPv6, NAT, 체크섬
- 라우터: 경로 지정, 브로드캐스트 컨트롤, 라우팅과 스위칭
- DHCP 동작 방식과 UDP를 사용하는 이유
- IP의 한계와 TCP·UDP, 3-Way Handshake
- TCP 4-Way Handshake와 TIME_WAIT
- DNS 정리: 구조, 질의 방식, 레코드, 이중화와 위임
- 로드 밸런서: 구성 방식, 헬스 체크, L4와 L7
- HTTP 메서드와 상태 코드: 멱등성, POST와 PUT의 차이
- HTTP의 Stateless·Connectionless와 쿠키, 세션
- HTTP/1.1, HTTP/2, HTTP/3 비교
- 소켓과 포트, 그리고 웹소켓
Stateless와 Connectionless
HTTP는 두 가지 특성을 가진다.
- Stateless: 서버가 클라이언트의 이전 상태를 보존하지 않는다.
- Connectionless: 응답을 보낸 뒤 TCP 연결을 끊어, 연결을 유지하지 않는다. 서버의 자원을 효율적으로 관리하고 많은 요청에 대응할 수 있다.
Stateless와 Stateful
Stateless
- 서버가 클라이언트의 세션 상태나 세션 정보를 저장하지 않고, 요청에 대한 응답만 처리한다.
- 통신에 필요한 모든 상태 정보는 클라이언트가 가지고 있다가, 서버와 통신할 때 데이터에 실어 보낸다.
- 예: UDP, HTTP
Stateful
- 서버가 클라이언트의 상태를 보존한다.
- 연결 상태를 양쪽이 유지하는 TCP가 대표적인 예다.
- 단점: 상태를 가진 서버가 멈추거나, 장애로 다른 서버를 사용해야 할 때 문제가 생긴다.
HTTP가 Stateless 구조를 사용하는 이유
서버가 상태를 보관하지 않으므로 클라이언트의 요청에 어느 서버가 응답해도 상관없다. 따라서 요청이 대폭 증가해도 서버를 증설해 해결할 수 있다.
HTTP 지속 연결 (Persistent Connection)
Connectionless 방식에서는 요청마다 3-way handshake를 다시 해야 한다. 지속 연결은 이 비용 문제를 해결한다.
- 연결이 맺어진 뒤 일정 시간 동안 연결을 유지하거나, 응답이 다 올 때까지 기다린 후 연결을 종료한다.
- 요청 헤더에
Connection: keep-alive를 추가해 사용한다. - HTTP/1.1에서는 헤더를 명시하지 않아도 모든 요청과 응답이 기본적으로 지속 연결을 지원한다.
Connection: keep-alive를 이해하지 못하는 프록시가 중간에 있으면 클라이언트와 서버 간의 연결이 정상적으로 동작하지 않을 수 있다.
HTTP/2는 멀티플렉싱으로 단일 TCP 연결 위에서 다수의 요청과 응답을 스트림 형태로 지연 없이 주고받는다. 그래서 HTTP/2를 사용하면 지속 연결을 더 이상 고민할 필요가 없다. 자세한 내용은 HTTP/1.1, HTTP/2, HTTP/3 비교에서 다룬다.
TCP keep-alive와 HTTP keep-alive
- TCP keep-alive: 연결이 여전히 살아 있는지, 끊어졌는지를 확인하는 역할이다.
- HTTP keep-alive: 연결을 끊지 않고 유지해 재사용하는 데 쓰인다.
Cookie
Stateless한 HTTP에서 상태를 이어 가기 위해 쓰는 것이 쿠키다. 쿠키는 클라이언트(브라우저) 로컬에 저장되는 Key-Value 형태의 데이터다.
- 서버가 응답 헤더의
Set-Cookie로 클라이언트에 쿠키를 만든다. - 이후에는 사용자가 따로 요청하지 않아도 브라우저가 요청 헤더에 쿠키를 넣어 서버에 전송한다.
- 유효 시간을 명시할 수 있으며, 유효 시간이 정해지면 브라우저가 종료되어도 인증이 유지된다.
- 브라우저마다 다르지만, 대체로 쿠키는 50개를 넘기지 말고 하나의 크기는 4KB 미만으로 유지해야 한다.
쿠키는 Key, Value, 유효 시간, 도메인, 경로로 구성된다.
Session
- 쿠키를 기반으로 동작하지만, 사용자 정보를 서버 측에서 관리한다.
- 서버가 클라이언트에게 Session ID를 부여하고, 브라우저가 서버에 접속해서 종료할 때까지 인증 상태를 유지한다.
- 사용자 정보를 서버에 두기 때문에 보안에 유리하지만, 서버 메모리를 많이 차지한다.
세션 방식의 로그인
- 클라이언트가 서버에 로그인하면 Session ID를 발급받는다.
- 클라이언트는 Session ID를 쿠키에 저장한다.
- 이후 요청마다 헤더에 Session ID를 실어 서버에 전달한다.
- 서버는 Session ID로 세션 저장소에 있는 클라이언트 정보를 가져온다.
- 그 정보로 요청을 처리해 클라이언트에게 응답한다.
Stateless의 의미를 생각하면, 세션은 적절하지 않은 인증 방법일까
세션은 서버가 클라이언트의 상태를 보관하므로 서버에 부하가 생기고, 세션 ID를 탈취당할 위험도 있다. 그래서 JWT 같은 토큰 기반 인증 방식을 사용하는 경우가 많다.
서버가 여러 대일 때 세션을 관리하는 방법
규모가 커져 서버가 여러 대가 되면, 한 서버에만 저장된 세션은 다른 서버에서 사용할 수 없다. 해결 방법은 크게 두 가지다.
세션 클러스터링
서버가 가진 세션 정보를 여러 서버에 공유하는 방식이다. 톰캣은 DeltaManager와 BackupManager 두 가지 방식을 제공한다.
- DeltaManager: 클러스터에 존재하는 모든 노드에 세션을 복제한다.
- 애플리케이션이 배포되지 않은 노드까지 복제 대상이 되기 때문에, 4개 이상의 노드를 가진 대규모 클러스터에는 권장하지 않는다.
- BackupManager: 세션 데이터를 하나의 백업 노드에만 복제하며, 애플리케이션이 배포된 노드만 대상으로 한다.
- 세션이 복제되지 않은 노드는 세션을 가진 노드의 위치를 알고 있다가, 요청이 오면 해당 노드에 요청해 처리한다.
Redis
Key-Value 구조로 데이터를 저장하고 관리하는 인메모리 DB다. 메모리에 데이터를 저장하므로 휘발성을 가진다.
- 세션 클러스터링: 서버 내부의 메모리에서 세션을 관리한다.
- Redis: 서버 외부의 저장소에서 세션을 관리한다.