작업 상자 백그라운드 동기화

웹 서버로 데이터를 전송할 때 요청이 실패하는 경우가 있습니다. 사용자의 연결이 끊어졌거나 서버가 다운되었기 때문일 수 있습니다. 어떤 경우든 나중에 요청을 다시 보내려고 하는 경우가 많습니다.

BackgroundSync API는 이 문제를 해결하는 이상적인 솔루션입니다. 서비스 워커는 네트워크 요청이 실패한 것을 감지하면 sync 이벤트를 수신하도록 등록할 수 있습니다. 이 이벤트는 브라우저에서 연결이 반환되었다고 생각할 때 전달됩니다. 동기화 이벤트는 사용자가 애플리케이션을 나간 경우에도 전송될 수 있으므로 실패한 요청을 다시 시도하는 기존 방법보다 훨씬 효과적입니다.

작업 상자 백그라운드 동기화는 BackgroundSync API를 더 쉽게 사용하고 다른 Workbox 모듈과 사용을 통합할 수 있도록 설계되었습니다. 또한 아직 BackgroundSync를 구현하지 않은 브라우저를 위한 대체 전략도 구현합니다.

BackgroundSync API를 지원하는 브라우저는 브라우저에서 관리하는 간격에 따라 실패한 요청을 자동으로 재생합니다. 재생 시도 사이에 지수 백오프를 사용할 수 있습니다. 기본적으로 BackgroundSync API를 지원하지 않는 브라우저에서는 서비스 워커가 시작될 때마다 Workbox 백그라운드 동기화가 자동으로 재생을 시도합니다.

기본 사용법

백그라운드 동기화를 사용하는 가장 쉬운 방법은 실패한 요청을 자동으로 큐에 추가하고 향후 sync 이벤트가 발생할 때 다시 시도하는 Plugin를 사용하는 것입니다.

import {BackgroundSyncPlugin} from 'workbox-background-sync';
import {registerRoute} from 'workbox-routing';
import {NetworkOnly} from 'workbox-strategies';

const bgSyncPlugin = new BackgroundSyncPlugin('myQueueName', {
  maxRetentionTime: 24 * 60, // Retry for max of 24 Hours (specified in minutes)
});

