다다의 개발일지 6v6

🔐JWT(JSON Web Token) 토큰에 대해 + vs 세션 기반 인증 본문

Computer Science/네트워크

🔐JWT(JSON Web Token) 토큰에 대해 + vs 세션 기반 인증

dev6v6 2025. 2. 2. 00:51

JWT (JSON Web Token) 토큰이란?

JWT 웹 애플리케이션에서 인증정보 교환을 위해 사용하는 토큰이다.

사용자의 로그인 상태를 유지하거나, 정보를 안전하게 주고받는 데 사용된다!

  • JWT는 로그인 후 서버가 생성하는 디지털 서명된 토큰으로,
  • 사용자가 로그인할 때마다 세션을 유지하지 않고도 신원을 확인할 수 있음

 

JWT 토큰의 특징

  • JSON 형식으로 데이터 저장
  • 자체적으로 인증 정보를 포함 (서버가 세션을 저장할 필요 없음 → Stateless -> 보안 강화)
  • 디지털 서명 포함 → 변조 불가능
  • 주로 로그인 인증(Authentication)에 사용

 

JWT의 구조 (3가지 부분으로 구성됨)

JWT는 .(점)으로 구분된 3가지 부분으로 이루어져 있음.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJ1c2VySWQiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

 

1️⃣ Header (헤더)

JWT의 타입과 해싱 알고리즘을 정의

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg: 사용할 해싱 알고리즘 (예: HS256, RS256)
  • typ: 토큰 타입 (JWT)

2️⃣ Payload (페이로드)

사용자 정보(클레임, Claims)를 포함하는 부분

{
  "userId": "123",
  "role": "admin",
  "exp": 1712345678
}
  • userId: 사용자 ID
  • role: 사용자 역할 (예: admin, user)
  • exp: 토큰 만료 시간 (Unix Timestamp)

Payload에는 중요한 정보(비밀번호, 카드번호 등)를 넣으면 안 됨!
Base64 인코딩이므로 쉽게 디코딩 가능 → 보안에 주의해야 함

3️⃣ Signature (서명)

서버가 서명한 정보로 JWT의 무결성을 보장

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret_key
)
  • Header + Payload 를 조합해 해시 생성
  • 비밀키(Secret Key)로 서명하여 변조 방지

-> 서명이 다르면 토큰이 위변조되었음을 감지할 수 있음

 

JWT 인증 방식 (로그인 과정)

  1. 사용자가 로그인 요청 (POST /login)
  2. 서버가 로그인 정보 확인 후 JWT 생성
  3. 클라이언트(브라우저, 모바일 등)에 JWT 전달
  4. 클라이언트가 API 요청 시 JWT를 함께 전송
  5. 서버는 JWT를 검증하여 요청 허용

이때 JWT는 어디에 저장되어 있을까

JWT는 로그인 후 클라이언트(브라우저, 모바일 앱 등)에 저장됨.
저장 위치는 보안과 편의성에 따라 선택할 수 있으며, 주로 로컬 스토리지(Local Storage), 세션 스토리지(Session Storage), 쿠키(Cookie).

더보기

🔍 JWT 저장 위치 3가지

 

Local Storage 브라우저 내장 저장소 (window.localStorage) 사용이 간편, 브라우저 종료 후에도 유지 XSS 공격에 취약
Session Storage 브라우저 내장 저장소 (window.sessionStorage) 브라우저가 종료되면 자동 삭제 XSS 공격에 취약
Cookie (HTTPOnly) 클라이언트-서버 간 자동 전송 XSS 공격 방어 가능, 보안성 높음 CSRF 공격 위험

1. Local Storage (window.localStorage)

localStorage.setItem("token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");
const token = localStorage.getItem("token");

장점: 브라우저 종료 후에도 유지됨
단점: XSS(크로스 사이트 스크립팅) 공격에 취약 (악성 스크립트가 JWT를 탈취할 수 있음)
보안 취약점 때문에 권장되지 않음

