캐시 스토리지는 강력한 도구입니다. 앱이 네트워크 상태에 덜 의존하게 됩니다. 캐시를 잘 사용하면 오프라인에서 웹 앱을 사용할 수 있고 모든 네트워크 조건에서 애셋을 최대한 빠르게 제공할 수 있습니다. 애셋 및 데이터에 설명된 대로 필요한 애셋을 캐시하는 최적의 전략을 결정할 수 있습니다. 서비스 워커가 상호작용하는 캐시를 관리하려면 Cache Storage API를 사용하세요.
캐시 스토리지 API는 다음과 같은 다양한 컨텍스트에서 사용할 수 있습니다.
- 창 컨텍스트 (PWA의 기본 스레드)
- 서비스 워커
- 사용하는 다른 작업자
서비스 워커를 사용하여 캐시를 관리하는 한 가지 이점은 수명 주기가 창에 연결되지 않아 기본 스레드를 차단하지 않는다는 것입니다. Cache Storage API를 사용하려면 이러한 컨텍스트 대부분이 TLS 연결 아래에 있어야 합니다.
캐시할 항목
캐싱에 관해 가장 먼저 떠오르는 질문은 무엇을 캐시해야 하는지일 것입니다. 이 질문에 대한 단일 답변은 없지만 사용자 인터페이스를 렌더링하는 데 필요한 모든 최소 리소스로 시작할 수 있습니다.
이러한 리소스에는 다음이 포함되어야 합니다.
- 기본 페이지 HTML (앱의 start_url)
- 기본 사용자 인터페이스에 필요한 CSS 스타일시트입니다.
- 사용자 인터페이스에 사용되는 이미지입니다.
- 사용자 인터페이스를 렌더링하는 데 필요한 JavaScript 파일입니다.
- 기본 환경을 렌더링하는 데 필요한 데이터(예: JSON 파일)
- 웹 글꼴
- 여러 페이지로 구성된 애플리케이션에서 오프라인 상태이거나 빠르게 제공하려는 다른 HTML 문서
오프라인 사용 가능
오프라인 지원은 프로그레시브 웹 앱의 요구사항 중 하나이지만, 클라우드 게임 솔루션이나 암호화폐 앱과 같이 모든 PWA에 완전한 오프라인 환경이 필요한 것은 아닙니다. 따라서 사용자에게 이러한 상황을 안내하는 기본 사용자 인터페이스를 제공해도 됩니다.
PWA는 웹 렌더링 엔진이 페이지를 로드할 수 없다는 브라우저의 오류 메시지를 렌더링해서는 안 됩니다. 대신 서비스 워커를 사용하여 자체 메시지를 표시하여 일반적이고 혼란스러운 브라우저 오류를 방지하세요.
PWA의 요구사항에 따라 사용할 수 있는 다양한 캐싱 전략이 있습니다. 따라서 빠르고 안정적인 환경을 제공하도록 캐시 사용을 설계하는 것이 중요합니다. 예를 들어 모든 앱 애셋이 빠르게 다운로드되고, 많은 공간을 차지하지 않으며, 모든 요청에서 업데이트할 필요가 없는 경우 모든 애셋을 캐싱하는 것이 유효한 전략입니다. 반면에 최신 버전이어야 하는 리소스가 있는 경우 이러한 애셋을 전혀 캐시하지 않는 것이 좋습니다.
API 사용
캐시 스토리지 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 응답을 저장하며, 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)
를 사용하여 애셋을 삭제할 수 있습니다.
자세한 내용은 캐시 객체 문서를 참고하세요.
캐시 저장소 디버깅
많은 브라우저에서는 개발자 도구 애플리케이션 탭 내에서 캐시 저장소의 콘텐츠를 디버깅하는 방법을 제공합니다. 여기에서 현재 출처 내 모든 캐시의 콘텐츠를 확인할 수 있습니다. 이러한 도구에 관한 자세한 내용은 도구 및 디버그 챕터에서 다루겠습니다.