registerRoute(
  /\/api\/.*\/*.json/,
  new NetworkOnly({
    plugins: [bgSyncPlugin],
  }),
  'POST'
);

BackgroundSyncPluginfetchDidFail 플러그인 콜백에 후크되며, 네트워크 장애로 인해 예외가 발생할 때만 fetchDidFail가 호출됩니다. 즉, 4xx 또는 5xx 오류 상태로 수신된 응답이 있으면 요청이 재시도되지 않습니다. 예를 들어 5xx 상태가 발생하는 모든 요청을 재시도하려면 전략에 fetchDidSucceed 플러그인을 추가하면 됩니다.

const statusPlugin = {
  fetchDidSucceed: ({response}) => {
    if (response.status >= 500) {
      // Throwing anything here will trigger fetchDidFail.
      throw new Error('Server error.');
    }
    // If it's not 5xx, use the response as-is.
    return response;
  },
};

// Add statusPlugin to the plugins array in your strategy.

고급 사용법

Workbox 백그라운드 동기화는 실패한 요청을 인스턴스화하고 추가할 수 있는 Queue 클래스도 제공합니다. 실패한 요청은 IndexedDB에 저장되며 브라우저에서 연결이 복원되었다고 판단하면 (즉, 동기화 이벤트를 수신할 때) 재시도됩니다.

큐 만들기

작업 상자 백그라운드 동기화 큐를 만들려면 큐 이름 (원본에 고유해야 함)으로 큐를 구성해야 합니다.

import {Queue} from 'workbox-background-sync';

const queue = new Queue('myQueueName');

큐 이름은 전역 SyncManager에 의해 register()으로 지정된 태그 이름의 일부로 사용됩니다. IndexedDB 데이터베이스의 객체 저장소 이름으로도 사용됩니다.

큐에 요청 추가

큐 인스턴스를 만든 후에는 실패한 요청을 추가할 수 있습니다. .pushRequest() 메서드를 호출하여 실패한 요청을 추가합니다. 예를 들어 다음 코드는 실패한 요청을 포착하여 큐에 추가합니다.

import {Queue} from 'workbox-background-sync';

const queue = new Queue('myQueueName');

self.addEventListener('fetch', event => {
  // Add in your own criteria here to return early if this
  // isn't a request that should use background sync.
  if (event.request.method !== 'POST') {
    return;
  }

  const bgSyncLogic = async () => {
    try {
      const response = await fetch(event.request.clone());
      return response;
    } catch (error) {
      await queue.pushRequest({request: event.request});
      return error;
    }
  };

  event.respondWith(bgSyncLogic());
});

대기열에 추가되면 서비스 워커가 sync 이벤트를 수신할 때 (브라우저에서 연결이 복원되었다고 생각할 때 발생) 요청이 자동으로 재시도됩니다. BackgroundSync API를 지원하지 않는 브라우저는 서비스 워커가 시작될 때마다 큐를 다시 시도합니다. 서비스 워커가 실행되도록 제어하는 페이지가 필요하므로 이 페이지는 효과적이지 않습니다.

Workbox 백그라운드 동기화 테스트

안타깝게도 BackgroundSync 테스트는 여러 가지 이유로 다소 직관적이지 않고 어렵습니다.

구현을 테스트하는 가장 좋은 방법은 다음을 실행하는 것입니다.

  1. 페이지를 로드하고 서비스 워커를 등록합니다.
  2. 컴퓨터의 네트워크를 끄거나 웹 서버를 끕니다.
    • Chrome 개발자 도구를 오프라인으로 사용하면 안 됩니다. DevTools의 오프라인 체크박스는 페이지의 요청에만 영향을 미칩니다. 서비스 워커 요청은 계속 진행될 것입니다.
  3. Workbox 백그라운드 동기화로 대기열에 추가해야 하는 네트워크 요청을 만듭니다.
    • Chrome DevTools > Application > IndexedDB > workbox-background-sync > requests를 살펴보면 요청이 큐에 추가되었는지 확인할 수 있습니다.
  4. 이제 네트워크 또는 웹 서버를 켭니다.
  5. Chrome DevTools > Application > Service Workers로 이동하고 workbox-background-sync:<your queue name>의 태그 이름을 입력한 다음(<your queue name>는 설정한 큐의 이름이어야 함) 'Sync' 버튼을 클릭하여 초기 sync 이벤트를 강제 적용합니다.

    Chrome DevTools의 동기화 버튼 예

  6. 실패한 요청에 대해 네트워크 요청이 처리되는 것을 볼 수 있습니다. 요청이 성공적으로 재생되었으므로 이제 IndexedDB 데이터가 비어 있습니다.

유형

BackgroundSyncPlugin

fetchDidFail 수명 주기 콜백을 구현하는 클래스입니다. 이렇게 하면 실패한 요청을 백그라운드 동기화 큐에 쉽게 추가할 수 있습니다.

속성

Queue

실패한 요청을 IndexedDB에 저장하고 나중에 다시 시도하는 클래스 저장 및 재생 프로세스의 모든 부분은 콜백을 통해 관찰할 수 있습니다.

속성

  • 생성자

    void

    지정된 옵션으로 Queue의 인스턴스를 만듭니다.

    constructor 함수는 다음과 같습니다.

    (name: string,options?: QueueOptions)=> {...}

    • 이름

      string

      이 큐의 고유한 이름입니다. 이 이름은 동기화 이벤트를 등록하고 이 인스턴스와 관련된 IndexedDB에 요청을 저장하는 데 사용되므로 고유해야 합니다. 중복된 이름이 감지되면 오류가 발생합니다.

    • 옵션

      QueueOptions 선택사항

  • 이름

    string

  • getAll

    void

    maxRetentionTime별로 만료되지 않은 모든 항목을 반환합니다. 만료된 항목은 큐에서 삭제됩니다.

    getAll 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      Promise<QueueEntry[]>

  • popRequest

    void

    큐에서 마지막 요청을 삭제하고 타임스탬프 및 모든 메타데이터와 함께 반환합니다. 반환된 객체의 형식은 {request, timestamp, metadata}입니다.

    popRequest 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      Promise<QueueEntry>

  • pushRequest

    void

    전달된 요청을 큐 끝에 있는 IndexedDB (타임스탬프 및 메타데이터 포함)에 저장합니다.

    pushRequest 함수는 다음과 같습니다.

    (entry: QueueEntry)=> {...}

    • entry

      QueueEntry

    • returns

      Promise<void>

  • registerSync

    void

    이 인스턴스에 고유한 태그와 함께 동기화 이벤트를 등록합니다.

    registerSync 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      Promise<void>

  • replayRequests

    void

    대기열에서 각 요청을 반복하고 다시 가져오기 시도합니다. 요청이 다시 가져오기에 실패하면 요청은 큐의 동일한 위치에 다시 배치됩니다 (다음 동기화 이벤트의 재시도를 등록함).

    replayRequests 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      Promise<void>

  • shiftRequest

    void

    큐에서 첫 번째 요청을 삭제하고 타임스탬프 및 모든 메타데이터와 함께 반환합니다. 반환된 객체의 형식은 {request, timestamp, metadata}입니다.

    shiftRequest 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      Promise<QueueEntry>

  • 크기

    void

    큐에 있는 항목 수를 반환합니다. 만료된 항목 (maxRetentionTime당)도 이 수에 포함됩니다.

    size 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      프로미스<number>

  • unshiftRequest

    void

    전달된 요청을 큐 시작 시 IndexedDB (타임스탬프 및 메타데이터 포함)에 저장합니다.

    unshiftRequest 함수는 다음과 같습니다.

    (entry: QueueEntry)=> {...}

    • entry

      QueueEntry

    • returns

      Promise<void>

QueueOptions

속성

  • forceSyncFallback

    부울 선택사항

  • maxRetentionTime

    number 선택사항

  • onSync

    OnSyncCallback 선택사항

QueueStore

IndexedDB에서 큐의 요청 저장을 관리하는 클래스로, 쉽게 액세스할 수 있도록 큐 이름으로 색인을 생성합니다.

대부분의 개발자는 이 클래스에 직접 액세스할 필요가 없습니다. 이 클래스는 고급 사용 사례에 노출됩니다.

속성

  • 생성자

    void

    추가된 항목을 큐 이름으로 식별할 수 있도록 이 인스턴스를 큐 인스턴스와 연결합니다.

    constructor 함수는 다음과 같습니다.

    (queueName: string)=> {...}

    • queueName

      string

  • deleteEntry

    void

    지정된 ID의 항목을 삭제합니다.

    경고: 이 메서드는 삭제된 항목이 이 큐에 속하는지 확인하지 않습니다 (즉, queueName와 일치). 하지만 이 클래스는 이 클래스가 공개적으로 노출되지 않으므로 허용될 수 있습니다. 추가로 확인하면 이 메서드가 필요 이상으로 느려질 수 있습니다.

    deleteEntry 함수는 다음과 같습니다.

    (id: number)=> {...}

    • id

      숫자

    • returns

      Promise<void>

  • getAll

    void

    스토어에서 queueName와 일치하는 모든 항목을 반환합니다.

    getAll 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      Promise<QueueStoreEntry[]>

  • popEntry

    void

    큐에서 queueName와 일치하는 마지막 항목을 삭제하고 반환합니다.

    popEntry 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      Promise<QueueStoreEntry>

  • pushEntry

    void

    대기열의 마지막에 항목을 추가합니다.

    pushEntry 함수는 다음과 같습니다.

    (entry: UnidentifiedQueueStoreEntry)=> {...}

    • entry

      UnidentifiedQueueStoreEntry

    • returns

      Promise<void>

  • shiftEntry

    void

    큐에서 queueName와 일치하는 첫 번째 항목을 삭제하고 반환합니다.

    shiftEntry 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      Promise<QueueStoreEntry>

  • 크기

    void

    스토어에서 queueName와 일치하는 항목 수를 반환합니다.

    size 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      프로미스<number>

  • unshiftEntry

    void

    대기열의 항목 앞에 항목을 추가합니다.

    unshiftEntry 함수는 다음과 같습니다.

    (entry: UnidentifiedQueueStoreEntry)=> {...}

    • entry

      UnidentifiedQueueStoreEntry

    • returns

      Promise<void>

StorableRequest

요청을 더 쉽게 직렬화 및 역직렬화하여 IndexedDB에 저장할 수 있도록 하는 클래스입니다.

대부분의 개발자는 이 클래스에 직접 액세스할 필요가 없습니다. 이 클래스는 고급 사용 사례에 노출됩니다.

속성

  • 생성자

    void

    Request를 구성하는 데 사용할 수 있지만 IndexedDB에도 저장할 수 있는 요청 데이터의 객체를 허용합니다.

    constructor 함수는 다음과 같습니다.

    (requestData: RequestData)=> {...}

    • requestData

      RequestData

      url 및 [requestInit]https://fetch.spec.whatwg.org/#requestinit의 관련 속성을 포함하는 요청 데이터의 객체입니다.

  • clone

    void

    인스턴스의 딥 클론을 만들고 반환합니다.

    clone 함수는 다음과 같습니다.

    ()=> {...}

  • toObject

    void

    인스턴스 _requestData 객체의 딥 클론을 반환합니다.

    toObject 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      RequestData

  • toRequest

    void

    이 인스턴스를 요청으로 변환합니다.

    toRequest 함수는 다음과 같습니다.

    ()=> {...}

    • returns

      요청

  • fromRequest

    void

    요청 객체를 클론하거나 JSON으로 문자열화할 수 있는 일반 객체로 변환합니다.

    fromRequest 함수는 다음과 같습니다.

    (request: Request)=> {...}

    • 요청

      요청