폼 검증을 통과한 데이터는 저장되어야 가치가 생깁니다. 브라우저가 제공하는 Web Storage API는 키-값 형태로 문자열을 보관하는 내장 기능입니다. 이번 글은 개념 → 코드 → 이유 순으로 localStorage, sessionStorage를 다룹니다.
이번 글에서 새로 나오는 용어
- Web Storage API: 브라우저 안에 있는 간단한 저장소 세트로, 키-값 형태의 문자열을 보관합니다.
- localStorage: 같은 사이트를 다시 방문해도 남아 있는 영구 저장소입니다.
- sessionStorage: 현재 탭이 열려 있는 동안만 살아 있는 임시 저장소입니다.
- JSON.stringify/parse: 객체·배열을 문자열로 바꾸고 다시 복원하는 함수 쌍입니다.
- TTL(Time To Live): 데이터를 저장할 때 만료 시간을 함께 기록해, 시간이 지나면 직접 삭제하는 방식입니다.
핵심 개념
- localStorage: 동일 출처(origin) 전체에 적용되는 영구 저장소입니다. 사용자가 직접 지우거나 코드에서 삭제할 때까지 남습니다.
- sessionStorage: 탭 단위로 살아있는 임시 저장소입니다. 탭을 닫으면 사라져 초안이나 임시 필터 값에 적합합니다.
- JSON 직렬화: 저장소는 문자열만 저장하므로 객체·배열을
JSON.stringify로 저장하고JSON.parse로 복원합니다. - storage 이벤트: 같은 출처의 다른 탭에서
localStorage값이 바뀌면 자동으로 발생하는 이벤트입니다. 실시간 동기화에 사용합니다. - 만료 시간(TTL): Web Storage에는 만료 개념이 없으니 직접
{ value, expiresAt }구조를 저장해 만료를 관리합니다.
코드로 확인하기
const STORAGE_KEY = "starter-dashboard";
function loadState(defaultValue) {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return defaultValue;
return JSON.parse(raw);
} catch (error) {
console.error("저장소 파싱 실패", error);
return defaultValue;
}
}
function saveState(state) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
}
const initial = loadState({ todos: [], theme: "light" });
const state = { ...initial };
function update(partial) {
Object.assign(state, partial);
render();
saveState(state);
}
window.addEventListener("storage", (event) => {
if (event.key !== STORAGE_KEY || !event.newValue) return;
const next = JSON.parse(event.newValue);
Object.assign(state, next);
render();
});
const formDraftKey = "signup-draft";
function saveDraft(event) {
const formData = new FormData(event.currentTarget);
const draft = Object.fromEntries(formData.entries());
sessionStorage.setItem(formDraftKey, JSON.stringify(draft));
}
function restoreDraft(form) {
const raw = sessionStorage.getItem(formDraftKey);
if (!raw) return;
const draft = JSON.parse(raw);
Object.entries(draft).forEach(([name, value]) => {
const input = form.elements.namedItem(name);
if (input instanceof HTMLInputElement) input.value = value;
});
}
const form = document.querySelector(".signup-form");
form?.addEventListener("input", debounce(saveDraft, 400));
if (form) restoreDraft(form);
function setWithExpiration(key, value, ttlMs) {
const payload = { value, expiresAt: Date.now() + ttlMs };
localStorage.setItem(key, JSON.stringify(payload));
}
function getWithExpiration(key) {
const raw = localStorage.getItem(key);
if (!raw) return null;
const payload = JSON.parse(raw);
if (Date.now() > payload.expiresAt) {
localStorage.removeItem(key);
return null;
}
return payload.value;
}
왜 중요한가
localStorage를 붙이면 새로고침이나 브라우저 재시작 후에도 사용자 데이터가 유지되어 과제용 프로토타입을 신뢰할 수 있습니다.sessionStorage초안 저장은 가입 폼이나 설문지를 잠시 떠났다가 돌아왔을 때 입력값을 복원해 줍니다.storage이벤트와 TTL 패턴은 같은 계정을 여러 탭에서 사용할 때 화면이 엇갈리지 않도록 돕습니다.
실습
- 따라 하기: Todo 상태에
loadState,saveState를 연결해 새로고침 후에도 데이터가 남는지 확인합니다. - 확장하기:
sessionStorage로 폼 초안을 저장하고, 두 번째 탭에서는storage이벤트로 리스트가 실시간 갱신되게 만듭니다. - 디버깅: JSON 파싱 오류나 용량 초과를 고의로 발생시키고, 에러 로그를 남긴 뒤 기본값으로 안전하게 복원합니다.
- 완료 기준: 저장 버튼 없이도 상태 변경 시 자동으로 보존되고, 다른 탭에서 값을 바꿨을 때 렌더가 즉시 재실행되면 실습이 끝납니다.
마무리
브라우저 저장소는 “작은 서버” 같은 역할을 해 줍니다. 다음 글에서는 저장된 데이터를 기반으로 이벤트 루프와 debounce/throttle 제어기를 연결해 반응성을 높여 보겠습니다.
💬 댓글
이 글에 대한 의견을 남겨주세요