백그라운드 가져오기 API를 사용하여 AI 모델 다운로드

게시일: 2025년 2월 20일

대규모 AI 모델을 안정적으로 다운로드하는 것은 어려운 작업입니다. 사용자가 인터넷 연결이 끊기거나 웹사이트 또는 웹 애플리케이션을 닫으면 부분적으로 다운로드된 모델 파일이 손실되어 페이지로 돌아올 때 다시 시작해야 합니다. 백그라운드 가져오기 API를 점진적 개선으로 사용하면 사용자 환경을 크게 개선할 수 있습니다.

Browser Support

  • Chrome: 74.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Source

서비스 워커 등록

백그라운드 가져오기 API를 사용하려면 앱이 서비스 워커를 등록해야 합니다.

if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    const registration = await navigator.serviceWorker.register('sw.js');
    console.log('Service worker registered for scope', registration.scope);
  });
}

백그라운드 가져오기 트리거

브라우저가 가져오면 사용자에게 진행 상황을 표시하고 다운로드를 취소하는 방법을 제공합니다. 다운로드가 완료되면 브라우저가 서비스 워커를 시작하고 애플리케이션이 응답을 사용하여 작업을 실행할 수 있습니다.

백그라운드 가져오기 API는 오프라인 상태에서 시작할 가져오기를 준비할 수도 있습니다. 사용자가 다시 연결하면 다운로드가 시작됩니다. 사용자가 오프라인 상태가 되면 사용자가 다시 온라인 상태가 될 때까지 프로세스가 일시중지됩니다.

다음 예시에서 사용자는 버튼을 클릭하여 Gemma 2B를 다운로드합니다. 가져오기 전에 모델이 이전에 다운로드되고 캐시되었는지 확인하여 불필요한 리소스를 사용하지 않습니다. 캐시되지 않은 경우 백그라운드 가져오기를 시작합니다.

const FETCH_ID = 'gemma-2b';
const MODEL_URL =
  'https://storage.googleapis.com/jmstore/kaggleweb/grader/g-2b-it-gpu-int4.bin';

downloadButton.addEventListener('click', async (event) => {
  // If the model is already downloaded, return it from the cache.
  const modelAlreadyDownloaded = await caches.match(MODEL_URL);
  if (modelAlreadyDownloaded) {
    const modelBlob = await modelAlreadyDownloaded.blob();
    // Do something with the model.
    console.log(modelBlob);
    return;
  }

  // The model still needs to be downloaded.
  // Feature detection and fallback to classic `fetch()`.
  if (!('BackgroundFetchManager' in self)) {
    try {
      const response = await fetch(MODEL_URL);
      if (!response.ok || response.status !== 200) {
        throw new Error(`Download failed ${MODEL_URL}`);
      }
      const modelBlob = await response.blob();
      // Do something with the model.
      console.log(modelBlob);
      return;
    } catch (err) {
      console.error(err);
    }
  }

  // The service worker registration.
  const registration = await navigator.serviceWorker.ready;

  // Check if there's already a background fetch running for the `FETCH_ID`.
  let bgFetch = await registration.backgroundFetch.get(FETCH_ID);

  // If not, start a background fetch.
  if (!bgFetch) {
    bgFetch = await registration.backgroundFetch.fetch(FETCH_ID, MODEL_URL, {
      title: 'Gemma 2B model',
      icons: [
        {
          src: 'icon.png',
          size: '128x128',
          type: 'image/png',
        },
      ],
      downloadTotal: await getResourceSize(MODEL_URL),
    });
  }
});

getResourceSize() 함수는 다운로드의 바이트 크기를 반환합니다. HEAD 요청을 만들어 이를 구현할 수 있습니다.

const getResourceSize = async (url) => {
  try {
    const response = await fetch(url, { method: 'HEAD' });
    if (response.ok) {
      return response.headers.get('Content-Length');
    }
    console.error(`HTTP error: ${response.status}`);
    return 0;
  } catch (error) {
    console.error('Error fetching content size:', error);
    return 0;
  }
};

보고서 다운로드 진행률

백그라운드 가져오기가 시작되면 브라우저에서 BackgroundFetchRegistration를 반환합니다. 이를 사용하여 progress 이벤트를 통해 사용자에게 다운로드 진행 상황을 알릴 수 있습니다.

