Workbox로 복원력이 우수한 검색 환경 빌드

이 Codelab에서는 Workbox로 복원력이 우수한 검색 환경을 구현하는 방법을 보여줍니다. 앱에서 사용하는 데모 앱에는 서버 엔드포인트를 호출하고 사용자를 기본 HTML 페이지로 리디렉션하는 검색창이 포함되어 있습니다.

측정

최적화를 추가하기 전에 항상 애플리케이션의 현재 상태를 먼저 분석하는 것이 좋습니다.

  • 리믹스하여 수정을 클릭하여 프로젝트를 수정할 수 있도록 합니다.
  • 사이트를 미리 보려면 View App을 누른 다음 Fullscreen 전체 화면을 누릅니다.

새로 열린 탭에서 웹사이트가 오프라인으로 전환될 때 어떻게 작동하는지 확인합니다.

  1. `Control+Shift+J` (Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.
  2. 네트워크 탭을 클릭합니다.
  3. Chrome DevTools를 열고 Network 패널을 선택합니다.
  4. 제한 드롭다운 목록에서 오프라인을 선택합니다.
  5. 데모 앱에서 검색어를 입력한 다음 Search 버튼을 클릭합니다.

표준 브라우저 오류 페이지가 표시됩니다.

브라우저의 기본 오프라인 UX 스크린샷

대체 응답 제공

서비스 워커에는 오프라인 페이지를 사전 캐시 목록에 추가하는 코드가 포함되어 있으므로 서비스 워커 install 이벤트에서 항상 캐시될 수 있습니다.

일반적으로 라이브러리를 원하는 빌드 도구 (예: webpack 또는 gulp)와 통합하여 빌드 시 이 파일을 사전 캐시 목록에 추가하도록 Workbox에 지시해야 합니다.

편의상 이 작업을 이미 완료하였습니다. public/sw.js의 다음 코드가 이를 실행합니다.

const FALLBACK_HTML_URL = '/index_offline.html';
…
workbox.precaching.precacheAndRoute([FALLBACK_HTML_URL]);

그런 다음 오프라인 페이지를 대체 응답으로 사용하는 코드를 추가합니다.

  1. 소스를 보려면 소스 보기를 누릅니다.
  2. public/sw.js 하단에 다음 코드를 추가합니다.
workbox.routing.setDefaultHandler(new workbox.strategies.NetworkOnly());

workbox.routing.setCatchHandler(({event}) => {
  switch (event.request.destination) {
    case 'document':
      return caches.match(FALLBACK_HTML_URL);
      break;
    default:
      return Response.error();
  }
});

위 코드의 기능은 다음과 같습니다.

  • 모든 요청에 적용할 기본 네트워크 전용 전략을 정의합니다.
  • workbox.routing.setCatchHandler()를 호출하여 실패한 요청을 관리하여 전역 오류 핸들러를 선언합니다. 문서에 대한 요청인 경우 대체 오프라인 HTML 페이지가 반환됩니다.

이 기능을 테스트하려면 다음 단계를 따르세요.

  1. 앱을 실행 중인 다른 탭으로 돌아갑니다.
  2. 제한 드롭다운 목록을 다시 온라인으로 설정합니다.
  3. Chrome의 뒤로 버튼을 눌러 검색 페이지로 돌아갑니다.
  4. DevTools에서 캐시 사용 중지 체크박스가 사용 중지되어 있는지 확인합니다.
  5. Chrome의 새로고침 버튼을 길게 누르고 캐시 비우기 및 하드 리로드를 선택하여 서비스 워커가 업데이트되었는지 확인합니다.
  6. 제한 드롭다운 목록을 다시 오프라인으로 설정합니다.
  7. 검색어를 입력하고 검색 버튼을 다시 클릭합니다.

다음과 같이 대체 HTML 페이지가 표시됩니다.

브라우저의 맞춤 오프라인 UX 스크린샷

알림 권한 요청

편의상 views/index_offline.html의 오프라인 페이지 하단에 있는 스크립트 블록에 알림 권한을 요청하는 코드가 이미 포함되어 있습니다.

function requestNotificationPermission(event) {
  event.preventDefault();

  Notification.requestPermission().then(function (result) {
    showOfflineText(result);
  });
}

위 코드의 기능은 다음과 같습니다.

  • 사용자가 알림 구독을 클릭하면 Notification.requestPermission()를 호출하는 requestNotificationPermission() 함수가 호출되어 기본 브라우저 권한 메시지를 표시합니다. 프로미스는 사용자가 선택한 권한(granted, denied 또는 default)으로 확인됩니다.
  • 확인된 권한을 showOfflineText()에 전달하여 사용자에게 적절한 텍스트를 표시합니다.

오프라인 쿼리를 유지하고 다시 온라인 상태가 되면 재시도

그런 다음 작업 상자 백그라운드 동기화를 구현하여 오프라인 쿼리를 유지합니다. 그러면 브라우저에서 연결이 반환된 것을 감지할 때 쿼리를 다시 시도할 수 있습니다.

  1. 수정하려면 public/sw.js 앱을 여세요.
  2. 파일 끝에 다음 코드를 추가합니다.
const bgSyncPlugin = new workbox.backgroundSync.Plugin('offlineQueryQueue', {
  maxRetentionTime: 60,
  onSync: async ({queue}) => {
    let entry;
    while ((entry = await queue.shiftRequest())) {
      try {
        const response = await fetch(entry.request);
        const cache = await caches.open('offline-search-responses');
        const offlineUrl = `${entry.request.url}&notification=true`;
        cache.put(offlineUrl, response);
        showNotification(offlineUrl);
      } catch (error) {
        await this.unshiftRequest(entry);
        throw error;
      }
    }
  },
});

위 코드의 기능은 다음과 같습니다.

  • workbox.backgroundSync.Plugin에는 실패한 요청을 나중에 재시도할 수 있도록 큐에 추가하는 로직이 포함됩니다. 이러한 요청은 IndexedDB에서 유지됩니다.
  • maxRetentionTime는 요청을 재시도할 수 있는 시간을 나타냅니다. 여기에서는 60분을 선택했습니다 (이후에는 삭제됩니다).
  • onSync는 이 코드에서 가장 중요한 부분입니다. 연결이 복구되면 이 콜백이 호출되어 대기 중인 요청을 가져온 다음 네트워크에서 가져옵니다.
  • 네트워크 응답이 offline-search-responses 캐시에 추가되어 &notification=true 쿼리 매개변수가 추가되어 사용자가 알림을 클릭할 때 이 캐시 항목이 선택될 수 있습니다.

백그라운드 동기화를 서비스와 통합하려면 검색 URL (/search_action)에 대한 요청에 대한 NetworkOnly 전략을 정의하고 이전에 정의된 bgSyncPlugin를 전달합니다. public/sw.js 하단에 다음 코드를 추가합니다.

const matchSearchUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return url.pathname === '/search_action' && !(notificationParam === 'true');
};