2. Session Storage (window.sessionStorage)

sessionStorage.setItem("token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");
const token = sessionStorage.getItem("token");

장점: 브라우저가 닫히면 자동 삭제됨 → 보안성 Local Storage보다 높음
단점: XSS 공격에 여전히 취약함
탭을 닫으면 로그인이 풀릴 수 있음

3. Cookie (document.cookie)

document.cookie = "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...; Secure; HttpOnly; SameSite=Strict";

장점:

  • HttpOnly 옵션을 사용하면 JavaScript에서 접근 불가능 → XSS 공격 방어 가능
  • Secure 옵션을 사용하면 HTTPS에서만 전송
  • SameSite=Strict 옵션을 설정하면 CSRF 공격 방어

단점:

  • 기본적으로 CSRF(Cross-Site Request Forgery) 공격에 취약
  • 서버에서 쿠키 설정 필요

그럼 어디에 저장하는 게 가장 좋을까?

  1. 보안이 중요한 경우 → HttpOnly + Secure Cookie (최고의 보안)
  2. 간단한 토큰 저장이 필요한 경우 → Session Storage (보안은 Local Storage보다 나음)
  3. 편의성을 중시하는 경우 → Local Storage (하지만 보안 취약!)

추천 방법 JWT + Refresh Token (토큰 갱신)

  • JWT는 만료 시간이 지나면 더 이상 유효하지 않음
  • 이를 해결하기 위해 Refresh Token을 사용
  1. Access Token(JWT)은 짧은 유효기간 (예: 15분) 설정
  2. Refresh Token은 긴 유효기간 (예: 7일~30일) 설정
  3. JWT 만료 시 Refresh Token으로 새로운 JWT 발급
  4. Refresh Token도 만료되면 다시 로그인 필요

즉, JWT를 쿠키(HttpOnly, Secure)로 저장하고, Refresh Token을 사용해 Access Token을 갱신하는 방식이 가장 안전함

 

JWT 사용 과정

1️⃣ 로그인 요청 (JWT 생성)

클라이언트 → 서버에 로그인 요청

POST /login HTTP/1.1
Content-Type: application/json

{
  "username": "hong",
  "password": "1234"
}

 

서버 응답 (JWT 발급)

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

 

2️⃣ API 요청 시 JWT 포함

클라이언트 → 서버에 API 요청 시 JWT를 Authorization 헤더에 포함

GET /profile HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

 

서버는 JWT를 검증 후 요청을 처리

 

JWT의 장점

Stateless (무상태성) 서버가 세션을 저장할 필요 없음 → 확장성 증가
빠른 인증 처리 세션 조회 없이 토큰만 확인하면 됨
다양한 플랫폼에서 사용 가능 모바일, 웹, 서버 간 인증에 활용
변조 방지 디지털 서명이 포함되어 무결성 보장

Stateless가 확장성이 우수한 이유

  1. 서버가 세션 데이터를 저장하지 않음 → 서버 부담이 적어짐
  2. 서버 간 상태 공유가 필요 없음 → 여러 서버에서 쉽게 확장 가능
  3. 로드 밸런서가 어떤 서버로 요청을 보내도 동일한 방식으로 처리 가능

JWT의 단점과 보안 이슈

Payload가 디코딩 가능 중요한 정보(비밀번호 등)를 절대 넣지 말 것
토큰 탈취 시 보안 위험 HTTPS 사용 + 짧은 만료 시간 설정
토큰 크기가 큼 헤더 부담이 클 경우 압축 (ex: Brotli)
토큰이 만료되면 갱신 필요 Refresh Token을 활용

 

 

JWT vs. 세션 기반 인증

서버 저장 필요? ❌ 없음 (Stateless) ✅ 있음 (Stateful)
확장성 ✅ 높음 (서버 부담 적음) ❌ 낮음 (서버 메모리 부담)
속도 ✅ 빠름 (DB 조회 불필요) ❌ 느림 (세션 조회 필요)
보안 ⚠️ 탈취 시 위험 ✅ 비교적 안전

 

