1. 소개
고품질의 오프라인 지원 웹 콘텐츠는 현재 사용자가 쉽게 발견할 수 없습니다. 사용자는 오프라인에서 콘텐츠를 탐색할 수 있으려면 어떤 웹사이트가 오프라인에서 동작하는지 알고 있어야 하거나, 설치된 PWA가 필요합니다. 이는 사용 가능한 콘텐츠를 발견할 진입점이 없기 때문에 훌륭한 사용자 경험이 아닙니다. 이를 해결하기 위해 이 명세는 개발자가 특정 콘텐츠를 브라우저에 알릴 수 있게 하는 새 API를 다룹니다.
콘텐츠 색인은 웹사이트가 오프라인 사용 가능 콘텐츠를 브라우저에 등록할 수 있게 합니다. 그러면 브라우저는 웹사이트의 오프라인 기능을 개선하고 사용자가 오프라인 동안 탐색할 콘텐츠를 제공할 수 있습니다. 이 데이터는 기기 내 검색을 개선하고 탐색 기록을 보강하는 데에도 사용될 수 있습니다.
이 API를 사용하면 다음 예시 사용 사례에서 사용자가 콘텐츠를 더 쉽게 발견하는 데 도움이 될 수 있습니다:
-
뉴스 웹사이트가 백그라운드에서 최신 기사를 미리 가져오는 경우.
-
콘텐츠 스트리밍 앱이 다운로드한 콘텐츠를 브라우저에 등록하는 경우.
1.1. 예시
function deleteArticleResources( id) { return Promise. all([ caches. open( 'offline-articles' ) . then( articlesCache=> articlesCache. delete ( `/article/ ${ id} ` )), // 이 함수가 `contentdelete` 이벤트의 결과로 호출된 경우 // 이는 아무 작업도 수행하지 않습니다. self. registration. index. delete ( id), ]); } self. addEventListener( 'activate' , event=> { // 서비스 워커가 활성화되면 오래된 콘텐츠를 제거합니다. event. waitUntil( async function () { const descriptions= await self. registration. index. getAll(); const oldDescriptions= descriptions. filter( description=> shouldRemoveOldArticle( description)); await Promise. all( oldDescriptions. map( description=> deleteArticleResources( description. id))); }()); }); self. addEventListener( 'push' , event=> { const payload= event. data. json(); // 기사를 가져와 저장한 다음 등록합니다. event. waitUntil( async function () { const articlesCache= await caches. open( 'offline-articles' ); await articlesCache. add( `/article/ ${ payload. id} ` ); await self. registration. index. add({ id: payload. id, title: payload. title, description: payload. description, category: 'article' , icons: payload. icons, url: `/article/ ${ payload. id} ` , }); // 긴급한 경우 알림을 표시합니다. }()); }); self. addEventListener( 'contentdelete' , event=> { // 사용자가 삭제한 후 기본 콘텐츠를 지웁니다. event. waitUntil( deleteArticleResources( event. id)); });
위 예시에서 shouldRemoveOldArticle은 개발자가 정의한 함수입니다.
2. 개인정보 보호 고려사항
contentdelete 이벤트를 발생시키면 사용자가 페이지를 떠난 후 사용자의 IP 주소가 드러날 수 있습니다.
이를 악용하면 위치 기록을 추적하는 데 사용할 수 있습니다. 사용자 에이전트는 이벤트의 지속 시간을 제한하여
추적을 제한해야 합니다(SHOULD).
contentdelete 이벤트를 발생시킬 때, 사용자 에이전트는 웹사이트가 새 콘텐츠를 추가하지 못하도록
방지해야 합니다(SHOULD).
이는 스팸성 웹사이트가 같은 콘텐츠를 다시 추가하는 것과 사용자가 방금 삭제한 콘텐츠를 다시 보게 되는 것을
방지합니다.
등록된 모든 콘텐츠를 표시하면 악의적인 웹사이트가 노출을 최대화하기 위해 사용자에게 콘텐츠를 스팸처럼 표시할 수 있습니다. 사용자 에이전트는 모든 콘텐츠를 노출하지 말고, 사용자 경험 개선을 목표로 하는 사용자 에이전트 정의 신호 집합에 기반하여 표시할 적절한 콘텐츠를 선택할 것을 강력히 권장합니다.
3. 인프라
3.1. 서비스 워커 등록에 대한 확장
서비스 워커 등록은 추가로 다음을 가집니다:
3.2. 콘텐츠 색인 항목
콘텐츠 색인 항목은 다음으로 구성됩니다:
-
description (
ContentDescription). -
launch url (URL).
-
service worker registration (서비스 워커 등록).
3.2.1. 표시
사용자 에이전트는 entry가 서비스 워커 등록의 content index entries 안에 존재하는 한, 언제든 콘텐츠 색인 항목 (entry)을 표시할 수 있습니다(MAY).
참고: 사용자 에이전트는 사용자에게 너무 많은 항목을 보이지 않도록 노출되는 콘텐츠를 제한해야 합니다.
-
UI는 entry의 service worker registration의 scope url의 출처를 눈에 띄게 표시해야 합니다(MUST).
-
UI는 entry의 description의
title을 표시해야 합니다(MUST). -
UI는 entry의 description의
description을 표시할 수 있습니다(MAY). -
UI는 표시 결정을 할 때 entry의 description의
category를 사용할 수 있습니다(MAY). -
UI는 entry의 icons 중 어느 것이든 이미지로 표시할 수 있습니다(MAY).
-
UI는 사용자가 UI에 노출된 기본 entry를 삭제할 방법을 제공해야 하며(SHOULD), 이 경우 entry에 대해 콘텐츠 색인 항목 삭제를 실행합니다.
-
UI는 사용자가 이를 활성화할 방법(예: 클릭)을 제공해야 하며(MUST), 이 경우 entry에 대해 콘텐츠 색인 항목 활성화를 실행합니다.
3.2.2. 표시 해제
4. 알고리즘
4.1. 콘텐츠 색인 항목 삭제
-
id를 entry의 description의
id로 둡니다. -
contentIndexEntries를 entry의 service worker registration의 content index entries로 둡니다.
-
entry의 service worker registration의 entry edit queue에 다음 단계를 큐에 넣습니다:
-
entry를 표시 해제합니다.
-
contentIndexEntries[id]를 제거합니다.
-
entry에 대해 콘텐츠 삭제 이벤트 발생을 수행합니다.
-
4.2. 콘텐츠 색인 항목 활성화
-
activeWorker를 entry의 service worker registration의 active worker로 둡니다.
-
activeWorker가 null이면 이 단계를 중단합니다.
-
newContext를 새 최상위 탐색 컨텍스트로 둡니다.
-
newContext의
Window객체의 환경 설정 객체의 책임 이벤트 루프에서 사용자 상호작용 태스크 소스를 사용하여 다음 단계를 실행하도록 태스크를 큐에 넣습니다:-
HandleNavigate: 탐색을 수행하여, newContext를 entry의 launch url로 이동시키며, 예외 활성화 및 대체 활성화를 사용합니다.
-
HandleNavigate로 표시된 단계에서 호출된 알고리즘 단계가 예외를 던지면, 이 단계를 중단합니다.
-
frameType을 "`top-level`"로 둡니다.
-
visibilityState를 newContext의 활성 문서의
visibilityState속성 값으로 둡니다. -
focusState를 newContext의 활성 문서를 인수로 하여 포커스 있음 단계를 실행한 결과로 둡니다.
-
ancestorOriginsList를 newContext의 활성 문서의 관련 전역 객체의
Location객체의 조상 출처 목록의 관련 목록으로 둡니다. -
serviceWorkerEventLoop에서 DOM 조작 태스크 소스를 사용하여 다음 단계를 실행하도록 태스크를 큐에 넣습니다:
-
여기서 새 Browsing Context를 만드는 것이 올바른 일인가? (issue)
4.3. 콘텐츠 삭제 이벤트 발생
ContentIndexEvent로
이름이 "contentdelete"인 기능
이벤트를 발생시킵니다:
id-
entry의 description의
id.
5. API
5.1.
ServiceWorkerGlobalScope에
대한 확장
partial interface ServiceWorkerGlobalScope {attribute EventHandler ; };oncontentdelete
5.1.1. 이벤트
다음은 ServiceWorker
인터페이스를 구현하는 모든 객체가 이벤트 핸들러 IDL 속성으로 지원해야 하는 이벤트 핸들러(및 그에 대응하는 이벤트 핸들러 이벤트 타입)입니다:
| 이벤트 핸들러 이벤트 타입 | 이벤트 핸들러 | 인터페이스 |
|---|---|---|
contentdelete
| oncontentdelete
| ContentIndexEvent
|
5.2. ServiceWorkerRegistration에
대한 확장
partial interface ServiceWorkerRegistration { [SameObject ]readonly attribute ContentIndex index ; };
ServiceWorkerRegistration은
content index (ContentIndex)를
가지며, 초깃값은
그 service worker registration이 컨텍스트
객체의 서비스 워커 등록인 새 ContentIndex입니다.
index 속성의 getter는
컨텍스트 객체의 content index를 반환해야 합니다.
5.3. ContentIndex
현재 하나의 엔진에만 있습니다.
Opera없음Edge없음
Edge (Legacy)없음IE없음
Firefox for Android없음iOS Safari없음Chrome for Android84+Android WebView84+Samsung Internet없음Opera Mobile60+
enum {ContentCategory ,"" ,"homepage" ,"article" ,"video" , };"audio" dictionary {ContentDescription required DOMString ;id required DOMString ;title required DOMString ;description ContentCategory = "";category sequence <ImageResource >= [];icons required USVString ; }; [url Exposed =(Window ,Worker )]interface {ContentIndex Promise <undefined >add (ContentDescription );description Promise <undefined >delete (DOMString );id Promise <sequence <ContentDescription >>getAll (); };
현재 하나의 엔진에만 있습니다.
Opera없음Edge없음
Edge (Legacy)없음IE없음
Firefox for Android없음iOS Safari없음Chrome for Android84+Android WebView84+Samsung Internet없음Opera Mobile60+
ContentIndex는
service worker registration (서비스 워커 등록)을 가집니다.
5.3.1.
add()
현재 하나의 엔진에만 있습니다.
Opera없음Edge없음
Edge (Legacy)없음IE없음
Firefox for Android없음iOS Safari없음Chrome for Android84+Android WebView84+Samsung Internet없음Opera Mobile60+
add(description) 메서드는 호출되면
새 promise promise를 반환하고
다음 단계를 병렬로 실행해야 합니다:
-
registration을 컨텍스트 객체의 service worker registration으로 둡니다.
-
registration의 active worker가 null이면, promise를
TypeError로 거부하고 이 단계를 중단합니다. -
description의
id,title,description, 또는url중 어느 하나가 빈 문자열이면, promise를TypeError로 거부하고 이 단계를 중단합니다. -
launchURL를 description의
url을 컨텍스트 객체의 관련 설정 객체의 API 기준 URL로 파싱한 결과로 둡니다.참고: 더 좁은 범위를 가진 새 서비스 워커 등록이 나중에 도입될 수 있습니다.
-
matchedRegistration을 launchURL을 인수로 하여 Match Service Worker Registration 알고리즘을 실행한 결과로 둡니다.
-
matchedRegistration이 registration과 같지 않으면, promise를
TypeError로 거부하고 이 단계를 중단합니다. -
registration의 active worker의 확장 이벤트 집합이
FetchEvent를 포함하지 않으면, promise를TypeError로 거부하고 이 단계를 중단합니다. -
icons를 빈 목록으로 둡니다.
-
선택적으로, 사용자 에이전트는 description의
icons에서 사용할 아이콘을 선택할 수 있습니다(MAY). 이 경우, 성공적으로 파싱된 뒤 선택된 description의icons의 각 이미지 리소스 (resource)에 대해 다음 단계를 반복합니다:-
다음 속성을 가진 새 요청을 사용하여 fetch를 기다린 결과를 response로 둡니다:
- URL
-
resource의 src.
- Client
- Keepalive flag
-
설정됨.
- Destination
-
"`image`".
- Mode
-
"`no-cors`".
- Credentials mode
-
"`include`".
-
response를 이미지로 디코딩할 수 없으면, promise를
TypeError로 거부하고 이 단계를 중단합니다. -
response를 icons에 추가합니다.
-
-
entry를 다음을 가진 새 콘텐츠 색인 항목으로 둡니다:
- description
-
description.
- launch url
-
launchURL
- service worker registration
-
registration.
- icons
-
icons
-
id를 description의
id로 둡니다. -
contentIndexEntries를 registration의 content index entries로 둡니다.
-
registration의 entry edit queue에 다음 단계를 큐에 넣습니다:
참고: 기존 ID를 가진 설명을 추가하면 이전 값을 덮어씁니다.
5.3.2.
delete()
현재 하나의 엔진에만 있습니다.
Opera없음Edge없음
Edge (Legacy)없음IE없음
Firefox for Android없음iOS Safari없음Chrome for Android84+Android WebView84+Samsung Internet없음Opera Mobile60+
delete(id) 메서드는 호출되면
새 promise promise를 반환하고 다음
단계를 병렬로 실행해야 합니다:
-
registration을 컨텍스트 객체의 service worker registration으로 둡니다.
-
contentIndexEntries를 registration의 content index entries로 둡니다.
-
registration의 entry edit queue에 다음 단계를 큐에 넣습니다:
5.3.3.
getAll()
getAll() 메서드는 호출되면 새 promise
promise를 반환하고 다음
단계를 병렬로 실행해야 합니다:
-
registration을 컨텍스트 객체의 service worker registration으로 둡니다.
-
contentIndexEntries를 registration의 content index entries로 둡니다.
-
descriptions를 빈 목록으로 둡니다.
-
registration의 entry edit queue에 다음 단계를 큐에 넣습니다:
-
contentIndexEntries의 각 id → entry에 대해 반복합니다:
-
entry의 description을 descriptions에 추가합니다.
-
-
promise를 descriptions로 이행합니다.
-
5.4. ContentIndexEvent
현재 하나의 엔진에만 있습니다.
Opera없음Edge없음
Edge (Legacy)없음IE없음
Firefox for Android없음iOS Safari없음Chrome for Android84+Android WebView84+Samsung Internet없음Opera Mobile60+
dictionary :ContentIndexEventInit ExtendableEventInit {required DOMString ; }; [id Exposed =ServiceWorker ]interface :ContentIndexEvent ExtendableEvent {(constructor DOMString ,type ContentIndexEventInit );init readonly attribute DOMString ; };id