1. 소개
수년에 걸쳐 웹은 IndexedDB, localStorage
, showNotification()
와 같은 다양한
저장소 관련 API들을 발전시켜 왔습니다. 저장소 표준은 다음을 정의하여 이러한 API들을 통합합니다:
- 이 API들이 데이터를 저장하는 기본 단위인 버킷
- 그 버킷을 영구적으로 만드는 방법
- origin의 사용량과 할당량 추정치를 얻는 방법
전통적으로, 사용자의 기기 저장 공간이 부족해지면 이러한 API로 저장된 데이터는 사용자가 개입할 수 없는 상태에서 손실되었습니다. 그러나 영구 버킷은 사용자의 동의 없이 삭제될 수 없습니다. 이는 네이티브 플랫폼에서 사용자가 누리던 데이터 보장 기능을 웹으로 가져옵니다.
간단히 말해, 저장소를 영구적으로 만드는 방법은 persist()
메서드를 호출하는 것입니다. 이 메서드는 동시에 최종 사용자에게 권한을 요청하고, 허용되면 저장소를 영구적으로
변경합니다:
navigator. storage. persist(). then( persisted => {
if ( persisted) {
/* … */
}
});
사용자 에이전트 기반의 대화 상자를 예고 없이 사용자에게 표시하지 않으려면 다음과 같이 약간 더 복잡한 코드를 작성할 수 있습니다:
Promise. all([
navigator. storage. persisted(),
navigator. permissions. query({ name: "persistent-storage" })
]). then(([ persisted, permission]) => {
if ( ! persisted && permission. state == "granted" ) {
navigator. storage. persist(). then( /* … */ );
} else if ( ! persisted && permission. state == "prompt" ) {
showPersistentStorageExplanation();
}
});
estimate()
메서드는 애플리케이션에 콘텐츠를 저장할 충분한 공간이 남아 있는지 판단하는 데 사용할 수 있습니다:
function retrieveNextChunk( nextChunkInfo) {
return navigator. storage. estimate(). then( info => {
if ( info. quota - info. usage > nextChunkInfo. size) {
return fetch( nextChunkInfo. url);
} else {
throw new Error ( "insufficient space to store next chunk" );
}
}). then( /* … */ );
}
2. 용어
이 명세서는 Infra 표준에 의존합니다. [INFRA]
이 명세서는 HTML, IDL, 및 Permissions 표준에서 사용하는 용어를 사용합니다. [HTML] [WEBIDL] [PERMISSIONS]
3. 구조 개요
사용자 에이전트는 다양한 종류의 반영구적 상태를 보유합니다:
- 자격 증명
-
HTML 폼을 통해 제출된 사용자 이름 및 비밀번호와 같은 최종 사용자의 자격 증명
- 권한
-
지리적 위치 등 다양한 기능에 대한 권한
- 네트워크
-
HTTP 캐시, 쿠키, 인증 항목, TLS 클라이언트 인증서
- 저장소
- Indexed DB, Cache API, 서비스 워커 등록,
localStorage
,sessionStorage
, 애플리케이션 캐시, 알림 등
이 표준은 주로 저장소와 관련이 있습니다.
4. 모델
로컬 또는 세션 저장소 API를 정의하는 표준은 저장소 엔드포인트를 정의하고 이 표준을 수정하여 등록합니다. 이러한 표준은 로컬 저장소 병 맵 가져오기 또는 세션 저장소 병 맵 가져오기 알고리즘을 호출하며, 이는 다음을 제공합니다:
-
실패, 이는 API가 예외를 발생시키거나 해당 환경 설정 객체에 사용할 수 있는 저장소가 없음을 표시해야 할 수도 있음을 의미합니다.
-
API에 적합한 방식으로 데이터를 저장하는 데 사용할 수 있는 저장소 프록시 맵. 이 표준은 다른 API, 저장소 키, 및 저장소 유형으로부터 해당 데이터를 격리하는 작업을 수행합니다.
이러한 API를 위한 표준을 정의하는 경우 이 표준에 대해 문제를 제기하여 지원 및 검토를 요청하는 것을 고려하세요.
이 데이터를 격리하기 위해 이 표준은 저장소 창고를 정의하며, 이는 저장소 선반을 저장소 키로 구분합니다. 저장소 선반은 저장소 버킷으로 구성되며, 미래에는 다양한 저장소 정책을 허용하기 위해 여러 저장소 버킷으로 구성될 가능성이 높습니다. 마지막으로, 저장소 버킷은 각각의 저장소 엔드포인트에 대해 하나씩 저장소 병으로 구성됩니다.
4.1. 저장소 엔드포인트
저장소 엔드포인트는 이 표준에서 정의한 인프라(특히 저장소 병)를 사용하여 저장소 요구를 추적하는 로컬 또는 세션 저장소 API입니다.
저장소 엔드포인트는 식별자를 가지며, 이는 저장소 식별자입니다.
저장소 엔드포인트는 또한 유형을 가지며, 이는 집합으로, 저장소 유형을 포함합니다.
저장소 엔드포인트는 또한 할당량을 가지며, 이는 이 저장소 엔드포인트에 해당하는 각 저장소 병에 권장되는 할당량(바이트 단위)을 나타내는 숫자거나 null입니다.
저장소 식별자는 ASCII 문자열입니다.
저장소 유형은
"local
" 또는 "session
"입니다.
등록된 저장소 엔드포인트는 다음 표에 정의된 집합의 저장소 엔드포인트입니다:
식별자 | 유형 | 할당량 |
---|---|---|
"caches "
| « "local " »
| null |
"indexedDB "
| « "local " »
| null |
"localStorage "
| « "local " »
| 5 × 220 (즉, 5 메비바이트) |
"serviceWorkerRegistrations "
| « "local " »
| null |
"sessionStorage "
| « "session " »
| 5 × 220 (즉, 5 메비바이트) |
언급된 바와 같이, 표준은 이러한 저장소 식별자를 로컬 저장소 병 맵 가져오기 및 세션 저장소 병 맵 가져오기와 함께 사용할 수 있습니다. 앞으로 일부 API가 저장소 유형 모두에 적용될 것으로 예상됩니다.
4.2. 저장소 키
저장소 키는 튜플로 구성되며, 이는 출처(출처)를 포함합니다. [HTML]
이는 변경될 가능성이 있습니다. 클라이언트 측 저장소 분할을 참조하세요.
저장소 키를 가져오기 위해, 환경 environment가 주어졌을 때:
-
key를 environment와 함께 비저장 목적의 저장소 키 가져오기를 실행한 결과로 설정합니다.
-
사용자가 저장소를 비활성화한 경우, 실패를 반환합니다.
-
key를 반환합니다.
비저장 목적의 저장소 키 가져오기 위해, 환경 environment가 주어졌을 때:
4.3. 저장소 창고
저장소 창고는 맵으로 구성되며, 이는 저장소 키를 저장소 선반에 매핑합니다. 초기 상태는 비어 있습니다.
사용자 에이전트는 저장소 창고를 보유하며, 이는 저장소 창고입니다. 사용자 에이전트의 저장소 창고는 모든 로컬 저장소 데이터를 보유합니다.
탐색 가능한 네비게이블은 저장소 창고를 보유하며, 이는 저장소 창고입니다. 탐색 가능한 네비게이블의 저장소 창고는 모든 세션 저장소 데이터를 보유합니다.
탐색 가능한 저장소 창고를 레거시 복제하기 위해, 탐색 가능한 네비게이블 A와 탐색 가능한 네비게이블 B가 주어졌을 때, 다음 단계를 실행합니다:
이는 레거시로 간주되며, 이점이 있더라도 구현 복잡성을 상회하지 않습니다. 따라서 HTML 외부에서 확장되거나 사용되지 않을 것입니다. [HTML]
4.4. 저장소 선반
저장소 선반은 저장소 창고 내의 각 저장소 키에 대해 존재합니다. 이는 버킷 맵을 포함하며, 이는 맵의 문자열에서 저장소 버킷으로 매핑됩니다.
현재로서는 "default
"가 키로 존재하는 유일한 버킷 맵입니다. issue #2를 참조하세요. 이는 저장소 선반이 처음 획득될 때 값을 갖게 됩니다.
저장소 선반을 획득하기 위해 저장소 창고 shed, 환경 설정 객체 environment, 저장소 유형 type이 주어졌을 때 다음 단계를 실행합니다:
-
key를 environment와 함께 저장소 키를 획득을 실행한 결과로 설정합니다.
-
만약 key가 실패라면, 실패를 반환합니다.
-
만약 shed[key]가 존재하지 않는다면, shed[key]를 type과 함께 저장소 선반 생성을 실행한 결과로 설정합니다.
-
shed[key]를 반환합니다.
로컬 저장소 선반을
획득하기 위해 환경 설정 객체 environment가 주어졌을 때, 사용자 에이전트의 저장소 창고,
environment, "local
"과 함께 저장소 선반을 획득을 실행한 결과를 반환합니다.
저장소 선반 생성하기 위해 저장소 유형 type이 주어졌을 때, 다음 단계를 실행합니다:
4.5. 저장소 버킷
저장소 버킷은 저장소 엔드포인트가 데이터를 저장하는 장소입니다.
저장소 버킷은 병 맵을 가지며, 이는 저장소 식별자에서 저장소 병으로 매핑됩니다.
로컬 저장소 버킷은 로컬 저장소 API를 위한 저장소 버킷입니다.
로컬 저장소 버킷은 "모드"를 가지며, 이는 "best-effort
" 또는
"persistent
"일 수 있습니다. 초기 상태는 "best-effort
"입니다.
세션 저장소 버킷은 세션 저장소 API를 위한 저장소 버킷입니다.
저장소 버킷 생성하기 위해 저장소 유형 type이 주어졌을 때, 다음 단계를 실행합니다:
-
bucket을 null로 설정합니다.
-
만약 type이 "
local
"이라면, bucket을 새로운 로컬 저장소 버킷으로 설정합니다. -
그렇지 않다면:
-
확인: type이 "
session
"임을 보장합니다. -
bucket을 새로운 세션 저장소 버킷으로 설정합니다.
-
-
각 endpoint에 대해, 이는 등록된 저장소 엔드포인트 중 유형이 type을 포함하는 경우입니다, bucket의 병 맵[endpoint의 식별자]를 저장소 병으로 설정합니다. 이 저장소 병의 할당량은 endpoint의 할당량입니다.
-
bucket을 반환합니다.
4.6. 저장소 병
저장소 병은 단일 저장소 엔드포인트를 위해 저장소 버킷에서 분리된 부분입니다. 저장소 병은 초기에는 비어 있는 맵인 맵을 가집니다. 또한, 저장소 병은 초기에는 비어 있는 집합인 프록시 맵 참조 집합을 가집니다. 또한, 저장소 병은 널(null)이거나 저장 가능한 총 바이트 수의 보수적인 추정을 나타내는 숫자인 할당량을 가집니다. Null은 제한이 없음을 나타냅니다. 이는 여전히 포함하는 저장소 선반의 저장소 할당량에 의해 제한을 받습니다.
저장소 병의 맵은 저장되어야 할 실제 데이터를 보관하는 곳입니다. 사용자 에이전트는 이 데이터를 저장하고, 에이전트와 심지어 에이전트 클러스터 경계를 넘어 이 데이터를 제공하도록 기대됩니다. 이는 구현 정의 방식으로 처리되며, 이를 통해 이 표준 및 이 표준을 사용하는 표준이 콘텐츠에 접근할 수 있습니다.
저장소 병 맵을 획득하기 위해, 저장소 유형 type, 환경 설정 객체 environment, 저장소 식별자 identifier가 주어졌을 때 다음 단계를 실행합니다:
-
shed를 null로 설정합니다.
-
만약 type이 "
local
"이라면, shed를 사용자 에이전트의 저장소 창고로 설정합니다. -
그렇지 않다면:
-
확인: type이 "
session
"임을 보장합니다. -
shed를 environment의 전역 객체의 연관된
Document
의 노드 네비게이블의 탐색 가능한 네비게이블의 저장소 창고로 설정합니다.
-
-
shelf를 shed, environment, type과 함께 저장소 선반을 획득을 실행한 결과로 설정합니다.
-
만약 shelf가 실패라면, 실패를 반환합니다.
-
bucket을 shelf의 버킷 맵["
default
"]로 설정합니다. -
bottle을 bucket의 병 맵[identifier]로 설정합니다.
-
추가 proxyMap를 bottle의 프록시 맵 참조 집합에 추가합니다.
-
proxyMap를 반환합니다.
로컬 저장소 병 맵을
획득하기 위해, 환경 설정 객체 environment, 저장소 식별자 identifier가 주어졌을 때,
저장소 병
맵을 획득을 "local
", environment, identifier와 함께 실행한 결과를 반환합니다.
세션 저장소 병 맵을
획득하기 위해, 환경 설정 객체 environment, 저장소 식별자 identifier가 주어졌을 때,
저장소 병
맵을 획득을 "session
", environment, identifier와 함께 실행한 결과를 반환합니다.
4.7. 저장소 프록시 맵
저장소 프록시 맵은 맵과 동등하지만, 모든 작업이 대신 백업 맵에서 수행됩니다.
이는 백업 맵을 교체할 수 있도록 합니다. 이는 이슈 #4 및 잠재적으로 저장소 액세스 API에 필요합니다.
4.8. 저장소 작업 소스
저장소 작업 소스는 작업 소스로, 작업과 관련된 모든 저장소 엔드포인트에 사용됩니다. 특히, 저장소 엔드포인트의 할당량과 관련된 작업들에 사용됩니다.
저장소 작업을 큐에 추가하기 위해, 전역 객체 global과 일련의 단계 steps가 주어졌을 때, 전역 작업을 큐에 추가를 저장소 작업 소스와 함께 global 및 steps로 실행합니다.
5. 영구 권한
로컬 저장소 버킷은 사용자가 (혹은 사용자를 대신하여 사용자 에이전트가)
"persistent-storage
" 강력한
기능 사용 권한을 부여한 경우에만 모드를
"persistent
"로 변경할 수 있습니다.
출처에 권한이 부여되면, 영구 권한은 사용자 에이전트의 데이터 삭제 정책으로부터 저장소를 보호하는 데 사용할 수 있습니다. 사용자 에이전트는 출처나 사용자의 관여 없이 영구로 표시된 저장소를 삭제할 수 없습니다. 이는 오프라인 상태에서 사용자가 필요로 하는 리소스나 사용자가 로컬에 생성한 리소스에 특히 유용합니다.
"persistent-storage
" 강력한
기능의 권한 관련 알고리즘과 유형은 기본값으로 설정되지만, 다음의 경우는 제외됩니다:
- 권한 상태
-
"
persistent-storage
"의 권한 상태는 주어진 출처에 대해 모든 환경 설정 객체에서 동일한 값을 가져야 합니다. - 권한 철회 알고리즘
-
-
"
persistent-storage
"에 대해 현재 권한 상태 가져오기의 결과가 "granted
"라면, 반환합니다. -
shelf를 로컬 저장소 선반 획득을 현재 설정 객체와 함께 실행한 결과로 설정합니다.
-
6. 사용량과 할당량
저장소 선반의 저장소 사용량은 사용된 바이트의 양에 대한 구현 정의의 대략적인 추정치입니다.
사용자 에이전트가 중복 제거, 압축 및 기타 기술을 사용할 수 있으며 권장되므로, 저장소 선반이 사용하는 바이트를 정확히 알 수 없습니다.
저장소 선반의 저장소 할당량은 저장 가능한 총 바이트 수에 대한 구현 정의의 보수적인 추정치입니다. 이
양은 기기의 총 저장 공간보다 적어야 합니다. 이는 기기의 사용 가능한 저장 공간의 함수가 되어서는 안 됩니다.
사용자 에이전트는 할당량을 결정할 때 탐색 빈도, 최근 방문, 북마킹 및 "persistent-storage
"에 대한 권한을 고려하는 것이 강력히 권장됩니다.
사용 가능한 저장 공간을 직접 또는 간접적으로 공개하면 출처 범위 밖의 정보 누출 및 지문 채취로 이어질 수 있습니다.
7. 관리
사용자 에이전트가 저장소 버킷을 삭제할 때, 반드시 전체를 삭제해야 합니다. 사용자 에이전트는 이를 액세스할 수 있는 스크립트가 실행 중일 때 저장소 버킷을 삭제하지 않아야 하며, 사용자가 별도로 지시한 경우를 제외합니다.
만약 저장소 버킷의 삭제가 포함하는 저장소 선반의 버킷 맵을 비우게 하면, 해당 저장소 선반과 관련된 저장소 키를 포함하는 저장소 창고에서 삭제합니다.
7.1. 저장소 압박
저장소 압박을 받는 사용자 에이전트는 네트워크 상태와 로컬 저장소 버킷을 삭제해야
하며, 이때 모드가
"best-effort
"인 것을 우선적으로 삭제하고 사용자에게 영향을 최소화하는 방식으로 삭제해야 합니다.
만약 사용자 에이전트가 계속해서 저장소 압박을 받는다면, 사용자에게 알리고 남은 로컬
저장소 버킷—즉, 모드가
"persistent
"인 것—을 삭제할 방법을 제공해야 합니다.
세션 저장소 버킷은 탐색 가능한 네비게이블이 닫힐 때 삭제해야 합니다.
만약 사용자 에이전트가 탐색 가능한 네비게이블을 복원할 수 있도록 허용한다면, 예를 들어 탐색 가능한 네비게이블을 다시 열거나 사용자 에이전트를 재시작한 후 계속 사용하는 경우, 삭제에는 더 복잡한 휴리스틱 집합이 필요합니다.
7.2. 사용자 인터페이스 가이드라인
사용자 에이전트는 개별 웹사이트의 네트워크 상태 및 저장소를 삭제할 수 있는 기능을 사용자에게 제공해야 합니다. 사용자 에이전트는 네트워크 상태와 저장소를 사용자 인터페이스에서 구분하지 않아야 합니다. 이는 네트워크 상태가 저장소를 복원하는 데 사용되지 않도록 하고 사용자가 이해해야 할 개념의 수를 줄입니다.
자격 증명은 사용자가 복원할 수 없는 데이터(예: 자동 생성된 비밀번호)를 포함할 수 있으므로 별도로 분리해야 합니다. 권한도 사용자에게 불편함을 주지 않도록 분리하는 것이 가장 좋습니다.
8. API
[SecureContext ]interface mixin { [
NavigatorStorage SameObject ]readonly attribute StorageManager storage ; };Navigator includes NavigatorStorage ;WorkerNavigator includes NavigatorStorage ;
각 환경 설정 객체는 연관된 StorageManager
객체를 가집니다. [HTML]
storage
getter 단계는 this의 관련 설정 객체의 StorageManager
객체를 반환하는 것입니다.
[SecureContext ,Exposed =(Window ,Worker )]interface {
StorageManager Promise <boolean >persisted (); [Exposed =Window ]Promise <boolean >persist ();Promise <StorageEstimate >estimate (); };dictionary {
StorageEstimate unsigned long long ;
usage unsigned long long ; };
quota
persisted()
메서드 단계는 다음과 같습니다:
-
promise를 새로운 promise로 설정합니다.
-
shelf를 로컬 저장소 선반 획득을 this의 관련 설정 객체와 함께 실행한 결과로 설정합니다.
-
그렇지 않다면, 다음 단계를 병렬로 실행합니다:
-
persisted를 true로 설정합니다, 만약 shelf의 버킷 맵["
default
"]의 모드가 "persistent
"라면; 그렇지 않으면 false로 설정합니다.내부 오류가 있을 경우 false로 설정됩니다.
-
저장소 작업을 큐에 추가를 global과 함께 실행하여, resolve promise를 persisted로 설정합니다.
-
-
promise를 반환합니다.
persist()
메서드 단계는 다음과 같습니다:
-
promise를 새로운 promise로 설정합니다.
-
shelf를 로컬 저장소 선반 획득을 this의 관련 설정 객체와 함께 실행한 결과로 설정합니다.
-
그렇지 않다면, 다음 단계를 병렬로 실행합니다:
-
permission을 사용 권한 요청 "
persistent-storage
"의 결과로 설정합니다.사용자 에이전트는 동일 출처에 대해 동일한 시간에 두 번 질문하지 않도록 권장됩니다. 이 알고리즘은 이러한 시나리오를 처리할 수 없습니다.
-
bucket을 shelf의 버킷 맵["
default
"]로 설정합니다. -
persisted를 true로 설정합니다, 만약 bucket의 모드가 "
persistent
"라면; 그렇지 않으면 false로 설정합니다.내부 오류가 있을 경우 false로 설정됩니다.
-
만약 persisted가 false이고 permission이 "
granted
"라면:-
bucket의 모드를 "
persistent
"로 설정합니다. -
내부 오류가 없었다면, persisted를 true로 설정합니다.
-
-
저장소 작업을 큐에 추가를 global과 함께 실행하여, resolve promise를 persisted로 설정합니다.
-
-
promise를 반환합니다.
estimate()
메서드 단계는 다음과 같습니다:
-
promise를 새로운 promise로 설정합니다.
-
shelf를 로컬 저장소 선반 획득을 this의 관련 설정 객체와 함께 실행한 결과로 설정합니다.
-
그렇지 않다면, 다음 단계를 병렬로 실행합니다:
-
usage를 shelf의 저장소 사용량으로 설정합니다.
-
quota를 shelf의 저장소 할당량으로 설정합니다.
-
dictionary를
StorageEstimate
사전으로 설정하며, 이 사전의usage
멤버는 usage,quota
멤버는 quota로 설정합니다. -
만약 usage와 quota를 획득하는 동안 내부 오류가 발생했다면, 저장소 작업을 큐에 추가를 global과 함께 실행하여, reject promise를
TypeError
와 함께 실행합니다.내부 오류는 매우 드물며 저수준 플랫폼이나 하드웨어 결함을 나타냅니다. 하지만 웹의 규모와 구현 및 플랫폼의 다양성을 고려할 때, 예기치 않은 일이 발생할 수 있습니다.
-
그렇지 않다면, 저장소 작업을 큐에 추가를 global과 함께 실행하여, resolve promise를 dictionary로 설정합니다.
-
-
promise를 반환합니다.
감사의 글
이에 대해 깊은 감사를 드립니다: Adrian Bateman, Aislinn Grigas, Alex Russell, Ali Alabbas, Andrew Sutherland, Andrew Williams, Austin Sullivan, Ben Kelly, Ben Turner, Dale Harvey, David Grogan, Domenic Denicola, fantasai, Jake Archibald, Jeffrey Yasskin, Jesse Mykolyn, Jinho Bang, Jonas Sicking, Joshua Bell, Kenji Baheux, Kinuko Yasuda, Luke Wagner, Michael Nordman, Mike Taylor, Mounir Lamouri, Shachar Zohar, 黃強 (Shawn Huang), 簡冠庭 (Timothy Guan-tin Chien), 그리고 Victor Costan 여러분이 대단하십니다!
이 표준은 Anne van Kesteren (Apple, annevk@annevk.nl)에 의해 작성되었습니다.
지적 재산권
저작권 © WHATWG (Apple, Google, Mozilla, Microsoft). 이 작업은 Creative Commons Attribution 4.0 International License에 따라 라이선스가 부여됩니다. 소스 코드로 통합된 부분에 대해서는, 해당 소스 코드 부분은 BSD 3-Clause License에 따라 라이선스가 부여됩니다.
이는 현행 표준입니다. 특허 검토 버전에 관심이 있는 분들은 현행 표준 검토 초안을 확인하시기 바랍니다.