2025년 10월 29일

세션 기반 인증 vs 토큰 기반 인증 / JWT 학습

인증 상태 유지(Authentication State)

  • 인증 상태 유지란 로그인 후 일정 기간 동안 사용자가 다시 로그인하지 않아도 신원이 확인된 상태를 유지하는 것을 의미한다.
  • 이를 구현하는 방법은 크게 2가지가 존재한다.
 

세션 기반 인증 (Session-based)

핵심: 서버가 사용자의 인증 상태를 저장하고 관리 (Stateful)
  • 세션 방식은 전통적인 웹사이트에서 많이 사용되며, 서버가 사용자의 로그인 상태를 기억하고 있다.
      1. 사용자가 아이디/비밀번호로 로그인에 성공하면 서버는 사용자를 식별할 수 있는 고유한 세션 ID를 생성한다.
      1. 이후 해당 세션 정보를 서버의 메모리나 데이터베이스에 저장한다.
      1. 그리고 서버는 이 세션 ID를 세션 쿠키에 담아 클라이언트(브라우저)에게 보낸다.
      1. 클라이언트는 이후 모든 요청에 이 세션 쿠키(세션 ID)를 자동으로 포함하여 서버에 보낸다.
      1. 서버는 요청에 담긴 세션 ID를 받고, 자신이 저장한 세션 정보와 비교해서 사용자가 로그인 상태임을 확인한다.
 
  • 세션 기반 인증의 장점은 다음과 같다.
    • 서버가 모든 인증 상태를 관리하므로, 특정 사용자의 세션을 즉시 무효화시킬 수 있다.
    • 중요한 사용자 정보를 서버에만 보관하고 클라이언트에게는 식별자만 보내므로, 민간함 정보가 클라이언트에 노출될 위험이 적다.
 
  • 세션 기반 인증의 단점은 다음과 같다.
    • 사용자가 많아질수록 서버가 저장하고 관리해야 할 세션 정보가 늘어나 서버 메모리나 DB에 부하를 준다.
    • 서버가 여러 대일 경우, 사용자의 요청이 항상 동일한 서버가 가도록 설정하거나 모든 서버가 세션 정보를 실시간으로 공유해야 하는 복잡함이 있다.
 

토큰 기반 인증 (Token-based)

핵심: 서버가 인증 정보를 암호화하여 클라이언트에게 맡김 (Stateless)
  • 토큰 방식은 주로 JWT(JSON Web Token)를 사용하며, 현대 웹 애플리케이션에서 주로 쓰인다.
      1. 사용자가 로그인에 성공하면, 서버는 사용자의 정보(ID, 권한 등)와 만료 시간을 담은 토큰(JWT)을 생성하고, 이를 서버만의 비밀 키로 서명한다.
      1. 서버는 이 서명된 토큰을 클라이언트에게 전송한다.
      1. 클라이언트는 이 토큰을 받아 로컬 스토리지, 세션 스토리지 또는 쿠키에 저장한다.
      1. 클라이언트는 이후 모든 요청 시 이 토큰을 HTTP 헤더(주로 Authorization: Bearer <token>)에 포함하여 보낸다.
      1. 서버는 받은 토큰의 서명을 자신이 가진 비밀 키로 검증한다. 서명이 유효하면 토큰에 담긴 정보를 신뢰하고 사용자를 인증한다. (별도 DB 조회가 필요 없음)
 
  • 토큰 기반 인증의 장점은 다음과 같다.
    • 서버가 인증 상태를 따로 저장하지 않으므로(Stateless), 서버 부하가 적고 여러 서버 간에 자유롭게 요청을 분산할 수 있다. (MSA에 매우 유리)
    • 모바일 앱, 웹, 다른 도메인의 서비스 등 다양한 플랫폼과 환경에서 인증을 공유하기 용이하다.
 
  • 토큰 기반 인증의 단점은 다음과 같다.
    • 토큰이 제3자에게 탈취되면 악용될 수 있다. (만료 시간을 짧게 설정해야 함)
    • 토큰은 일단 발급되면 만료 전까지 유효하므로, 특정 사용자를 강제로 로그아웃시키기 힘들다. (별도의 블랙리스트 관리 필요)
 
  • 세션 기반과 토큰 기반 인증의 차이를 표로 정리해보면 다음과 같다.
    • 특징
      세션 기반 (Session-based)
      토큰 기반 (Token-based)
      상태 관리
      Stateful (서버가 상태 저장)
      Stateless (서버가 상태 저장 안 함)
      인증 정보 저장
      서버 (메모리, DB 등)
      클라이언트 (로컬 스토리지, 쿠키 등)
      클라이언트 전송
      세션 ID (주로 쿠키)
      토큰 (주로 Authorization 헤더)
      확장성
      낮음 (서버 부하, 세션 공유 필요)
      높음 (서버 부하 적음)
      주요 사용처
      전통적인 서버 렌더링 웹사이트
      모바일 앱, API, SPA, 마이크로서비스
      강제 로그아웃
      쉬움 (서버에서 세션 삭제)
      어려움 (만료 시점까지 유효)
 

