캐시 저장소는 강력한 도구입니다. 이를 통해 앱이 네트워크 상태에 덜 의존하게 됩니다. 캐시를 잘 사용하면 웹 앱을 오프라인으로 사용할 수 있고 어떤 네트워크 상태에서도 애셋을 최대한 빨리 제공할 수 있습니다. 애셋 및 데이터에서 언급한 대로 필요한 애셋을 캐시하는 데 가장 적합한 전략을 결정할 수 있습니다. 캐시를 관리하기 위해 서비스 워커는 Cache Storage API와 상호작용합니다.
Cache Storage API는 다양한 컨텍스트에서 사용할 수 있습니다.
- 창 컨텍스트 (PWA의 기본 스레드)
- 서비스 워커
- 사용하는 다른 작업자
서비스 워커를 사용하여 캐시를 관리하는 한 가지 이점은 수명 주기가 창에 연결되어 있지 않으므로 기본 스레드를 차단하지 않는다는 것입니다. Cache Storage API를 사용하려면 이러한 컨텍스트의 대부분이 TLS 연결 상태여야 합니다.
캐시할 항목
캐싱에 관해 가장 먼저 궁금한 점은 무엇을 캐시해야 하는지입니다. 이 질문에 대한 단일 답변은 없지만 사용자 인터페이스를 렌더링하는 데 필요한 모든 최소 리소스로 시작할 수 있습니다.
이러한 리소스에는 다음이 포함되어야 합니다.
- 기본 페이지 HTML (앱의 start_url)
- 기본 사용자 인터페이스에 필요한 CSS 스타일시트입니다.
- 사용자 인터페이스에 사용되는 이미지입니다.
- 사용자 인터페이스를 렌더링하는 데 필요한 JavaScript 파일입니다.
- 기본 환경을 렌더링하는 데 필요한 데이터(예: JSON 파일)입니다.
- 웹 글꼴
- 멀티페이지 애플리케이션에서 빠르게 또는 오프라인으로 제공하려는 다른 HTML 문서
오프라인 지원
오프라인 사용 가능 여부는 프로그레시브 웹 앱의 요구사항 중 하나이지만 클라우드 게임 솔루션이나 암호화폐 애셋 앱과 같이 모든 PWA에 전체 오프라인 환경이 필요한 것은 아니라는 점을 이해하는 것이 중요합니다. 따라서 이러한 상황을 통해 사용자를 안내하는 기본 사용자 인터페이스를 제공해도 됩니다.
PWA는 웹 렌더링 엔진이 페이지를 로드할 수 없다는 브라우저의 오류 메시지를 렌더링해서는 안 됩니다. 대신 서비스 워커를 사용하여 자체 메시지를 표시하여 혼란스러운 일반적인 브라우저 오류를 방지합니다.
PWA의 요구사항에 따라 사용할 수 있는 다양한 캐싱 전략이 있습니다. 따라서 빠르고 안정적인 환경을 제공할 수 있도록 캐시 사용을 설계하는 것이 중요합니다. 예를 들어 모든 앱 애셋이 빠르게 다운로드되고, 공간을 많이 차지하지 않으며, 모든 요청에서 업데이트할 필요가 없는 경우 모든 애셋을 캐시하는 것이 적절한 전략입니다. 반면에 최신 버전이어야 하는 리소스가 있는 경우 이러한 애셋을 전혀 캐시하지 않는 것이 좋습니다.
API 사용
Cache Storage API를 사용하여 출처 내에서 각 캐시가 정의할 수 있는 문자열 이름으로 식별되는 캐시 집합을 정의합니다. caches
객체를 통해 API에 액세스하고 open
메서드를 사용하면 이미 생성된 캐시를 만들거나 열 수 있습니다. open 메서드는 캐시 객체의 프로미스를 반환합니다.
caches.open("pwa-assets")
.then(cache => {
// you can download and store, delete or update resources with cache arguments
});
애셋 다운로드 및 저장
브라우저에 애셋을 다운로드하고 저장하도록 요청하려면 add
또는 addAll
메서드를 사용합니다. add
메서드는 요청을 하고 HTTP 응답 1개를 저장하며, addAll
는 요청 또는 URL 배열을 기반으로 HTTP 응답 그룹을 트랜잭션으로 저장합니다.
caches.open("pwa-assets")
.then(cache => {
cache.add("styles.css"); // it stores only one resource
cache.addAll(["styles.css", "app.js"]); // it stores two resources
});
캐시 저장소 인터페이스는 모든 헤더와 본문을 포함한 응답 전체를 저장합니다. 따라서 나중에 HTTP 요청이나 URL을 키로 사용하여 검색할 수 있습니다. 게재 챕터에서 방법을 알아보세요.
캐시해야 하는 경우
PWA에서는 파일을 캐시할 시기를 개발자가 결정합니다. 서비스 워커가 설치될 때 최대한 많은 애셋을 저장하는 방법도 있지만 일반적으로 권장되지 않습니다. 불필요한 리소스를 캐시하면 대역폭과 저장용량이 낭비되고 앱에서 의도치 않게 오래된 리소스를 제공할 수 있습니다.
모든 애셋을 한 번에 캐시할 필요는 없습니다. 다음과 같이 PWA의 수명 주기 중에 애셋을 여러 번 캐시할 수 있습니다.
- 서비스 워커 설치 시
- 첫 번째 페이지 로드 후
- 사용자가 섹션 또는 경로로 이동할 때
- 네트워크가 유휴 상태일 때
기본 스레드 또는 서비스 워커 컨텍스트에서 새 파일 캐싱을 요청할 수 있습니다.
서비스 워커에서 애셋 캐싱
가장 일반적인 시나리오 중 하나는 서비스 워커가 설치될 때 최소 애셋 세트를 캐시하는 것입니다. 이렇게 하려면 서비스 워커의 install
이벤트 내에서 캐시 저장소 인터페이스를 사용하면 됩니다.
서비스 워커 스레드는 언제든지 중지될 수 있으므로 브라우저에 addAll
약속이 완료될 때까지 기다리도록 요청하여 모든 애셋을 저장하고 앱의 일관성을 유지할 가능성을 높일 수 있습니다. 다음 예에서는 서비스 워커 이벤트 리스너에서 수신된 이벤트 인수의 waitUntil
메서드를 사용하여 이 작업을 수행하는 방법을 보여줍니다.
const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", event => {
event.waitUntil(
caches.open("pwa-assets")
.then(cache => {
return cache.addAll(urlsToCache);
});
);
});
waitUntil()
메서드는 프라미스를 수신하고 브라우저에 서비스 워커 프로세스를 종료하기 전에 프라미스의 태스크가 확인될 때까지 (성공 또는 실패) 기다리라고 요청합니다. 단일 결과가 waitUntil()
메서드에 도달하도록 프로미스를 연결하고 add()
또는 addAll()
호출을 반환해야 할 수 있습니다.
async/await 문법을 사용하여 프라미스를 처리할 수도 있습니다. 이 경우 다음 예와 같이 await
를 호출할 수 있고 호출된 후 waitUntil()
에 약속을 반환하는 비동기 함수를 만들어야 합니다.
const urlsToCache = ["/", "app.js", "styles.css", "logo.svg"];
self.addEventListener("install", (event) => {
let cacheUrls = async () => {
const cache = await caches.open("pwa-assets");
return cache.addAll(urlsToCache);
};
event.waitUntil(cacheUrls());
});
교차 도메인 요청 및 불투명 응답
PWA는 출처 및 교차 도메인(예: 서드 파티 CDN의 콘텐츠)의 애셋을 다운로드하고 캐시할 수 있습니다. 교차 도메인 앱의 경우 캐시 상호작용이 동일 출처 요청과 매우 유사합니다. 요청이 실행되고 응답 사본이 캐시에 저장됩니다. 다른 캐시된 애셋과 마찬가지로 앱의 출처에서만 사용할 수 있습니다.
애셋은 불투명 응답으로 저장되므로 코드에서 해당 응답의 콘텐츠나 헤더를 보거나 수정할 수 없습니다. 또한 불투명 응답은 스토리지 API에 실제 크기를 노출하지 않으므로 할당량에 영향을 미칩니다. 일부 브라우저는 파일이 1KB인지와 관계없이 7MB와 같은 큰 크기를 노출합니다.
애셋 업데이트 및 삭제
cache.put(request, response)
를 사용하여 저작물을 업데이트하고 delete(request)
를 사용하여 저작물을 삭제할 수 있습니다.
자세한 내용은 캐시 객체 문서를 참고하세요.
캐시 저장소 디버깅
많은 브라우저에서는 DevTools 애플리케이션 탭 내에서 캐시 저장소의 콘텐츠를 디버그하는 방법을 제공합니다. 여기에서 현재 출처 내의 모든 캐시 콘텐츠를 확인할 수 있습니다. 이러한 도구에 관한 자세한 내용은 도구 및 디버그 챕터에서 다룹니다.