기술 블로그 작성사용자설정 Gem기술 블로그 작성님이 보낸 내용브라우저의 기억 장치, LocalStorage 완벽 가이드: 데이터 지속성의 핵심

현대 웹 애플리케이션은 단순히 정보를 보여주는 단계를 넘어, 사용자의 상태를 기억하고 개인화된 경험을 제공하는 방향으로 진화했습니다. 사용자가 다크 모드를 설정했는지, 장바구니에 무엇을 담았는지, 혹은 작성 중이던 게시글이 있는지 브라우저는 기억해야 합니다.
과거에는 이러한 정보를 저장하기 위해 서버로 매번 요청을 보내거나, 용량이 작고 매 요청마다 서버로 전송되어 오버헤드를 일으키는 **쿠키(Cookie)**에 의존했습니다. 하지만 HTML5의 등장과 함께 탄생한 LocalStorage는 브라우저 측에 더 큰 데이터를 안전하고 효율적으로 저장할 수 있는 길을 열어주었습니다.
💡 핵심 개념 Deep Dive: 왜 LocalStorage인가?
LocalStorage는 Web Storage API의 한 종류로, 브라우저에 키-값(key-value) 쌍을 저장할 수 있는 메커니즘입니다.
1. 작동 원리와 특징
LocalStorage의 가장 큰 특징은 **영속성(Persistence)**입니다. 세션 스토리지(SessionStorage)가 탭을 닫으면 데이터가 날아가는 것과 달리, LocalStorage는 사용자가 직접 삭제하거나 브라우저 캐시를 비우지 않는 한 컴퓨터를 재부팅해도 데이터가 유지됩니다.
- 도메인 종속성: 데이터는 프로토콜(http/https)과 도메인별로 격리됩니다. a.com에서 저장한 데이터는 b.com에서 접근할 수 없습니다.
- 용량: 일반적으로 도메인당 약 5MB ~ 10MB의 저장 공간을 제공하며, 이는 쿠키(4KB)에 비해 압도적으로 큽니다.
- 동기적 실행: 모든 작업이 메인 스레드에서 동기적으로 이루어지므로, 너무 큰 데이터를 빈번하게 읽고 쓰면 UI 렌더링에 영향을 줄 수 있습니다.
2. 비유로 이해하기: 사무실 서랍 vs 포스트잇
- **쿠키(Cookie)**가 상대방에게 전달하기 위해 등에 붙인 포스트잇이라면, (매번 서버로 전송됨)
- LocalStorage는 내 책상 아래에 있는 개인 서랍과 같습니다. 퇴근하고 내일 출근해도 물건은 그대로 있고, 굳이 남에게 보여줄 필요 없이 나만 꺼내 쓰면 됩니다.
🛠️ 실전 Hands-on: "최근 본 상품" 기능 구현하기
이커머스 서비스에서 사용자가 최근 구경한 상품 리스트를 서버 DB 없이 로컬에 저장하여 보여주는 기능을 구현해 보겠습니다. LocalStorage는 오직 **문자열(String)**만 저장할 수 있다는 점에 유의하며 코드를 작성합니다.
Step 1. 데이터 저장 및 직렬화 (Serialization)
객체나 배열을 저장할 때는 반드시 JSON.stringify()를 거쳐야 합니다.
/**
* 최근 본 상품 ID를 LocalStorage에 추가하는 함수
* @param {string} productId - 상품 고유 ID
*/
const addToRecentlyViewed = (productId) => {
const STORAGE_KEY = 'recently_viewed_items';
// 1. 기존 데이터 가져오기 (없으면 빈 배열)
const rawData = localStorage.getItem(STORAGE_KEY);
let itemList = rawData ? JSON.parse(rawData) : [];
// 2. 중복 제거: 이미 있으면 지우고 맨 앞으로 보냄
itemList = itemList.filter(id => id !== productId);
itemList.unshift(productId);
// 3. 개수 제한: 최근 5개만 유지
const limitedList = itemList.slice(0, 5);
// 4. JSON 문자열로 변환하여 저장
localStorage.setItem(STORAGE_KEY, JSON.stringify(limitedList));
};
Step 2. 데이터 불러오기 및 예외 처리
저장된 데이터가 오염되었을 가능성에 대비하여 try-catch로 감싸는 것이 시니어의 디테일입니다.
/**
* 저장된 최근 본 상품 리스트를 안전하게 가져오는 함수
*/
const getRecentlyViewed = () => {
try {
const data = localStorage.getItem('recently_viewed_items');
return data ? JSON.parse(data) : [];
} catch (error) {
// JSON 파싱 에러나 스토리지 접근 불가 시 빈 배열 반환
console.error("데이터 복구 실패:", error);
return [];
}
};
⚠️ 트러블슈팅: 흔히 겪는 오류와 해결책
- QuotaExceededError (용량 초과):
- 사용자의 디스크 공간이 부족하거나 설정된 한계치를 넘었을 때 발생합니다. 데이터를 저장하기 전 항상 이전 데이터를 정리하거나, 오래된 항목을 삭제하는 로직(LRU 캐시 알고리즘 등)을 도입하세요.
- 보안 이슈 (XSS):
- LocalStorage는 자바스크립트로 접근이 가능하므로 **민감한 정보(비밀번호, 개인정보, 세션 토큰 등)**를 저장해서는 안 됩니다. 공격자가 악성 스크립트를 삽입(XSS)할 경우 데이터를 그대로 탈취당할 수 있습니다.
- 데이터 타입 왜곡:
- localStorage.setItem('age', 25)라고 저장해도 다시 불러오면 "25"(문자열)가 됩니다. 산술 연산이 필요하다면 Number() 등으로 형 변환을 잊지 마세요.
⚖️ Trade-offs: LocalStorage vs IndexedDB
| 특징 | LocalStorage | IndexedDB |
| 복잡도 | 매우 낮음 (Key-Value) | 높음 (NoSQL 방식) |
| 성능 | 동기식 (작은 데이터에 적합) | 비동기식 (대용량 데이터에 적합) |
| 데이터 타입 | 문자열만 가능 | 객체, 바이너리 등 다양함 |
| 검색 | 키를 통한 전체 일치만 가능 | 인덱스를 활용한 복합 검색 가능 |
결론적으로: 설정값, 테마, 간단한 상태 유지에는 LocalStorage가 정답이지만, 수천 개의 데이터를 오프라인에서 관리해야 한다면 IndexedDB를 고려해야 합니다.
📋 요약 및 제언
LocalStorage는 웹 브라우저에 영구적인 기억력을 부여하는 가장 빠르고 쉬운 방법입니다. setItem, getItem이라는 직관적인 API 덕분에 도입 장벽도 낮습니다. 하지만 **"문자열만 저장 가능하다"**는 제약과 **"보안에 취약하다"**는 특징을 항상 염두에 두어야 합니다.
여러분의 프로젝트에서는 현재 어떤 데이터를 LocalStorage에 담고 있나요? 혹시 보안이 중요한 인증 정보를 무방비하게 방치하고 있지는 않은지 검토해 보시기 바랍니다.
더 나아가, 복잡한 상태 관리가 필요한 SPA(Single Page Application) 환경이라면 LocalStorage와 상태 관리 라이브러리(Redux, Zustand 등)를 연동하여 Persist 기능을 구현해 보는 것도 좋은 실전 연습이 될 것입니다.