Web Storage의 동작 원리와 보안
Reference
1. Web Storage란?
웹 스토리지는 브라우저 내부에 데이터를 저장하기 위한 API이다. 대표적으로 다음 4가지 방식이 있다.
저장소 | 수명 | 저장 위치 | 크기 제한 | 특징 |
---|---|---|---|---|
localStorage | 브라우저를 닫아도 남음 | 브라우저 로컬 | 약 5~10MB | 문자열만 저장, 도메인 단위 격리 |
sessionStorage | 탭 종료 시 삭제 | 브라우저 세션 | 약 5MB | 세션(탭) 단위 저장 |
cookie | 수명 설정 가능 | 서버와 자동 전송 | 약 4KB | 서버 통신 시 헤더로 전송됨 |
IndexedDB | 영구 저장 | 브라우저 DB | 수백MB~GB | 구조화된 데이터 저장 가능 |
2. Web Storage의 동작 원리 — same-origin policy
출처가 다르면 완전히 다른 저장소처럼 취급된다.
웹 스토리지는 도메인(origin) 단위
로 격리되어 있다. 즉, https://example.com
에서 저장한 데이터는 https://another.com
이나 http://example.com
(http/https 차이)에서는 접근할 수 없다.
https://example.com 접근 가능
https://example.com:3000 포트가 다르면 다른 origin
http://example.com 프로토콜이 다르면 다른 origin
https://sub.example.com 서브도메인은 다른 origin
브라우저를 바꾸거나 기기를 옮기면 물리 저장소가 달라져 데이터가 존재하지 않는다.
3. localStorage의 구조와 직렬화
모든 데이터는 문자열 형태로 저장된다.
객체를 그대로 저장하면 [object Object]
로 변환되므로, 반드시 JSON.stringify()
를 이용해야 한다.
localStorage.setItem("user", JSON.stringify({ name: "Binny" }));
const user = JSON.parse(localStorage.getItem("user"));
4. localStorage의 한계 (성능/기능 측면)
localStorage는 HTML5 초창기, 빠른 접근
을 목표로 만들어졌기 때문에 여러 제약이 있다.
- 문자열만 저장 가능 → 객체는
JSON.stringify()
로 변환 필요 - 동기적 API → 대용량 데이터 처리 시 메인 스레드를 블로킹
- 보안 취약성 → XSS(스크립트 주입 공격)에 노출 가능
- 용량 제한 → 브라우저마다 다르지만 5~10MB 수준
즉, localStorage는 빠른 임시 저장소로는 유용하지만, 안전하거나 대용량을 다루기엔 적합하지 않다. 복잡한 데이터에는 IndexedDB가 권장된다.
5. localStorage vs cookie
구분 | localStorage | cookie |
---|---|---|
저장 위치 | 브라우저 로컬 | 브라우저 + 서버 |
용량 | 약 5~10MB | 약 4KB |
만료 | 수동 삭제 전까지 | 만료일로 자동 삭제 |
접근 방식 | JS에서 접근 가능 | JS 접근 제한 가능 (HttpOnly ) |
전송 여부 | 서버에 자동 전송 | 요청 시 자동 전송 |
주요 용도 | UI 상태, 임시 데이터 | 인증/세션 관리 |
쿠키는 서버와 자동으로 주고받는 데이터, localStorage
는 클라이언트 전용 저장소라는 게 가장 큰 차이다.
6. 보안 이슈 — localStorage는 안전하지 않다.
localStorage는 JavaScript로 접근 가능한 공간이다.
만약 XSS
공격이 발생하면 공격자가 데이터를 그대로 읽을 수 있다.
// 예: 악성 스크립트 삽입
console.log(localStorage.getItem("access_token"));
그래서 로그인 토큰을 localStorage에 저장하는 건 매우 위험하다.
안전한 쿠키 설정
HttpOnly
: JavaScript 접근 차단Secure
: HTTPS 연결에서만 전송SameSite
: 교차 사이트 요청 제한
localStorage는 XSS 공격에, 쿠키는 CSRF 공격에 취약하다.
두 공격을 모두 완화하려면 HttpOnly
와 SameSite
옵션을 함께 설정하는 것이 좋다.
7. IndexedDB - 구조화된 데이터 저장소
오프라인 지원, 대용량 상태 저장, 캐시용으로 사용되는 브라우저 내 데이터베이스
- 비동기 API로 작동해 메인 스레드를 블로킹하지 않음
- 객체 단위 저장 가능 (JSON 구조 그대로)
- 트랜잭션 기반으로 데이터 일관성 유지
- PWA / 오프라인 캐시 / 이미지 저장 등에 자주 사용됨
IndexedDB는 SQL이 아닌 Key-Value 기반 DB
이며,
Service Worker와 함께 사용하면 네트워크 끊김에도 데이터 유지가 가능하다.
8. IndexedDB의 구조와 동작
Database (DB)
├─ Object Store (테이블 유사)
│ ├─ Record (key-value 데이터)
│ └─ Index (검색 속성)
└─ Transaction (읽기/쓰기 단위)
Database
: 전체 스토리지 컨테이너Object Store
: 데이터를 저장하는 실제 공간 (테이블 유사)Record
: key-value로 저장된 개별 데이터Index
: 특정 필드 기준으로 빠르게 검색 가능Transaction
: 데이터 조작 시 무결성을 보장
기본 사용 예시
const request = indexedDB.open("MyDB", 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
db.createObjectStore("users", { keyPath: "id" });
};
request.onsuccess = (event) => {
const db = event.target.result;
const tx = db.transaction("users", "readwrite");
const store = tx.objectStore("users");
store.put({ id: 1, name: "Binny" });
};
- 모든 작업은 트랜잭션 내부에서만 수행되며, 도중 오류 시 자동 복구된다.
9. localStorage vs IndexedDB
항목 | localStorage | IndexedDB |
---|---|---|
API | 동기적 | 비동기적 |
구조 | 단일 key-value | Object Store + Index |
데이터 | 문자열만 저장 | 객체 형태 그대로 저장 |
성능 | 빠르지만 제한적 | 대용량 데이터 처리 가능 |
보안 | XSS에 취약 | 동일 출처 정책 동일 |
10. Zustand persist와 IndexedDB
Zustand의 persist 미들웨어는 기본적으로 localStorage를 사용하지만,
옵션으로 storage 옵션을 커스터마이징하여 IndexedDB 기반 저장소로 변경할 수 있다.
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";
const useStore = create(
persist(
(set) => ({
items: [],
addItem: (item) => set((s) => ({ items: [...s.items, item] })),
}),
{
name: "items-db",
storage: createJSONStorage(() => indexedDBStorage),
}
)
);
💡 indexedDBStorage는 Zustand가 기본 제공하지 않는다.
직접 IndexedDB 접근 로직을 구현하여 전달해야 한다.
11. Zustand persist의 동작 원리
const useResultStore = create(
persist(
(set) => ({
results: [],
addResult: (r) => set((s) => ({ results: [...s.results, r] })),
}),
{ name: "quiz-results" } // localStorage key
)
);
- persist는 상태를 JSON으로 직렬화해 저장/복원하는 단순 미들웨어다.
암호화나 보안 기능은 포함되어 있지 않으므로, 민감한 데이터 저장에는 부적합하다.
정리
선택 기준 | 추천 저장소 | 이유 |
---|---|---|
가벼운 상태 저장 (UI, 캐시) | localStorage | 빠르고 단순 |
세션 단위 데이터 | sessionStorage | 탭별로 격리 |
인증/보안 관련 데이터 | Cookie (HttpOnly) | 서버 통신 시 안전 |
오프라인/대용량 저장 | IndexedDB | 구조화된 데이터 저장 가능 |
전역 상태 유지 (클라이언트) | Zustand persist | localStorage/IndexedDB 연동 가능 |