Skip to content

Browser Storage

🧩 Reference

쿠키 (Cookie)

  • 서버가 브라우저에 사용자에 대한 작은 데이터 조각을 저장해두는 방식.
  • 브라우저는 이후 같은 도메인으로 요청을 보낼 때 이 쿠키를 자동으로 함께 전송한다.

세션 (Session)

  • 서버가 메모리에 이 사용자는 누구인지에 대한 상태(세션 데이터)를 들고 있고,
  • 브라우저에는 그 세션을 가리키는 세션 ID만 쿠키로 저장하는 방식의 인증 모델.

토큰 (Token)

  • 서버가 발급하는 “서명된 인증 정보 뭉치”.
  • 보통 이 토큰을 들고 다니면서, 매 요청마다 Authorization 헤더 등에 직접 실어 보내 인증한다.

JWT (JSON Web Token)

  • 토큰 포맷의 한 종류로,
    header.payload.signature 구조를 가진 JSON 기반 서명 토큰.
  • payload 안에 유저 ID, 만료 시간 같은 정보를 담고,
    서버는 이 토큰이 위조되지 않았는지만 검증하고 세션은 따로 들고 있지 않아도 된다.

🧩 아래 정리를 하고 깨달은 점!

  • RefreshToken을 왜 localStorage에 저장하면 위험한지
    → localStorage는 JS로 읽혀서 XSS에 그대로 털리기 때문.
  • httpOnly 쿠키가 왜 XSS에 안전한지
    → JS 접근이 불가능해 스크립트가 떠도 쿠키 값을 읽을 수 없기 때문.
  • localStorage vs sessionStorage vs memory
    → 모두 JS 접근 가능하지만, 보존 기간·범위가 달라서 용도에 맞게 선택해야 한다.
  • AccessToken을 왜 Authorization 헤더에 실어야 하는지 → 쿠키 자동 전송을 피하고, 명시적으로 인증 정보를 보내 CSRF 위험을 줄이기 위해서.
  • JWT 인증은 HTTP/HTTPS 규약 위에서 움직인다는 점
    → 토큰 전달·쿠키 저장·401 응답·재발급 흐름은 모두 HTTP 프로토콜 규칙에 따른 동작이다.

웹 인증을 이해하려면, 먼저 브라우저가 어떤 저장소들을 제공하는지 확실히 알아야 한다!

브라우저에는 크게 네 가지 저장소가 있다:

  • Memory (JS 메모리)
  • LocalStorage
  • SessionStorage
  • Cookie

Memory (애플리케이션 메모리)

메모리란 브라우저 탭이 열려 있는 동안, JS 런타임이 들고 있는 휘발성 데이터 영역이다.
React state, Zustand, context 등에 저장되는 모든 데이터는 메모리에 있다.

특징

  • 새로고침하면 사라진다.
  • XSS에 취약하다. (JS가 읽을 수 있으므로)
  • 속도는 가장 빠르다.
  • 브라우저 탭을 닫으면 사라진다.
  • 서버로 자동 전송되지 않는다. (쿠키와 다름)

JWT에서 어떻게 쓸까?

AccessToken을 메모리에 넣는 서비스가 많다.

  • AT는 짧게 살아서 털려도 피해 작다.
  • JS에서 관리해도 된다.
  • 자동 전송되지 않아 CSRF 위험이 없다.
  • 새로고침해도 RefreshToken이 쿠키에 있으니 재발급하면 된다.

즉, AccessToken은 메모리에 저장해도 괜찮다.


LocalStorage

도메인별로 키-값 쌍을 영구적으로 저장하는 브라우저 내장 저장소이다.
브라우저를 꺼도 남아 있고, JS에서 localStorage API로 읽고 쓸 수 있다.

특징

  • 브라우저를 꺼도 유지된다.(영구 저장)
  • 저장할 수 있는 용량이 크다.
  • JS로 접근 가능 → XSS 공격에 의해 값이 그대로 유출될 수 있다.
  • 서버로 자동 전송되지 않음 (쿠키와 다름)

