웹용 스토리지

브라우저에 데이터를 저장하는 방법에는 여러 가지가 있습니다. 어떤 옵션이 내 요구사항에 가장 적합할까요?

이동 중에는 인터넷 연결이 불안정하거나 존재하지 않을 수 있습니다. 따라서 오프라인 지원과 안정적인 성능이 프로그레시브 웹 앱의 일반적인 기능입니다. 완벽한 무선 환경에서도 캐싱 및 기타 저장 기술을 현명하게 사용하면 사용자 환경이 크게 향상될 수 있습니다. 정적 애플리케이션 리소스 (HTML, JavaScript, CSS, 이미지 등)와 데이터 (사용자 데이터, 뉴스 기사 등)를 캐시하는 방법에는 여러 가지가 있습니다. 하지만 어떤 솔루션이 가장 좋을까요? 얼마나 저장할 수 있나요? 제거되지 않도록 하려면 어떻게 해야 하나요?

리소스 저장을 위한 일반적인 권장사항은 다음과 같습니다.

IndexedDB, OPFS, Cache Storage API는 모든 최신 브라우저에서 지원됩니다. 비동기식이며 기본 스레드를 차단하지 않습니다. 하지만 웹 워커에서만 사용할 수 있는 OPFS의 동기식 변형도 있습니다. window 객체, 웹 워커, 서비스 워커에서 액세스할 수 있으므로 코드의 어디서나 사용할 수 있습니다.

다른 저장소 메커니즘은?

브라우저에서 사용할 수 있는 다른 스토리지 메커니즘이 몇 가지 있지만 사용 범위가 제한되어 있고 심각한 성능 문제를 일으킬 수 있습니다.

SessionStorage는 탭별이며 탭의 전체 기간으로 범위가 지정됩니다. IndexedDB 키와 같이 소량의 세션별 정보를 저장하는 데 유용할 수 있습니다. 동기적이고 기본 스레드를 차단하므로 주의해서 사용해야 합니다. 크기는 약 5MB로 제한되며 문자열만 포함할 수 있습니다. 탭별이므로 웹 워커나 서비스 워커에서 액세스할 수 없습니다.

LocalStorage는 동기적이고 기본 스레드를 차단하므로 사용하지 않는 것이 좋습니다. 크기는 약 5MB로 제한되며 문자열만 포함할 수 있습니다. 웹 워커 또는 서비스 워커에서는 LocalStorage에 액세스할 수 없습니다.

쿠키에는 용도가 있지만 저장소로 사용해서는 안 됩니다. 쿠키는 모든 HTTP 요청과 함께 전송되므로 소량 이상의 데이터를 저장하면 모든 웹 요청의 크기가 크게 증가합니다. 동기식이며 웹 워커에서 액세스할 수 없습니다. LocalStorage 및 SessionStorage와 마찬가지로 쿠키는 문자열로만 제한됩니다.

File System Access API는 사용자가 로컬 파일 시스템에 있는 파일을 읽고 편집할 수 있도록 설계되었습니다. 페이지에서 로컬 파일을 읽거나 쓸 수 있으려면 사용자가 권한을 부여해야 하며, 파일 핸들이 IndexedDB에 캐시되지 않는 한 권한은 세션 전반에서 유지되지 않습니다. File System Access API는 파일을 열고 수정한 후 변경사항을 파일에 다시 저장해야 하는 편집기와 같은 사용 사례에 가장 적합합니다.

File System API 및 FileWriter API는 샌드박스 처리된 파일 시스템에 파일을 읽고 쓰는 메서드를 제공합니다. 비동기식이지만 Chromium 기반 브라우저에서만 사용할 수 있으므로 권장하지 않습니다.

저장할 수 있는 양은 얼마인가요?

간단히 말해 매우 많습니다. 최소 수백 메가바이트에서 수백 기가바이트 이상일 수 있습니다. 브라우저 구현은 다양하지만 일반적으로 사용할 수 있는 저장용량은 기기에서 사용할 수 있는 저장용량에 따라 다릅니다.

  • Chrome에서는 브라우저가 총 디스크 공간의 최대 80%를 사용할 수 있습니다. 출처는 총 디스크 공간의 최대 60%를 사용할 수 있습니다. StorageManager API를 사용하여 사용 가능한 최대 할당량을 결정할 수 있습니다. 다른 Chromium 기반 브라우저는 다를 수 있습니다.
    • 시크릿 모드에서는 Chrome이 출처에서 사용할 수 있는 저장용량을 총 디스크 공간의 약 5%로 줄입니다.
    • 사용자가 Chrome에서 '창을 모두 닫으면 쿠키 및 사이트 데이터 삭제'를 사용 설정한 경우 저장용량이 최대 약 300MB로 크게 줄어듭니다.
  • Firefox에서는 브라우저가 최대 50%의 디스크 여유 공간을 사용할 수 있습니다. eTLD+1 그룹(예: example.com, www.example.com, foo.bar.example.com)는 최대 2GB를 사용할 수 있습니다. StorageManager API를 사용하여 아직 사용 가능한 공간을 확인할 수 있습니다.
  • Safari(데스크톱 및 모바일 모두)는 약 1GB를 허용하는 것으로 보입니다. 한도에 도달하면 Safari에서 사용자에게 메시지를 표시하고 200MB 단위로 한도를 늘립니다. 이와 관련된 공식 문서를 찾을 수 없습니다.
    • PWA가 모바일 Safari의 홈 화면에 추가되면 새 저장소 컨테이너가 생성되며 PWA와 모바일 Safari 간에 아무것도 공유되지 않습니다. 설치된 PWA의 할당량에 도달하면 추가 저장용량을 요청할 방법이 없는 것으로 보입니다.

