HTTP를 설명할 때 자주 나오는 말이 stateless다. 서버가 클라이언트의 이전 상태를 기억하지 않고, 각각의 요청을 독립적인 요청으로 처리한다는 뜻이다.

처음 들으면 조금 이상하게 느껴질 수 있다. 웹 서비스는 로그인 상태도 유지해야 하고, 장바구니도 기억해야 하고, 사용자가 어떤 화면을 보고 있는지도 어느 정도 이어져야 한다. 그런데 HTTP 자체는 왜 굳이 상태를 기억하지 않는 방향으로 설계됐을까?

단순함과 확장성

가장 큰 이유는 단순함과 확장성이다. 초기 웹은 문서를 요청하고 응답을 받는 구조에 가까웠다. 서버가 각 사용자의 상태를 계속 들고 있지 않으면, 요청 하나를 처리하는 방식이 단순해진다. 어떤 서버가 요청을 받든 요청 안에 필요한 정보가 들어 있으면 처리할 수 있다.

반대로 서버가 사용자 상태를 직접 기억해야 한다면 이야기가 복잡해진다. 사용자가 많아질수록 서버가 들고 있어야 하는 상태도 늘어난다. 서버를 여러 대로 늘렸을 때도 “이 사용자의 상태를 어느 서버가 알고 있는가”라는 문제가 생긴다. 결국 세션 공유 저장소를 두거나, 로드밸런서에서 같은 사용자를 같은 서버로 보내는 식의 추가 설계가 필요해진다.

그래서 HTTP의 기본 방향은 요청을 독립적으로 만드는 쪽이다. 서버는 이전 요청을 몰라도, 지금 들어온 요청만 보고 처리할 수 있어야 한다. 이 구조 덕분에 서버를 수평으로 늘리기 쉽고, 장애가 나도 다른 서버가 요청을 이어받기 쉽다.

그래도 상태는 필요하다

물론 실제 서비스에서 상태가 아예 필요 없는 것은 아니다. 로그인 상태를 예로 들면, 보통 쿠키나 토큰을 이용해 클라이언트가 매 요청마다 인증 정보를 함께 보낸다. 서버는 그 정보를 보고 사용자를 확인한다. 상태를 완전히 없앤다기보다, HTTP 요청 안에서 필요한 상태를 다시 전달하는 방식에 가깝다.

세션 방식은 서버 쪽에 로그인 상태를 저장하고, 클라이언트는 세션 ID만 쿠키로 들고 있는 구조다. 서버가 세션을 직접 관리하므로 강제 로그아웃이나 세션 무효화가 비교적 쉽다. 대신 서버가 상태를 들고 있기 때문에 확장할 때 세션 저장소나 동기화 문제가 따라온다.

JWT 같은 토큰 방식은 사용자 정보를 담은 토큰을 클라이언트가 보관하고, 요청마다 헤더나 쿠키로 보낸다. 서버는 토큰을 검증해서 사용자를 확인한다. 서버가 세션 저장소를 조회하지 않아도 되기 때문에 확장에는 유리하지만, 이미 발급된 토큰을 즉시 강제로 만료시키려면 블랙리스트나 짧은 만료 시간 같은 별도 장치가 필요하다.

핵심은 상태의 위치다

결국 stateless는 “서비스가 상태를 가지면 안 된다”는 말이 아니다. HTTP 서버가 요청 사이의 맥락을 기본적으로 기억하지 않는다는 뜻이다. 필요한 상태는 쿠키, 세션, 토큰, 데이터베이스 같은 다른 장치를 통해 명시적으로 다룬다. 이 구분을 잡고 나면 HTTP가 왜 단순한 요청-응답 구조를 유지하려 했는지 이해하기 쉬워진다.