언제 사용할까?

  • 사용자 설정(테마, 언어, UI 상태)
  • 로그인 여부(Boolean만)
  • 간단한 캐시 등

JWT에서는?

AccessToken을 여기에 저장하는 서비스도 있지만 보안 관점에서는 비추천이다.

  • XSS 공격에서 localStorage는 바로 털린다. = AccessToken 탈취
  • 매우 위험하지는 않지만(AT만 털리면 재발급 가능하니까) 그래도 좋지는 않다.

추가로, localStorage는 브라우저를 꺼도 계속 남아 있는 영구 저장소라서,
원래 짧게 쓰고 버려야 할 AccessToken이 불필요하게 오래 살아남는다는 점도 위험 요소다.
→ 토큰이 유효 기간 동안 브라우저에 계속 남아 있으니, 탈취 가능 기간도 함께 길어진다.

💡

AccessToken = localStorage 가능하나 XSS 취약
RefreshToken = 절대 localStorage 금지


SessionStorage

localStorage와 비슷하지만 브라우저 탭(세션) 단위로 유지되는 키-값 저장소이다.
구조는 localStorage와 같지만, 탭을 닫으면 함께 사라진다.

특징

  • 탭 닫으면 날아간다.
  • 새로고침해도 유지된다.
  • JS 접근 가능 → XSS 취약하다.

JWT에서 어떻게 쓸까?

JWT에서는 sessionStorage 거의 쓰지 않는다.


HTTP 요청/응답 헤더를 통해 서버와 브라우저가 주고받는 작은 텍스트 조각이다.
브라우저가 자동으로 저장·관리하며, 같은 도메인으로 요청을 보낼 때 Cookie 헤더에 자동으로 실어 보낸다.

쿠키는 브라우저가 서버 말을 듣고 직접 관리하므로 프론트에서 쿠키 저장을 하지 않아도 된다.


서버가 쿠키를 저장하는 과정

http
Set-Cookie: refresh_token=abc123; HttpOnly; Secure; SameSite=Strict

이렇게 서버가 응답을 내려주면:

  • 브라우저가 쿠키를 자동으로 저장한다.
  • 같은 도메인으로 요청을 보낼 때 자동으로 Cookie 헤더에 실려 나간다.
http
Cookie: refresh_token=abc123;

쿠키 속성들

속성설명예시
httpOnlytrue/falseJS 접근 차단 → XSS 방어HttpOnly
Securetrue/falseHTTPS에서만 전송Secure
SameSiteStrict/Lax/NoneCSRF 방지 수준 조절SameSite=Strict
Domain도메인명쿠키 적용 도메인 범위Domain=.example.com
Path경로쿠키 적용 경로 범위Path=/api
Max-Age쿠키 수명 (초 단위)Max-Age=604800 (7일)
Expires날짜쿠키 만료 절대 시간Expires=Wed, 21 Oct 2025

특히 Path/auth/api/auth처럼 최대한 좁게 설정하면,
쿠키가 전송되는 요청 범위를 줄일 수 있어서 공격 표면을 줄이는 데 도움이 된다.

SameSite 상세:

  • Strict: 완전 동일 사이트만 → 가장 안전, UX 불편
  • Lax: GET + 탑레벨 네비게이션 허용 → 기본값, 균형적
  • None: 모든 크로스사이트 허용 → Secure 필수

일반 쿠키 vs httpOnly 쿠키 비교

구분Cookie (일반)Cookie (httpOnly)
JS 접근 가능 여부O (document.cookie)X (JS 접근 불가)
XSS 공격매우 취약안전
CSRF 위험동일 (둘 다 자동 전송)동일
JWT에 적합?부적합RefreshToken 저장 표준 방식
서버 제어 가능?OO

왜 RefreshToken은 쿠키여야 할까?

1. 기본적으로 자동 전송이 필요하다.

