그순간

쿠키, 세션, JWT

홍지민·2026년 10월 12일·4 조회

1. 문제의 출발점 — HTTP는 무상태(Stateless) 프로토콜

HTTP는 요청과 응답이 끝나면 연결 정보를 유지하지 않는다. 즉 서버 입장에서는 매 요청이 완전히 새로운 클라이언트의 요청처럼 보인다. 하지만 로그인 상태 유지, 장바구니 정보 유지처럼 "사용자를 기억해야 하는" 기능이 반드시 필요하다. 이 문제를 해결하기 위해 등장한 것이 쿠키, 세션, 토큰(JWT) 기반 인증 방식이다.

개념

쿠키는 서버가 클라이언트(브라우저)에게 발급하여 클라이언트 측에 저장하도록 하는 작은 데이터 조각이다. 브라우저는 이후 동일한 도메인으로 요청을 보낼 때마다 저장된 쿠키를 자동으로 HTTP 헤더(Cookie)에 담아 함께 전송한다.

동작 흐름

  1. 클라이언트가 서버에 요청을 보낸다.
  2. 서버가 응답 헤더 Set-Cookie에 데이터를 담아 응답한다.
  3. 브라우저는 이 쿠키를 저장한다.
  4. 이후 같은 도메인에 요청할 때마다 브라우저가 자동으로 Cookie 헤더에 담아 전송한다.

주요 속성

  • Expires / Max-Age: 쿠키의 만료 시점(수명)을 지정한다.
  • Domain / Path: 쿠키가 전송될 대상 범위를 지정한다.
  • Secure: HTTPS 연결에서만 전송되도록 제한한다.
  • HttpOnly: JavaScript(document.cookie)로 접근하지 못하게 막아 XSS 공격을 통한 탈취를 방어한다.
  • SameSite: 요청이 다른 사이트(도메인)에서 발생했을 때 쿠키 전송 여부를 제어하여 CSRF 공격을 완화한다. (Strict, Lax, None)

한계

  • 클라이언트에 저장되므로 민감한 정보(비밀번호, 개인정보)를 직접 담기에 부적합하다.
  • 탈취(XSS, 네트워크 스니핑) 시 도용 위험이 있다.
  • 이 때문에 실무에서는 쿠키 자체에 사용자 정보를 담기보다, "세션 식별자(Session ID)"만 담고 실제 데이터는 서버에 보관하는 방식을 많이 사용한다.

3. 세션 (Session)

개념

세션은 서버 측에 사용자의 상태 정보를 저장하고, 클라이언트에게는 그 정보를 찾을 수 있는 열쇠 역할을 하는 세션 ID만 쿠키로 발급하는 방식이다.

동작 흐름

  1. 클라이언트가 로그인에 성공하면 서버는 세션 저장소(메모리, Redis, DB 등)에 사용자 정보를 저장하고 고유한 세션 ID를 생성한다.
  2. 서버는 Set-Cookie: JSESSIONID=xxxx처럼 세션 ID를 쿠키에 담아 클라이언트에게 전달한다.
  3. 클라이언트는 이후 요청마다 이 세션 ID를 쿠키로 함께 전송한다.
  4. 서버는 세션 ID를 받아 저장소에서 사용자 정보를 조회하여 인증 상태를 확인한다.

특징

  • 실제 사용자 데이터는 서버에만 존재하므로 클라이언트 측 탈취 위험이 쿠키 단독 방식보다 낮다.
  • 서버가 세션을 강제로 만료시키거나 삭제하면 즉시 로그아웃 처리가 가능하다 (제어권이 서버에 있음).

한계

  • 서버 확장성 문제: 서버가 여러 대(스케일 아웃)로 늘어나면, 특정 서버에만 세션이 저장되어 있는 경우 다른 서버가 그 세션을 알지 못하는 문제(Sticky Session 필요, 혹은 Redis 등 외부 세션 스토리지로 세션 클러스터링 필요)가 발생한다.
  • 세션을 유지하는 사용자 수가 많아질수록 서버의 메모리/저장소 부담이 커진다.
  • 이런 특성 때문에 세션 방식은 "상태를 서버가 들고 있다"는 의미에서 Stateful 인증 방식으로 분류된다.

4. JWT (JSON Web Token)

개념

JWT는 서버가 사용자 정보를 자체적으로 서명하여 발급하는 토큰으로, 서버가 별도로 상태(세션)를 저장하지 않고도 토큰 자체의 내용과 서명만으로 사용자를 인증할 수 있게 한다. 이 때문에 Stateless 인증 방식으로 분류된다.

구조

JWT는 .으로 구분된 3개의 부분으로 구성된다.

헤더(Header).페이로드(Payload).서명(Signature)
  1. Header: 토큰 타입(JWT)과 서명에 사용된 알고리즘(예: HS256, RS256) 정보를 담고 있다. Base64Url로 인코딩된다.
  2. Payload: 사용자 식별 정보와 claim(주장하는 정보)을 담는다. 예: sub(subject, 사용자 식별자), exp(만료 시각), iat(발급 시각), 커스텀 claim(권한, 역할 등). Base64Url로 인코딩되지만 암호화된 것이 아니라 단순 인코딩이므로 누구나 디코딩해서 내용을 볼 수 있다. 따라서 비밀번호 같은 민감 정보를 payload에 넣으면 안 된다.
  3. Signature: Header와 Payload를 합친 값을 서버만 아는 비밀키(HMAC) 또는 개인키(RSA/ECDSA)로 서명한 값이다. 이 서명 덕분에 토큰이 위변조되지 않았음을 검증할 수 있다.

동작 흐름

  1. 로그인 성공 시 서버가 사용자 정보를 payload에 담아 서명한 JWT를 발급한다.
  2. 클라이언트는 이 토큰을 저장(주로 Authorization: Bearer 헤더로 전송, 혹은 쿠키에 저장)한다.
  3. 이후 요청마다 토큰을 함께 전송하면, 서버는 별도의 저장소 조회 없이 서명만 검증하여 유효성과 사용자 정보를 확인한다.

장점

  • 서버가 세션 저장소를 유지할 필요가 없어 수평 확장(스케일 아웃)에 유리하다.
  • 여러 서비스(마이크로서비스) 간 인증 정보를 공유하기 쉽다.

단점 / 주의점

  • 탈취 시 무효화가 어렵다: 세션은 서버에서 즉시 삭제하면 되지만, JWT는 만료 시간이 될 때까지 서버가 강제로 막을 방법이 기본적으로 없다(서명이 유효한 한 계속 인증 통과). 이를 보완하기 위해 블랙리스트(Redis 등)를 별도로 운영하거나, Access Token의 수명을 짧게 가져가는 방식을 사용한다.
  • Payload는 암호화가 아니라 인코딩이라는 점을 반드시 인지해야 한다(민감정보 저장 금지).
  • 토큰 크기가 쿠키의 세션 ID보다 커서 매 요청마다 전송 비용이 조금 더 든다.

Access Token / Refresh Token 패턴

실무에서는 보안과 사용성을 절충하기 위해 두 가지 토큰을 함께 사용하는 경우가 많다.

  • Access Token: 실제 API 요청 인증에 사용, 수명이 짧음(예: 15분~1시간).
  • Refresh Token: Access Token 재발급 용도로만 사용, 수명이 길고 안전한 저장소(HttpOnly 쿠키 등)에 보관. Access Token이 만료되면 Refresh Token으로 새 Access Token을 재발급받는다.
목차
쿠키, 세션, JWT | 그순간