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

Demián Renzulli
Demián Renzulli

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

측정

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

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

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

  1. `Control+Shift+J` (또는 Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.
  2. 네트워크 탭을 클릭합니다.
  3. Chrome DevTools를 열고 '네트워크' 패널을 선택합니다.
  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 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 추상화를 통해 백그라운드 동기화 API를 구현하여 오프라인 사용자 쿼리가 손실되지 않고 연결이 다시 설정되면 다시 시도할 수 있도록 하는 방법을 살펴봤습니다. 데모는 간단한 검색 앱이지만 채팅 앱, 소셜 네트워크에 메시지 게시 등 더 복잡한 시나리오와 사용 사례에도 유사한 구현을 사용할 수 있습니다.