RefreshToken은 서버가 클라이언트 신원을 확인하는 용도다.
이걸 프론트에서 직접 꺼내서 붙이는 구조로 만들면 안된다.

쿠키는 자동 저장, 자동 전송, JS 접근 불가
이 특징 때문에 RefreshToken을 넣기 최적화된 저장 공간이다.

2. XSS로부터 보호해야 한다.

localStorage / sessionStorage / 메모리는 JS로 접근 가능 → XSS 공격에 취약
httpOnly 쿠키는 JS로 읽을 수 없다.

3. RefreshToken 탈취 = 계정 영구 장악

RT가 재발급 토큰이므로 털리면 세션 영구 장악이다.
→ RT는 절대 노출되면 안 되므로 httpOnly + Secure + SameSite 쿠키를 사용.


Memory vs LocalStorage 저장 전략

AccessToken 저장 위치 선택 기준:

기준MemoryLocalStorage
보안(XSS)위험매우 위험
CSRF안전안전
새로고침사라짐유지됨
UX 편의성낮음높음
권장 여부권장조건부 가능

정리 :

  • 최대 보안 = Memory
  • 편의성 중시 = LocalStorage

세션 기반 인증 vs JWT 인증

구분세션 기반JWT 기반
서버 저장소세션 저장소 필요무상태(stateless)
확장성낮음높음 (분산 서버에서 좋음)
토큰 크기작음 (세션 ID)큼 (header+payload+signature)
RT 필요 여부필요 없음필요 (재발급용)
보안 수준높음구현에 따라 달라짐

브라우저 저장소 용량 제한

저장소용량 제한추가 제한사항
Memory브라우저 메모리에 따라탭 닫으면 사라짐
LocalStorage5-10MB (브라우저별 상이)도메인당 제한
SessionStorage5-10MB탭별 독립적
Cookie4KB/개, 50개/도메인모든 요청에 자동 전송

JWT RefreshToken 주의점:

  • JWT가 길면 쿠키 4KB 초과 가능
  • 이때는 JWT 대신 세션 ID 방식 고려

정리

구분MemoryLocalStorageSessionStorageCookie (httpOnly)
유지 기간X (탭 종료 시 삭제)O (브라우저 꺼도 유지)X (탭 닫으면 삭제)O (만료 시간까지 유지)
새로고침 영향X (사라짐)O (유지됨)O (유지됨)O (유지됨)
브라우저 자동 전송XXXO (Cookie 헤더로 자동 전송)
JS 접근 가능 여부O (접근 가능 → XSS 취약)O (접근 가능 → XSS 매우 취약)O (접근 가능 → XSS 취약)X (접근 불가 → XSS 안전)
사용 용도AccessToken, 앱 상태사용자 설정, 캐시, UI 상태탭 기반 임시 데이터RefreshToken, 인증 유지
보안 위협 모델XSS 취약XSS 매우 취약XSS 취약XSS 안전 / CSRF 고려 필요
서버가 통제 가능?XXXO (서버가 Set-Cookie로 관리)
적합한 JWT 역할AccessToken(추천)AccessToken(조건부)거의 사용 XRefreshToken(표준)

💡 저장소 선택 기준 요약

JWT 인증에서 저장소를 선택할 때 핵심 기준은 다음 네 가지다:

  • XSS 보호가 필요한가?
    → RefreshToken처럼 절대 노출되면 안 되는 값은 httpOnly Cookie

  • 자동 전송이 필요한가?
    → 서버가 알아서 확인해야 하는 값은 Cookie

  • 새로고침해도 유지해야 하나?
    → 오래 살아야 하는 값은 LocalStorage,
    → 짧게만 유지해도 되는 값은 Memory

  • CSRF 보호가 필요한가?
    → Cookie 쓰면 SameSite/CSRF 토큰을 반드시 고려해야 함

결론:

  • AccessToken → Memory(권장) / LocalStorage(조건부)
  • RefreshToken → 반드시 httpOnly Cookie