JWT (Json Web Token)

  • JWT(JSON Web Token)는 웹 환경에서 정보를 JSON 객체 형태로 안전하게 전송하기 위한 개방형 표준(RFC 7519)이다.
  • 이전 설명에서 '토큰 기반 인증'의 핵심이 '서버가 인증 상태를 저장하지 않는 것(Stateless)'라고 했었는데, JWT가 바로 이를 가능하게 하는 가장 대표적인 기술이다.
  • 조금 더 쉽게 비유하자면, JWT는 '정보가 담긴 위조 불가능한 디지털 자유이용권 팔찌'와 같다.
    • 놀이공원 직원이 팔찌를 보고(서명 검증) 이 사람이 놀이공원 이용객인지 바로 알 수 있다.
    • 직원은 고객이 누구인지 매번 컴퓨터에 검색해서 확인하지 않는다(Stateless).
    • 팔찌에는 "매직패스 고객", "종일권" 같은 정보(Payload)가 이미 적혀있다.
 

JWT의 3가지 구성 요소

  • JWT는 .(점)으로 구분된 세 부분으로 구성된다.
    • aaaaaa .bbbbbb . cccccc (Header) . (Payload) . (Signature)
 

1️⃣ 헤더 (Header)

  • 어떤 종류의 토큰인지, 그리고 어떤 알고리즘으로 서명되었는지를 나타낸다.
  • 구성
    • typ (Type): 토큰의 유형 (보통 "JWT")
    • alg (Algorithm): 서명 생성에 사용된 알고리즘 (예: "HS256" 또는 "RS256")
  • 이 JSON 객체를 Base64Url 방식으로 인코딩한 것이 첫 번째 부분(aaaaaa)이다.
 

2️⃣ 페이로드 (Payload)

  • 토큰을 통해 실제로 전달하려는 정보(데이터)가 담긴 부분이다.
  • 이 정보의 한 조각을 클레임(Claim) 이라고 부른다.
  • 주요 클레임
    • iss (Issuer): 토큰 발급자 (예: "https://test.com")
    • sub (Subject): 토큰의 주제 (예: 사용자의 고유 ID)
    • exp (Expiration Time): 토큰 만료 시간
    • iat (Issued At): 토큰 발급 시간
    • 이 외에도 사용자의 권한, 이름 등 필요한 정보를 자유롭게 추가할 수 있다.
  • 이 JSON 객체 역시 Base64Url 방식으로 인코딩된 것이 두 번째 부분(bbbbbb)이다.
 

3️⃣ 서명 (Signature)

  • 이 토큰이 위조되거나 변조되지 않았음을 증명하는 디지털 서명이다.
  • 생성 방식
      1. 인코딩된 헤더와 인코딩된 페이로드를 .으로 합친다.
      1. 이 값을 서버만 알고 있는 '비밀 키(Secret Key)'를 사용하여 헤더에 명시된 알고리즘(alg)으로 암호화한다.
  • 예시 (HMAC-SHA256)
    • HMAC-SHA256(secretKey, encodedHeader + "." + encodedPayload)
  • 이것이 세 번째 부분(cccccc)이다.
 