bgFetch.addEventListener('progress', (e) => {
  // There's no download progress yet.
  if (!bgFetch.downloadTotal) {
    return;
  }
  // Something went wrong.
  if (bgFetch.failureReason) {
    console.error(bgFetch.failureReason);
  }
  if (bgFetch.result === 'success') {
    return;
  }
  // Update the user about progress.
  console.log(`${bgFetch.downloaded} / ${bgFetch.downloadTotal}`);
});

사용자와 클라이언트에게 가져오기 완료 알림

백그라운드 가져오기가 성공하면 앱의 서비스 워커가 backgroundfetchsuccess 이벤트를 수신합니다.

다음 코드는 서비스 워커에 포함됩니다. 거의 끝에 있는 updateUI() 호출을 사용하면 브라우저의 인터페이스를 업데이트하여 사용자에게 백그라운드 가져오기가 성공했음을 알릴 수 있습니다. 마지막으로 postMessage()을 사용하여 다운로드가 완료되었음을 클라이언트에 알립니다.

self.addEventListener('backgroundfetchsuccess', (event) => {
  // Get the background fetch registration.
  const bgFetch = event.registration;

  event.waitUntil(
    (async () => {
      // Open a cache named 'downloads'.
      const cache = await caches.open('downloads');
      // Go over all records in the background fetch registration.
      // (In the running example, there's just one record, but this way
      // the code is future-proof.)
      const records = await bgFetch.matchAll();
      // Wait for the response(s) to be ready, then cache it/them.
      const promises = records.map(async (record) => {
        const response = await record.responseReady;
        await cache.put(record.request, response);
      });
      await Promise.all(promises);

      // Update the browser UI.
      event.updateUI({ title: 'Model downloaded' });

      // Inform the clients that the model was downloaded.
      self.clients.matchAll().then((clientList) => {
        for (const client of clientList) {
          client.postMessage({
            message: 'download-complete',
            id: bgFetch.id,
          });
        }
      });
    })(),
  );
});

서비스 워커로부터 메시지 수신

클라이언트에서 다운로드 완료에 관한 전송된 성공 메시지를 수신하려면 message 이벤트를 수신 대기합니다. 서비스 워커로부터 메시지를 받으면 AI 모델을 사용하고 Cache API를 사용하여 저장할 수 있습니다.

navigator.serviceWorker.addEventListener('message', async (event) => {
  const cache = await caches.open('downloads');
  const keys = await cache.keys();
  for (const key of keys) {
    const modelBlob = await cache
      .match(key)
      .then((response) => response.blob());
    // Do something with the model.
    console.log(modelBlob);
  }
});

백그라운드 가져오기 취소

사용자가 진행 중인 다운로드를 취소할 수 있도록 하려면 BackgroundFetchRegistrationabort() 메서드를 사용하세요.

const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.get(FETCH_ID);
if (!bgFetch) {
  return;
}
await bgFetch.abort();

모델 캐시

다운로드한 모델을 캐시하여 사용자가 모델을 한 번만 다운로드하도록 합니다. 백그라운드 가져오기 API는 다운로드 환경을 개선하지만 항상 클라이언트 측 AI에서 가능한 가장 작은 모델을 사용하는 것이 좋습니다.

이러한 API를 함께 사용하면 사용자에게 더 나은 클라이언트 측 AI 환경을 제공할 수 있습니다.

데모

이 접근 방식의 전체 구현은 데모소스 코드에서 확인할 수 있습니다.

Chrome DevTools 애플리케이션 패널이 Background Fetch 다운로드에 열려 있습니다.
Chrome DevTools를 사용하면 진행 중인 백그라운드 가져오기와 관련된 이벤트를 미리 볼 수 있습니다. 데모에서는 총 1.26GB 중 17.54MB가 완료된 다운로드를 보여줍니다. 브라우저의 다운로드 표시기에도 진행 중인 다운로드가 표시됩니다.

감사의 말씀

이 가이드는 프랑수아 보포르, 안드레 반다라, 세바스티안 벤츠, 모드 날파스, 알렉산드라 클레퍼가 검토했습니다.