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

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

측정

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

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

방금 열린 새 탭에서 웹사이트가 오프라인 상태일 때 어떻게 작동하는지 확인합니다.

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

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

브라우저의 기본 오프라인 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);
  });
}

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

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

오프라인 쿼리 유지 및 다시 온라인 상태일 때 다시 시도

그런 다음 Workbox 백그라운드 동기화를 구현하여 오프라인 쿼리를 유지합니다. 그러면 브라우저에서 연결이 복구되었음을 감지할 때 쿼리를 다시 시도할 수 있습니다.

  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. Throttling(제한) 드롭다운 목록을 다시 Online(온라인)으로 설정합니다.

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

전체 오프라인 흐름의 스크린샷

결론

Workbox는 PWA의 회복탄력성과 참여도를 높이는 여러 내장 기능을 제공합니다. 이 Codelab에서는 오프라인 사용자 쿼리가 손실되지 않고 연결이 복원되면 다시 시도할 수 있도록 Workbox 추상화를 통해 백그라운드 동기화 API를 구현하는 방법을 살펴봤습니다. 이 데모는 간단한 검색 앱이지만 채팅 앱, 소셜 네트워크에 메시지 게시 등 더 복잡한 시나리오와 사용 사례에 유사한 구현을 사용할 수 있습니다.