이전에는 사이트에서 저장된 데이터의 특정 기준점을 초과하면 브라우저에서 사용자에게 더 많은 데이터를 사용할 권한을 부여하라는 메시지를 표시했습니다. 예를 들어 출처가 50MB를 초과하는 경우 브라우저는 최대 100MB를 저장하도록 허용하라는 메시지를 사용자에게 표시한 후 50MB 단위로 다시 요청합니다.

현재 대부분의 최신 브라우저는 사용자에게 메시지를 표시하지 않으며 사이트가 할당된 할당량을 모두 사용할 수 있도록 허용합니다. 예외는 저장용량 할당량이 초과될 때 할당된 할당량을 늘리기 위한 권한을 요청하는 메시지를 표시하는 Safari인 것으로 보입니다. 출처가 할당된 할당량을 초과하여 사용하려고 하면 데이터를 더 이상 쓸 수 없습니다.

사용 가능한 저장용량을 확인하려면 어떻게 해야 하나요?

대부분의 브라우저에서는 StorageManager API를 사용하여 출처에서 사용할 수 있는 저장용량과 사용 중인 저장용량을 확인할 수 있습니다. IndexedDB 및 Cache API에서 사용하는 총 바이트 수를 보고하고 사용 가능한 대략적인 남은 저장공간을 계산할 수 있습니다.

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}

할당량 초과 오류를 포착해야 합니다(아래 참고). 사용 가능한 할당량이 사용 가능한 실제 저장용량을 초과하는 경우도 있습니다.

검사

개발 중에 브라우저의 DevTools를 사용하여 다양한 저장소 유형을 검사하고 저장된 모든 데이터를 삭제할 수 있습니다.

Chrome 88에는 스토리지 창에서 사이트의 스토리지 할당량을 재정의할 수 있는 새로운 기능이 추가되었습니다. 이 기능을 사용하면 다양한 기기를 시뮬레이션하고 디스크 가용성이 낮은 시나리오에서 앱의 동작을 테스트할 수 있습니다. 애플리케이션저장용량으로 이동하여 맞춤 스토리지 한도 시뮬레이션 체크박스를 사용 설정하고 유효한 숫자를 입력하여 스토리지 한도를 시뮬레이션합니다.

이 가이드를 작성하는 동안 최대한 많은 저장용량을 빠르게 사용해 보려고 간단한 도구를 작성했습니다. 이 방법을 사용하면 다양한 스토리지 메커니즘을 빠르게 실험하고 할당량을 모두 사용할 때 어떤 일이 발생하는지 확인할 수 있습니다.

할당량 초과는 어떻게 처리하나요?

할당량을 초과하면 어떻게 해야 하나요? 가장 중요한 점은 QuotaExceededError든 기타든 쓰기 오류를 항상 포착하고 처리해야 한다는 것입니다. 그런 다음 앱 설계에 따라 처리 방법을 결정합니다. 예를 들어 오랫동안 액세스하지 않은 콘텐츠를 삭제하거나, 크기를 기준으로 데이터를 삭제하거나, 사용자가 삭제할 항목을 선택할 수 있는 방법을 제공합니다.

사용 가능한 할당량을 초과하면 IndexedDB와 Cache API 모두 QuotaExceededError라는 DOMError를 발생시킵니다.

IndexedDB

출처가 할당량을 초과하면 IndexedDB에 대한 쓰기 시도가 실패합니다. 트랜잭션의 onabort() 핸들러가 호출되어 이벤트를 전달합니다. 이벤트의 오류 속성에 DOMException가 포함됩니다. 오류 name를 확인하면 QuotaExceededError이 반환됩니다.

const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
  const error = event.target.error; // DOMException
  if (error.name == 'QuotaExceededError') {
    // Fallback code goes here
  }
};

Cache API

출처의 할당량이 초과되면 Cache API에 쓰려고 하면 QuotaExceededError DOMException과 함께 거부됩니다.

try {
  const cache = await caches.open('my-cache');
  await cache.add(new Request('/sample1.jpg'));
} catch (err) {
  if (error.name === 'QuotaExceededError') {
    // Fallback code goes here
  }
}

제거는 어떻게 작동하나요?

웹 저장소는 '최선의 노력'과 '지속적'이라는 두 가지 버킷으로 분류됩니다. '최선의 방식'은 브라우저가 사용자를 방해하지 않고 저장소를 비울 수 있음을 의미하지만 장기 또는 중요한 데이터에 대한 내구성이 떨어집니다. 영구 저장소는 저장용량이 부족할 때 자동으로 삭제되지 않습니다. 사용자는 브라우저 설정을 통해 이 저장소를 수동으로 비워야 합니다.

