런타임 중 리소스 캐싱

웹 애플리케이션의 일부 애셋은 자주 사용되지 않거나 매우 크거나 사용자 기기 (예: 반응형 이미지) 또는 언어에 따라 다를 수 있습니다. 이러한 경우 프리캐싱이 피해야 할 패턴이 될 수 있으므로 런타임 캐싱을 대신 사용해야 합니다.

Workbox에서는 경로를 일치시키는 workbox-routing 모듈을 사용하여 애셋의 런타임 캐싱을 처리하고 workbox-strategies 모듈로 이에 대한 캐싱 전략을 처리할 수 있습니다.

캐싱 전략

기본 제공되는 캐싱 전략 중 하나를 사용하여 대부분의 애셋 경로를 처리할 수 있습니다. 이러한 내용은 이 문서의 앞부분에서 자세히 다루겠지만, 다음은 복습할 가치가 있는 몇 가지 사항입니다.

  • 재검증 중 비활성은 요청에 캐시된 응답을 사용할 수 있는 경우 이를 사용하고 네트워크의 응답으로 백그라운드에서 캐시를 업데이트합니다. 따라서 애셋이 캐시되지 않은 경우 네트워크 응답을 기다렸다가 이를 사용합니다. 이 방법을 사용하는 캐시 항목을 정기적으로 업데이트하므로 상당히 안전한 전략입니다. 단점은 항상 백그라운드에서 네트워크의 애셋을 요청한다는 것입니다.
  • 네트워크 우선은 먼저 네트워크에서 응답을 가져오려고 시도합니다. 응답이 수신되면 브라우저에 응답을 전달하고 캐시에 저장합니다. 네트워크 요청이 실패하면 마지막으로 캐시된 응답이 사용되어 애셋에 오프라인으로 액세스할 수 있습니다.
  • 캐시 우선은 캐시에서 응답을 먼저 확인하고 가능한 경우 응답을 사용합니다. 요청이 캐시에 없으면 네트워크가 사용되며 유효한 응답은 브라우저로 전달되기 전에 캐시에 추가됩니다.
  • 네트워크 전용: 네트워크에서 응답을 강제로 보냅니다.
  • 캐시 전용: 응답을 캐시에서 가져오도록 합니다.

workbox-routing에서 제공하는 메서드를 사용하여 이러한 전략을 일부 요청에 적용할 수 있습니다.

경로 일치로 캐싱 전략 적용

workbox-routing는 경로를 일치시키고 캐싱 전략으로 처리하기 위해 registerRoute 메서드를 노출합니다. registerRoute는 두 개의 인수를 허용하는 Route 객체를 허용합니다.

  1. 경로 일치 기준을 지정하기 위한 문자열, 정규 표현식 또는 일치 콜백입니다.
  2. 경로의 핸들러입니다. 일반적으로 workbox-strategies에서 제공하는 전략입니다.

일치 콜백은 Request 객체, 요청 URL 문자열, 가져오기 이벤트, 요청이 동일 출처 요청인지에 대한 불리언이 포함된 컨텍스트 객체를 제공하므로 경로를 일치시키는 데 선호됩니다.

그러면 핸들러가 일치하는 경로를 처리합니다. 다음 예에서는 수신되는 동일 출처 이미지 요청과 일치하는 새 경로가 생성되어 캐시를 먼저 저장하고 네트워크 전략으로 폴백합니다.

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';

// A new route that matches same-origin image requests and handles
// them with the cache-first, falling back to network strategy:
const imageRoute = new Route(({ request, sameOrigin }) => {
  return sameOrigin && request.destination === 'image'
}, new CacheFirst());

// Register the new route
registerRoute(imageRoute);

여러 캐시 사용

Workbox를 사용하면 번들 전략에서 사용 가능한 cacheName 옵션을 사용하여 캐시된 응답을 별도의 Cache 인스턴스로 버케팅할 수 있습니다.

다음 예에서 이미지는 오래된 기간 재검증 전략을 사용하는 반면 CSS 및 자바스크립트 애셋은 캐시 우선 대체 네트워크 전략을 사용합니다. 각 애셋의 경로는 cacheName 속성을 추가하여 응답을 별도의 캐시에 배치합니다.

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst, StaleWhileRevalidate } from 'workbox-strategies';

// Handle images:
const imageRoute = new Route(({ request }) => {
  return request.destination === 'image'
}, new StaleWhileRevalidate({
  cacheName: 'images'
}));

// Handle scripts:
const scriptsRoute = new Route(({ request }) => {
  return request.destination === 'script';
}, new CacheFirst({
  cacheName: 'scripts'
}));

// Handle styles:
const stylesRoute = new Route(({ request }) => {
  return request.destination === 'style';
}, new CacheFirst({
  cacheName: 'styles'
}));

// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);
registerRoute(stylesRoute);
Chrome DevTools의 애플리케이션 탭에 있는 캐시 인스턴스 목록의 스크린샷. 'scripts'라는 이름, 'styles'라는 이름, 'images'라는 이름의 캐시라는 세 가지 캐시가 표시됩니다.
Chrome DevTools의 Application 패널에 있는 Cache Storage 뷰어 다양한 애셋 유형에 대한 응답은 별도의 캐시에 저장됩니다.

캐시 항목의 만료 설정

서비스 워커 캐시를 관리할 때는 스토리지 할당량을 숙지하세요. ExpirationPlugin는 캐시 유지관리를 간소화하고 workbox-expiration에 의해 노출됩니다. 이 속성을 사용하려면 캐싱 전략 구성에서 이를 지정합니다.