좀 더 자세히 알아보기

더보기

세션이란?

 

세션(Session) 은 사용자의 로그인 상태와 같은 데이터를 서버가 저장하는 방식.
사용자가 웹사이트에 로그인하면 서버가 사용자를 식별할 수 있도록 데이터를 유지하는 공간이다.

 

🔹 세션 기반 인증 방식의 동작 과정

  1. 사용자가 로그인하면 서버에서 세션 ID를 생성
  2. 해당 세션 ID를 서버에 저장하고, 클라이언트(브라우저)에는 쿠키에 세션 ID 저장
    -> 쿠키에 세션 ID를 탈취당하면 똑같이 보안에 취약해서 얘도 쿠키의 HttpOnly, Secure 속성을 사용해 보안 강화 해줘야 함. ( 간단한 보안 처리 )
  3. 이후 요청에서 클라이언트가 세션 ID를 서버로 전송
  4. 서버는 세션 ID를 확인하여 사용자를 식별

 장점

  • 사용자의 로그인 상태를 서버가 직접 관리 → 보안성이 높음

 단점

  • 서버가 세션을 저장해야 하므로 메모리 사용량 증가
  • 로드 밸런싱이 어려움 (세션 정보가 특정 서버에만 저장됨)

Stateless란?

Stateless 란 서버가 클라이언트의 상태(로그인 정보 등)를 저장하지 않는 방식
즉, 각 요청은 독립적이며 서버는 요청을 받을 때마다 사용자를 새롭게 인증해야 한다.

 

🔹 JWT 기반 인증 (Stateless) 과정

  1. 사용자가 로그인하면 서버가 JWT 토큰을 생성하여 클라이언트에 전달
  2. 이후 요청에서 클라이언트는 JWT 토큰을 함께 전송
  3. 서버는 JWT를 검증하고, 내부 데이터(DB)와 비교하여 사용자 인증 수행

 장점

  • 서버가 세션을 저장하지 않으므로 확장성이 뛰어남
  • 여러 서버가 존재해도 클라이언트가 직접 JWT를 보내므로 부하가 분산됨
  • 로드 밸런싱이 쉬워짐

 단점

  • JWT 자체가 사용자의 정보(권한, ID 등)를 포함하기 때문에 토큰이 탈취되면 위험
  • 토큰의 유효기간이 끝날 때까지 유효 → 강제 로그아웃이 어려움

 

세션 기반 인증에서는 서버가 사용자의 세션 정보를 직접 저장하기 때문에 부하가 증가함.
대규모 시스템에서는 수많은 사용자의 세션을 관리해야 하므로, 서버의 메모리(RAM)와 저장소(디스크)가 부족해질 가능성이 크다.

-> 반면, JWT 기반 인증(Stateless)은 서버가 세션을 저장할 필요가 없음.
-> 즉, 어떤 서버에서 요청을 받아도 동일하게 처리할 수 있으므로 확장성이 뛰어남.

 

🔹 예를들어 로드 밸런싱 상황

 세션 기반 인증:

  • 사용자가 로그인하면 서버 A에 세션이 저장
  • 이후 사용자가 요청을 보냈을 때 서버 B가 요청을 처리하면 세션이 없어서 로그인이 풀리는 문제 발생
  • 따라서 세션을 공유하기 위해 Sticky Session(한 사용자의 요청을 특정 서버로 고정) 또는 세션 저장소(Redis 등) 사용이 필요

 JWT 기반 인증(Stateless):

  • 클라이언트가 요청할 때마다 JWT 토큰을 함께 전송
  • 서버가 요청을 받을 때마다 토큰을 검증하여 사용자 인증
  • 어느 서버에서 요청을 받아도 동일한 방식으로 처리 가능 → 로드 밸런싱이 쉬워지고 확장성이 향상됨