기본적으로 사이트의 데이터(IndexedDB, Cache API 등 포함)는 최선의 방식으로 처리됩니다. 즉, 사이트에서 지속성 저장소를 요청하지 않는 한 브라우저는 기기 저장용량이 부족한 경우와 같이 재량에 따라 사이트 데이터를 삭제할 수 있습니다.

최선의 방식의 제거 정책은 다음과 같습니다.

  • Chromium 기반 브라우저는 브라우저의 공간이 부족해지면 데이터를 제거하기 시작하여 가장 오래전에 사용한 출처에서 사이트 데이터를 먼저 지운 다음 브라우저가 더 이상 한도를 초과하지 않을 때까지 가장 오래전에 사용한 출처에서 모든 사이트 데이터를 삭제합니다.
  • Firefox는 사용 가능한 디스크 공간이 가득 차면 데이터를 제거하기 시작하여 가장 오래전에 사용한 출처에서 모든 사이트 데이터를 먼저 지우고, 그 다음부터는 브라우저가 더 이상 한도를 초과하지 않을 때까지 삭제합니다.
  • 이전에는 Safari에서 데이터를 제거하지 않았지만 최근에는 모든 쓰기 가능한 저장소에 새로운 7일 한도를 구현했습니다(아래 참고).

iOS 및 iPadOS 13.4 및 macOS의 Safari 13.1부터 IndexedDB, 서비스 워커 등록, Cache API를 비롯한 모든 스크립트 쓰기 가능 저장소에 7일의 한도가 적용됩니다. 즉, 사용자가 사이트와 상호작용하지 않으면 Safari를 사용한 지 7일 후에 Safari에서 캐시의 모든 콘텐츠를 삭제합니다. 이 제거 정책은 홈 화면에 추가된 설치된 PWA에는 적용되지 않습니다. 자세한 내용은 WebKit 블로그의 전체 서드 파티 쿠키 차단 등을 참고하세요.

저장소 버킷

Storage Buckets API의 핵심 아이디어는 사이트에 여러 스토리지 버킷을 만들 수 있는 기능을 부여하는 것입니다. 여기서 브라우저는 다른 버킷과 별개로 각 버킷을 삭제할 수 있습니다. 이를 통해 개발자는 가장 중요한 데이터가 삭제되지 않도록 하기 위해 제거 우선순위를 지정할 수 있습니다.

보너스: IndexedDB에 래퍼를 사용하는 이유

IndexedDB는 사용하기 전에 상당한 설정이 필요한 하위 수준 API이므로 복잡도가 낮은 데이터를 저장하는 데 특히 어려움이 있을 수 있습니다. 대부분의 최신 약속 기반 API와 달리 이벤트 기반입니다. IndexedDB용 idb와 같은 프라미스 래퍼는 일부 강력한 기능을 숨기지만, 더 중요한 것은 IndexedDB 라이브러리와 함께 제공되는 복잡한 절차(예: 트랜잭션, 스키마 버전 관리)를 숨깁니다.

보너스: SQLite Wasm

웹 SQL이 지원 중단되고 Chrome에서 삭제된 후 Google은 많이 사용되는 SQLite 데이터베이스의 유지관리자와 협력하여 SQLite 기반의 웹 SQL을 대체하는 기능을 제공했습니다. 사용 방법에 관한 자세한 내용은 원본 비공개 파일 시스템에서 지원하는 브라우저의 SQLite Wasm을 참고하세요.

결론

저장용량이 제한되어 사용자에게 더 많은 데이터를 저장하라는 메시지를 표시하던 시대는 지났습니다. 사이트에서는 실행에 필요한 모든 리소스와 데이터를 효과적으로 저장할 수 있습니다. StorageManager API를 사용하면 사용 가능한 용량과 사용한 용량을 확인할 수 있습니다. 또한 지속형 저장소를 사용하면 사용자가 삭제하지 않는 한 앱을 제거되지 않도록 보호할 수 있습니다.

추가 리소스

감사합니다.

이 가이드를 검토해 주신 Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink, Victor Costan님께 감사드립니다. 이 도움말의 원본을 작성한 에이지 키타무라, 애디 오스마니, 마크 코헨님께 감사드립니다. 에이지는 현재 동작을 검증하는 데 유용한 브라우저 저장소 악용자라는 유용한 도구를 작성했습니다. 데이터를 최대한 많이 저장하고 브라우저의 저장용량 한도를 확인할 수 있습니다. Safari를 조사하여 저장용량 한도를 확인한 프랑수아 보퍼트와 2024년 원본 비공개 파일 시스템, 저장소 버킷, SQLite Wasm, 전반적인 콘텐츠 업데이트에 관한 정보를 추가해 주신 토마스 슈타이너에게 감사드립니다.

히어로 이미지는 Unsplash의 Guillaume Bolduc님이 제공해 주셨습니다.