// sw.js
import { registerRoute, Route } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';

// Evict image cache entries older thirty days:
const imageRoute = new Route(({ request }) => {
  return request.destination === 'image';
}, new CacheFirst({
  cacheName: 'images',
  plugins: [
    new ExpirationPlugin({
      maxAgeSeconds: 60 * 60 * 24 * 30,
    })
  ]
}));

// Evict the least-used script cache entries when
// the cache has more than 50 entries:
const scriptsRoute = new Route(({ request }) => {
  return request.destination === 'script';
}, new CacheFirst({
  cacheName: 'scripts',
  plugins: [
    new ExpirationPlugin({
      maxEntries: 50,
    })
  ]
}));

// Register routes
registerRoute(imageRoute);
registerRoute(scriptsRoute);

저장용량 한도를 준수하는 것은 복잡할 수 있습니다. 저장용량 압력을 겪고 있거나 저장용량을 가장 효율적으로 활용하려는 사용자를 고려하는 것이 좋습니다. Workbox의 ExpirationPlugin 쌍이 이 목표를 달성하는 데 도움이 될 수 있습니다.

교차 출처 고려사항

서비스 워커와 교차 출처 애셋 간의 상호작용은 동일 출처 애셋에서의 상호작용과 상당히 다릅니다. 교차 출처 리소스 공유 (CORS)는 복잡하며, 이러한 복잡성은 서비스 워커에서 교차 출처 리소스를 처리하는 방식으로 확장됩니다.

불투명 응답

no-cors 모드에서 교차 출처 요청을 하면 응답은 서비스 워커 캐시에 저장될 수 있으며 브라우저에서 직접 사용할 수도 있습니다. 그러나 응답 본문 자체는 JavaScript를 통해 읽을 수 없습니다. 이를 불투명 응답이라고 합니다.

불투명 응답은 교차 출처 애셋 검사를 방지하기 위한 보안 조치입니다. 여전히 교차 출처 애셋을 요청하고 이를 캐시할 수도 있지만, 응답 본문을 읽거나 상태 코드를 읽을 수도 없습니다.

CORS 모드를 사용해야 합니다.

응답을 읽을 수 있는 허용 CORS 헤더를 설정하는 교차 출처 애셋을 로드하더라도 교차 출처 응답의 본문이 여전히 불투명할 수 있습니다. 예를 들어 다음 HTML은 설정된 CORS 헤더와 관계없이 불투명한 응답으로 연결되는 no-cors 요청을 트리거합니다.

<link rel="stylesheet" href="https://example.com/path/to/style.css">
<img src="https://example.com/path/to/image.png">

불투명하지 않은 응답을 생성하는 cors 요청을 명시적으로 트리거하려면 crossorigin 속성을 HTML에 추가하여 CORS 모드를 명시적으로 선택해야 합니다.

<link crossorigin="anonymous" rel="stylesheet" href="https://example.com/path/to/style.css">
<img crossorigin="anonymous" src="https://example.com/path/to/image.png">

이는 서비스 워커의 경로가 런타임에 로드되는 하위 리소스를 캐시하는 시점을 기억해야 합니다.

Workbox에서 불투명한 응답을 캐시하지 않을 수 있음

기본적으로 Workbox는 불투명 응답을 캐시할 때 신중을 기합니다. 응답 코드에서 불투명 응답을 검사할 수 없으므로 오류 응답을 캐시하면 캐시 우선 전략이나 캐시 전용 전략을 사용할 경우 환경이 지속적으로 손상될 수 있습니다.

Workbox에서 불투명 응답을 캐시해야 하는 경우 네트워크 우선 또는 오래된 검증 전략을 사용하여 처리해야 합니다. 예. 이렇게 하면 애셋이 여전히 매번 네트워크에서 요청되지만 실패한 응답은 유지되지 않으며 결국 사용 가능한 응답으로 대체됩니다.

다른 캐싱 전략을 사용 중이고 불투명 응답이 반환되면 Workbox는 개발 모드에서 응답이 캐시되지 않았다는 경고 메시지를 표시합니다.

불투명 응답의 강제 캐싱

캐시 우선 또는 캐시 전용 전략을 사용하여 불투명 응답을 캐시하려는 것이 확실한 경우 Workbox에서 workbox-cacheable-response 모듈을 사용하여 캐시하도록 강제할 수 있습니다.

import {Route, registerRoute} from 'workbox-routing';
import {NetworkFirst, StaleWhileRevalidate} from 'workbox-strategies';
import {CacheableResponsePlugin} from 'workbox-cacheable-response';

const cdnRoute = new Route(({url}) => {
  return url === 'https://cdn.google.com/example-script.min.js';
}, new CacheFirst({
  plugins: [
    new CacheableResponsePlugin({
      statuses: [0, 200]
    })
  ]
}))

registerRoute(cdnRoute);

불투명 응답 및 navigator.storage API

교차 도메인 정보의 유출을 방지하기 위해 저장용량 한도 계산에 사용되는 불투명 응답의 크기에 상당한 패딩이 추가됩니다. 이는 navigator.storage API의 저장용량 할당량 보고 방식에 영향을 미칩니다.

이 패딩은 브라우저에 따라 다르지만, Chrome의 경우 캐시된 단일 불투명 응답이 사용되는 전체 저장용량에 기여하는 최소 크기는 약 7MB입니다. 캐시할 불투명 응답 수를 결정할 때 이 점에 유의해야 합니다. 예상보다 훨씬 빨리 저장용량 할당량을 초과할 수 있기 때문입니다.