액세스 토큰과 리프레시 토큰

  • JWT는 한 번 발급되면 만료 전까지 유효하다.
  • 만약 이 토큰이 탈취당하면, 해커는 토큰 만료 시간까지 사용자의 계정을 마음대로 이용할 수 있다.
  • 이 문제를 해결하는 가장 좋은 방법은 액세스 토큰의 만료 시간을 매우 짧게(예: 30분, 1시간) 설정하는 것이다.
  • 하지만 이렇게 하면 새로운 문제가 생긴다.
  • 사용자가 30분마다 로그인이 풀리고 다시 아이디와 비밀번호를 입력해야 한다. 이건 최악의 사용자 경험(UX)이다.
  • 이 딜레마(보안 vs 편의성)을 해결하기 위해 '액세스 토큰'과 '리프레시 토큰'을 도입한다.
 

액세스 토큰 (Access Token)

  • 액세스 토큰은 실제 API 요청 시 사용하는 '단기 출입증' 이다.
  • 유효 기간을 30분 정도로 매우 짧게 설정해두기 때문에 해커에게 탈취되어도 30분 뒤면 쓸모가 없어진다.
 

리프레시 토큰 (Refresh Token)

  • 리프레시 토큰은 발급 받은 액세스 토큰이 만료되면 새로운 액세스 토큰으로 교환해주는 '장기 회원권 카드' 이다.
  • 유효 기간을 7일, 30일 정도로 매우 길게 설정해둔다는 특징이 있다.
  • 이 토큰 자체로는 API에 접근할 수 없고, 오직 새로운 액세스 토큰을 발급받는 용도로만 사용한다.
 

사용자 로그인 시나리오

  1. 최초 로그인
      • 사용자가 ID/PW로 로그인한다.
      • 서버는 인증 성공 시, 액세스 토큰(유효기간 30분)과 리프레시 토큰(유효기간 7일)을 둘 다 발급하여 클라이언트에게 전달한다.
      • 서버는 이 리프레시 토큰을 DB에 저장해둔다. (누가 이 토큰을 가졌는지 기록)
  1. 정상적인 API 요청 (30분 이내)
      • 클라이언트는 API 요청 시 액세스 토큰을 헤더에 담아 보낸다.
      • 서버는 액세스 토큰이 유효함을 확인하고 데이터를 응답한다.
  1. 액세스 토큰 만료 (30분 경과)
      • 클라이언트가 만료된 액세스 토큰으로 API를 요청한다.
      • 서버는 토큰이 만료되었음을 확인하고 401 Unauthorized (인증 만료) 에러를 응답한다.
  1. 액세스 토큰 재발급 요청
      • 라이언트는 401 에러를 받으면, 액세스 토큰이 만료됐다는 사실을 인지하고 숨겨둔 리프레시 토큰을 서버의 토큰 재발급 전용 API(예: /api/refresh)로 보낸다.
  1. 서버의 리프레시 토큰 검증
      • 서버는 받은 리프레시 토큰이 DB에 저장된 토큰과 일치하는지, 아직 유효 기간이 남았는지 확인한다.
  1. 새로운 액세스 토큰 발급
      • 검증이 성공하면, 서버는 새로운 액세스 토큰을 발급하여 클라이언트에게 전달합니다. (보안을 위해 리프레시 토큰도 새로 발급해 주기도 한다.)
  1. 원래 요청 재시도
      • 클라이언트는 새로 발급받은 액세스 토큰으로 (3)번에서 실패했던 API 요청을 다시 시도한다.
      • 서버는 유효한 토큰을 확인하고 정상적으로 데이터를 응답한다.
 
사용자는 중간에 로그인이 끊기는 것을 전혀 느끼지 못하고(편의성), 실제 인증에 사용되는 액세스 토큰은 30분마다 교체된다(보안성).
 
특징
액세스 토큰 (Access Token)
리프레시 토큰 (Refresh Token)
목적
API 요청 시 본인 인증
액세스 토큰 재발급
유효 기간
매우 짧음 (예: 30분 ~ 1시간)
매우 긺 (예: 7일 ~ 30일)
전송 빈도
API 요청 시 매번 전송
액세스 토큰 만료 시에만 전송
탈취 시 위험
낮음 (금방 만료됨)
높음 (새 액세스 토큰 발급 가능)
저장 위치
(상대적) 노출 위험 O
매우 안전한 곳 (예: HttpOnly 쿠키)
서버 저장
필요 없음 (Stateless)
필수 (DB에 보관하여 검증/무효화)