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

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

측정

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

  • 수정할 리믹스를 클릭하여 프로젝트를 수정할 수 있도록 합니다.
  • 사이트를 미리 보려면 앱 보기를 누릅니다. 그런 다음 전체 화면 전체 화면입니다.

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

  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에서 Disable cache 체크박스가 사용 중지되어 있는지 확인합니다.
  5. Chrome의 새로고침 버튼을 길게 누르고 캐시 비우기 및 강제 새로고침 서비스 워커가 업데이트되도록 합니다
  6. 제한 드롭다운 목록을 다시 오프라인으로 설정합니다.
  7. 검색어를 입력하고 검색 버튼을 다시 클릭합니다.

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

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

알림 권한 요청

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

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

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

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

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

오프라인 쿼리를 유지하고 온라인 상태가 되었을 때 재시도

다음으로 Workbox Background Sync를 구현하여 오프라인 쿼리를 유지합니다. 이렇게 하면 브라우저에서 연결이 되돌려졌음을 감지할 때 쿼리를 다시 시도할 수 있습니다.

  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를 구현하는 방법을 알아봤습니다. 데모는 간단한 검색 앱이지만 채팅 앱, 소셜 네트워크에 메시지 게시 등 더 복잡한 시나리오와 사용 사례에도 유사한 구현을 사용할 수 있습니다.