안녕하세요! 이번에는 브라우저 저장소의 핵심, 웹 스토리지 API (Web Storage API) 공식 문서를 가져오셨군요.
프론트엔드 개발을 하다 보면, 사용자가 페이지를 새로고침하거나 브라우저를 껐다 켜더라도 화면의 상태(예: 다크 모드 설정, 장바구니 데이터 등)를 그대로 유지하고 싶을 때가 정말 많습니다. 특히 최근 많이 쓰이는 상태 관리 패턴을 구현할 때 이 웹 스토리지의 개념을 정확히 알고 있는 것이 아주 중요하죠.
문서 내용 하나하나 빠짐없이, 그리고 실무에서 마주칠 수 있는 팁들까지 아낌없이 담아 구어체로 꼼꼼하게 번역해 드릴게요. 자, 시작해 볼까요!
웹 스토리지 API(Web Storage API)는 브라우저가 키/값(key/value) 쌍을 저장할 수 있는 메커니즘을 제공합니다. 기존에 사용하던 쿠키(cookies)보다 훨씬 더 직관적인 방식으로 말이죠.
기준선 (Baseline): 널리 사용 가능 (Widely available)
이 기능은 아주 잘 정립되어 있으며 다양한 기기와 브라우저 버전에서 동작합니다. 2015년 7월부터 모든 주요 브라우저에서 사용할 수 있게 되었습니다.
웹 스토리지 내에는 다음과 같은 두 가지 메커니즘이 있습니다:
sessionStorage (세션 스토리지): 브라우저 탭 단위, 그리고 출처(origin) 단위로 파티션(격리)됩니다. 메인 문서와 그 안에 삽입된 모든 브라우징 컨텍스트 (iframe 등)는 출처별로 그룹화되며, 각 출처는 자신만의 독립적인 저장 공간에 접근할 수 있습니다. 브라우저 탭을 닫으면 해당 탭과 연결된 모든 sessionStorage 데이터는 파괴(삭제)됩니다.localStorage (로컬 스토리지): 오직 출처(origin) 단위로만 파티션됩니다. 동일한 출처를 가진 모든 문서는 동일한 localStorage 공간에 접근할 수 있으며, 브라우저를 닫았다가 다시 열어도 데이터가 그대로 유지됩니다.💡 강사의 핵심 보충 설명:
실무에서 이 둘을 어떻게 구분해서 쓸까요?
localStorage는 "사용자가 로그아웃하기 전까지 계속 유지되어야 하는 데이터" (예: 사이트 테마 설정, 자동 로그인 토큰 등)에 주로 씁니다. 상태 관리 라이브러리인 Zustand의persist미들웨어를 쓸 때 기본 저장소로 활용되기도 하죠.
반면sessionStorage는 "이 탭에서만 유지되면 되는 임시 데이터" (예: 결제 프로세스 진행 중인 단계 정보, 뒤로 가기를 위한 스크롤 위치 등)에 적합합니다. 탭을 닫으면 깔끔하게 날아가니까요!
이 메커니즘들은 Window.sessionStorage와 Window.localStorage 속성을 통해 사용할 수 있습니다. 이 중 하나에 접근하면 Storage 객체의 인스턴스가 반환되며, 이 객체를 통해 데이터 항목을 설정(set), 조회(retrieve), 삭제(remove)할 수 있습니다. 각 출처마다 sessionStorage와 localStorage를 위해 서로 다른 스토리지 객체가 사용되며, 이 둘은 각각 독립적으로 작동하고 제어됩니다.
이 API들을 사용해 얼마만큼의 데이터를 저장할 수 있는지, 그리고 스토리지 용량 제한을 초과하면 어떤 일이 발생하는지 알아보려면 스토리지 할당량 및 퇴거 기준 (Storage quotas and eviction criteria) 문서를 참고하세요.
웹 스토리지의 sessionStorage와 localStorage는 모두 동기식(synchronous)으로 동작합니다. 이는 즉, 이 저장소 메커니즘에서 데이터를 설정하거나, 가져오거나, 지울 때 해당 작업이 동기적으로 수행되어 작업이 완료될 때까지 다른 자바스크립트 코드의 실행을 차단(block)한다는 뜻입니다. 이러한 동기적 특성은 저장하거나 검색하는 데이터의 양이 엄청나게 많을 경우, 웹 애플리케이션의 퍼포먼스에 악영향을 미칠 수 있습니다.
따라서 개발자들은 방대한 양의 데이터나 계산이 많이 필요한 작업을 포함하여 sessionStorage나 localStorage 작업을 수행할 때 각별히 주의해야 합니다. 코드를 최적화하고 동기식 작업을 최소화하여 사용자 인터페이스(UI)가 멈추거나 애플리케이션의 반응 속도가 지연되는 것을 막는 것이 중요합니다.
퍼포먼스가 걱정되거나 꽤 큰 규모의 데이터 세트를 다뤄야 하는 상황이라면, IndexedDB와 같은 비동기적(asynchronous)인 대안을 사용하는 것이 훨씬 더 적합할 수 있습니다. 이런 대안들은 논블로킹(non-blocking) 작업을 지원하기 때문에 웹 애플리케이션에서 훨씬 더 부드러운 사용자 경험과 더 나은 성능을 보장해 줍니다.
💡 강사의 실무 팁:
로컬 스토리지는 동기적으로 동작하기 때문에 한 번에 메가바이트 단위의 거대한 JSON 배열을 통째로JSON.parse/JSON.stringify해서 넣고 빼면, 그 순간 브라우저 메인 스레드가 얼어버립니다(프리징 현상).
또한, Next.js 같은 서버 사이드 렌더링(SSR) 환경에서는 서버 쪽에window.localStorage가 아예 존재하지 않습니다! 그래서 첫 렌더링 시 로컬 스토리지 값을 바로 읽어오려고 하면 화면 불일치(Hydration Mismatch) 에러가 나기 십상이죠. 반드시 컴포넌트가 브라우저에 마운트된 이후(useEffect내부 등)에 스토리지에 접근하는 습관을 들이세요.
참고:
만약 사용자가 서드파티 쿠키를 비활성화(disabled third-party cookies)했다면, 서드파티(타사) IFrame에서 웹 스토리지에 접근하는 것은 거부됩니다.
각 출처(origin)는 자신만의 독립적인 스토리지를 가집니다 — 이는 웹 스토리지나 공유 스토리지(shared storage) 모두에 해당되는 사실입니다. 하지만, 타사(서드파티, 즉 외부에 임베드된) 코드가 공유 스토리지에 접근할 수 있는지 여부는 해당 코드가 실행되는 브라우징 컨텍스트에 따라 달라집니다. 다른 출처에서 온 타사 코드가 실행되는 '컨텍스트'가 바로 그 타사 코드의 스토리지 접근 권한을 결정짓습니다.
타사 코드는 <script> 요소를 사용해 직접 주입되거나, 타사 코드를 포함하고 있는 외부 사이트를 <iframe>의 소스로 설정하는 방식으로 다른 사이트에 추가될 수 있습니다. 타사 코드를 통합하기 위해 사용된 방식에 따라 해당 코드의 브라우징 컨텍스트가 달라집니다.
<script> 요소를 통해 다른 사이트에 추가되었다면, 여러분의 코드는 코드를 삽입한 쪽(embedder)의 브라우징 컨텍스트 안에서 실행됩니다. 따라서 여기서 Storage.setItem()이나 SharedStorage.set()을 호출하면, 이 키/값 쌍은 코드를 삽입한 호스트 사이트의 스토리지에 기록됩니다. 브라우저 입장에서는 <script> 태그를 사용했을 때 1사(자사) 코드와 3사(타사) 코드 간의 구분이 전혀 없기 때문입니다.<iframe>을 통해 다른 사이트 내부에 추가되었다면, 이 <iframe> 안의 코드는 <iframe> 자체 출처의 브라우징 컨텍스트에서 실행됩니다. 만약 <iframe> 내부의 코드가 Storage.setItem()을 호출하면, 데이터는 해당 <iframe> 출처의 로컬 스토리지나 세션 스토리지에 기록됩니다. <iframe> 코드가 SharedStorage.set()을 호출한다면, 데이터는 <iframe> 출처의 공유 스토리지에 기록됩니다.Storage
특정 도메인과 스토리지 타입(세션 혹은 로컬)에 대해 데이터를 설정하고, 검색하고, 삭제할 수 있게 해주는 객체입니다.
Window
웹 스토리지 API는 Window 객체를 확장하여 두 개의 새로운 속성을 추가했습니다. 바로 Window.sessionStorage와 Window.localStorage입니다. 이들은 각각 현재 도메인의 세션 스토리지 객체와 로컬 Storage 객체에 접근할 수 있게 해 줍니다. 또한 스토리지 영역에 변화가 생길 때(예를 들어 새로운 항목이 저장되었을 때) 발생하는 storage 이벤트 핸들러도 함께 제공합니다.
StorageEvent
스토리지 영역이 변경될 때 문서의 Window 객체에서 storage 이벤트가 발생합니다.
💡 강사의 실무 팁:
이storage이벤트는 아주 특별합니다! 내가 로컬 스토리지를 수정한 '그 창(탭)'에서는 이벤트가 발생하지 않고, 동일한 사이트(출처)를 띄워놓은 '다른 창이나 탭'에서만 발생합니다.
이 특징을 활용하면 "한 탭에서 유저가 다크 모드 버튼을 눌러 localStorage 값을 바꿨을 때, 다른 탭의 화면도 동시에 다크 모드로 싹 바뀌게(동기화) 만드는 기능"을 아주 쉽게 구현할 수 있습니다!
웹 스토리지의 전형적인 사용법을 보여드리기 위해 저희가 상상력을 발휘해 웹 스토리지 데모 (Web Storage Demo)라는 이름의 예제를 만들었습니다.
이 랜딩 페이지(landing page)에 들어가 보시면 색상, 폰트, 장식용 이미지를 커스터마이징할 수 있는 컨트롤들이 있습니다. 여러분이 다른 옵션을 선택하면 페이지가 즉각적으로 업데이트됩니다. 게다가 이 선택 사항들은 localStorage에 고스란히 저장되기 때문에, 이 페이지를 떠났다가 나중에 다시 접속하더라도 여러분의 선택이 그대로 기억되어 남아있게 됩니다.
추가로 이벤트 출력 페이지 (event output page)도 제공합니다. 이 페이지를 다른 탭에 띄워둔 상태에서 랜딩 페이지의 옵션을 변경해 보시면, StorageEvent가 발생하면서 업데이트된 스토리지 정보가 화면에 출력되는 것을 직접 확인하실 수 있습니다.
| 명세 (Specification) |
|---|
| HTML (# dom-localstorage-dev) |
| HTML (# dom-sessionstorage-dev) |
웹 스토리지 API(Window.localStorage 및 Window.sessionStorage)는 현존하는 거의 모든 모던 브라우저 및 플랫폼에서 완벽하게 지원됩니다.
localStorage 지원 / 1.10+ 부터 sessionStorage 지원)💡 참고: 전체 지원 내역 및 세부 구현 노트는 MDN의 브라우저 호환성 표 원본을 참조하세요.
사생활 보호 창(Private windows), 시크릿 모드(incognito mode), 또는 이와 비슷한 이름으로 불리는 프라이버시 브라우징 옵션들은 사용자의 방문 기록이나 쿠키 같은 데이터를 저장하지 않습니다.
이러한 프라이빗 모드에서는 localStorage가 마치 sessionStorage처럼 취급됩니다. 즉, 스토리지 API 자체는 계속 사용할 수 있고 완벽하게 작동하지만, 프라이빗 창에 저장된 모든 데이터는 해당 브라우저나 브라우저 탭이 닫히는 순간 영구적으로 삭제됩니다.
어떠셨나요? 생각보다 사용법은 아주 직관적이고 간단하죠?
쿠키(Cookie)가 매번 서버로 HTTP 요청을 보낼 때마다 무겁게 따라다녀서 부담스러웠다면, 로컬 스토리지와 세션 스토리지는 오직 브라우저(클라이언트) 안에서만 안전하게 데이터를 쥐고 있기 때문에 프론트엔드 환경에서 화면 상태를 유지하는 데 아주 탁월한 선택지입니다.