workbox.routing.registerRoute(
  matchSearchUrl,
  new workbox.strategies.NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
);

이렇게 하면 Workbox가 항상 네트워크로 이동하고 요청이 실패하면 백그라운드 동기화 로직을 사용하도록 지시합니다.

이제 public/sw.js 하단에 다음 코드를 추가하여 알림에서 발생하는 요청의 캐싱 전략을 정의합니다. 캐시에서 제공될 수 있도록 CacheFirst 전략을 사용합니다.

const matchNotificationUrl = ({url}) => {
  const notificationParam = url.searchParams.get('notification');
  return (url.pathname === '/search_action' && (notificationParam === 'true'));
};

workbox.routing.registerRoute(matchNotificationUrl,
  new workbox.strategies.CacheFirst({
     cacheName: 'offline-search-responses',
  })
);

마지막으로 알림을 표시하는 코드를 추가합니다.

function showNotification(notificationUrl) {
  if (Notification.permission) {
     self.registration.showNotification('Your search is ready!', {
        body: 'Click to see you search result',
        icon: '/img/workbox.jpg',
        data: {
           url: notificationUrl
        }
     });
  }
}

self.addEventListener('notificationclick', function(event) {
  event.notification.close();
  event.waitUntil(
     clients.openWindow(event.notification.data.url)
  );
});

기능 테스트

  1. 앱을 실행 중인 다른 탭으로 돌아갑니다.
  2. 제한 드롭다운 목록을 다시 온라인으로 설정합니다.
  3. Chrome의 뒤로 버튼을 눌러 검색 페이지로 돌아갑니다.
  4. Chrome의 새로고침 버튼을 길게 누르고 캐시 비우기 및 하드 리로드를 선택하여 서비스 워커가 업데이트되었는지 확인합니다.
  5. 제한 드롭다운 목록을 다시 오프라인으로 설정합니다.
  6. 검색어를 입력하고 검색 버튼을 다시 클릭합니다.
  7. 알림 구독을 클릭합니다.
  8. Chrome에서 앱에 알림 전송 권한을 부여할지 묻는 메시지가 표시되면 허용을 클릭합니다.
  9. 다른 검색어를 입력하고 다시 검색 버튼을 클릭합니다.
  10. 제한 드롭다운 목록을 다시 온라인으로 설정합니다.

다시 연결되면 알림이 표시됩니다.

전체 오프라인 절차의 스크린샷

결론

Workbox는 PWA를 더 탄력적이고 매력적으로 만들 수 있는 여러 내장 기능을 제공합니다. 이 Codelab에서는 오프라인 사용자 쿼리가 손실되지 않고 다시 연결되었을 때 재시도할 수 있도록 Workbox 추상화를 통해 Background Sync API를 구현하는 방법을 알아봤습니다. 데모는 간단한 검색 앱이지만 채팅 앱, 소셜 네트워크에 메시지 게시 등 더 복잡한 시나리오와 사용 사례에 비슷한 구현을 사용할 수 있습니다.