Cache API: 빠른 가이드

Cache API를 사용하여 애플리케이션 데이터를 오프라인에서 사용할 수 있도록 하는 방법을 알아봅니다.

Cache API는 네트워크 요청과 해당 응답을 저장하고 검색하는 시스템입니다. 이러한 요청과 응답은 애플리케이션을 실행하는 과정에서 생성된 일반 요청과 응답일 수도 있고 나중에 사용할 데이터를 저장하기 위한 목적으로만 생성될 수도 있습니다.

Cache API는 서비스 워커가 네트워크 속도 또는 가용성에 관계없이 빠른 응답을 제공할 수 있도록 네트워크 요청을 캐시할 수 있도록 만들기 위해 만들어졌습니다. 그러나 이 API는 일반적인 저장소 메커니즘으로도 사용할 수 있습니다.

Cache API는 모든 최신 브라우저에서 사용할 수 있습니다. 전역 caches 속성을 통해 노출되므로 간단한 기능 감지로 API의 존재를 테스트할 수 있습니다.

const cacheAvailable = 'caches' in self;

브라우저 지원

  • Chrome: 40.
  • Edge: 16.
  • Firefox: 41
  • Safari: 11.1.

소스

Cache API는 창, iframe, 워커 또는 서비스 워커에서 액세스할 수 있습니다.

저장할 수 있는 항목

캐시에는 각각 HTTP 요청과 응답을 나타내는 RequestResponse 객체 쌍만 저장됩니다. 그러나 요청과 응답에는 HTTP를 통해 전송할 수 있는 모든 종류의 데이터가 포함될 수 있습니다.

저장할 수 있는 양은 얼마인가요?

간단히 말해 매우 많습니다. 최소 수백 메가바이트에서 수백 기가바이트 이상일 수 있습니다. 브라우저 구현은 다양하지만 사용 가능한 저장용량은 일반적으로 기기에서 사용 가능한 저장용량을 기준으로 합니다.

캐시 만들기 및 열기

캐시를 열려면 caches.open(name) 메서드를 사용하여 캐시 이름을 단일 매개변수로 전달합니다. 이름이 지정된 캐시가 없으면 캐시가 생성됩니다. 이 메서드는 Cache 객체로 확인되는 Promise를 반환합니다.

const cache = await caches.open('my-cache');
// do something with cache...

캐시에 추가

캐시에 항목을 추가하는 방법에는 add, addAll, put의 세 가지가 있습니다. 세 메서드 모두 Promise를 반환합니다.

cache.add

먼저 cache.add()가 있습니다. Request 또는 URL (string) 매개변수 하나를 사용합니다. 네트워크에 요청을 보내고 응답을 캐시에 저장합니다. 가져오기에 실패하거나 응답의 상태 코드가 200 범위에 있지 않으면 아무것도 저장되지 않고 Promise가 거부됩니다. CORS 모드가 아닌 교차 출처 요청은 0status를 반환하므로 저장할 수 없습니다. 이러한 요청은 put로만 저장할 수 있습니다.

// Retreive data.json from the server and store the response.
cache.add(new Request('/data.json'));

// Retreive data.json from the server and store the response.
cache.add('/data.json');

cache.addAll

다음은 cache.addAll()입니다. add()와 비슷하게 작동하지만 Request 객체 또는 URL (string) 배열을 사용합니다. 이는 개별 요청마다 cache.add를 호출하는 것과 유사하지만, 단일 요청이 캐시되지 않으면 Promise가 거부된다는 점을 제외하고는 동일합니다.

const urls = ['/weather/today.json', '/weather/tomorrow.json'];
cache.addAll(urls);

이 경우 새 항목이 일치하는 기존 항목을 덮어씁니다. 여기서는 검색 섹션에 설명된 것과 동일한 일치 규칙을 사용합니다.

cache.put

마지막으로 cache.put()가 있습니다. 이 함수를 사용하면 네트워크의 응답을 저장하거나 자체 Response를 만들고 저장할 수 있습니다. 두 개의 매개변수를 사용합니다. 첫 번째는 Request 객체 또는 URL (string)일 수 있습니다. 두 번째는 네트워크에서 가져오거나 코드에서 생성한 Response여야 합니다.

// Retrieve data.json from the server and store the response.
cache.put('/data.json');

// Create a new entry for test.json and store the newly created response.
cache.put('/test.json', new Response('{"foo": "bar"}'));

// Retrieve data.json from the 3rd party site and store the response.
cache.put('https://example.com/data.json');

put() 메서드는 add() 또는 addAll()보다 관대한 메서드이며, CORS가 아닌 응답이나 응답의 상태 코드가 200 범위에 있지 않은 다른 응답을 저장할 수 있습니다. 동일한 요청에 대한 이전 응답을 덮어씁니다.

요청 객체 만들기

저장되는 항목의 URL을 사용하여 Request 객체를 만듭니다.

const request = new Request('/my-data-store/item-id');

Response 객체 작업

Response 객체 생성자는 Blob, ArrayBuffer, FormData 객체, 문자열을 비롯한 다양한 유형의 데이터를 허용합니다.

const imageBlob = new Blob([data], {type: 'image/jpeg'});
const imageResponse = new Response(imageBlob);
const stringResponse = new Response('Hello world');

적절한 헤더를 설정하여 Response의 MIME 유형을 설정할 수 있습니다.

  const options = {
    headers: {
      'Content-Type': 'application/json'
    }
  }
  const jsonResponse = new Response('{}', options);

Response를 검색했고 본문에 액세스하려면 사용할 수 있는 여러 도우미 메서드가 있습니다. 각 메서드는 다른 유형의 값으로 확인되는 Promise를 반환합니다.

메서드 설명
arrayBuffer 바이트로 직렬화된 본문이 포함된 ArrayBuffer를 반환합니다.
blob Blob를 반환합니다. ResponseBlob로 생성된 경우 이 새 Blob의 유형은 동일합니다. 그렇지 않으면 ResponseContent-Type가 사용됩니다.
text 본문의 바이트를 UTF-8 인코딩된 문자열로 해석합니다.
json 본문의 바이트를 UTF-8 인코딩된 문자열로 해석한 후 JSON으로 파싱하려고 시도합니다. 결과 객체를 반환하거나 문자열을 JSON으로 파싱할 수 없는 경우 TypeError을 발생시킵니다.
formData 본문의 바이트를 multipart/form-data 또는 application/x-www-form-urlencoded로 인코딩된 HTML 양식으로 해석합니다. FormData 객체를 반환하거나 데이터를 파싱할 수 없는 경우 TypeError을 발생시킵니다.
body 본문 데이터의 ReadableStream을 반환합니다.

예:

const response = new Response('Hello world');
const buffer = await response.arrayBuffer();
console.log(new Uint8Array(buffer));
// Uint8Array(11) [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

캐시에서 검색

캐시에서 항목을 찾으려면 match 메서드를 사용하면 됩니다.

const response = await cache.match(request);
console.log(request, response);

request가 문자열인 경우 브라우저는 new Request(request)를 호출하여 requestRequest로 변환합니다. 이 함수는 일치하는 항목이 발견되면 Response로 확인되는 Promise을 반환하고, 그렇지 않으면 undefined을 반환합니다.

두 개의 Requests가 일치하는지 확인하기 위해 브라우저는 URL 외에도 다른 항목을 사용합니다. 두 요청의 쿼리 문자열, Vary 헤더 또는 HTTP 메서드 (GET, POST, PUT 등)가 서로 다른 경우 서로 다른 요청으로 간주됩니다.

옵션 객체를 두 번째 매개변수로 전달하여 이러한 항목의 일부 또는 전부를 무시할 수 있습니다.

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const response = await cache.match(request, options);
// do something with the response

캐시된 요청이 두 개 이상 일치하는 경우 먼저 생성된 요청이 반환됩니다. 일치하는 응답을 모두 검색하려면 cache.matchAll()를 사용하면 됩니다.

const options = {
  ignoreSearch: true,
  ignoreMethod: true,
  ignoreVary: true
};

const responses = await cache.matchAll(request, options);
console.log(`There are ${responses.length} matching responses.`);

바로가기로 각 캐시의 cache.match()를 호출하는 대신 caches.match()를 사용하여 모든 캐시를 한 번에 검색할 수 있습니다.

검색 중

Cache API는 Response 객체와 일치하는 항목을 제외하고 요청이나 응답을 검색하는 방법을 제공하지 않습니다. 하지만 필터링을 사용하거나 색인을 만들어 자체 검색을 구현할 수 있습니다.

필터링

자체 검색을 구현하는 한 가지 방법은 모든 항목을 반복하고 원하는 항목으로 필터링하는 것입니다. .png로 끝나는 URL이 있는 모든 항목을 찾으려 한다고 가정해 보겠습니다.

async function findImages() {
  // Get a list of all of the caches for this origin
  const cacheNames = await caches.keys();
  const result = [];

  for (const name of cacheNames) {
    // Open the cache
    const cache = await caches.open(name);

    // Get a list of entries. Each item is a Request object
    for (const request of await cache.keys()) {
      // If the request URL matches, add the response to the result
      if (request.url.endsWith('.png')) {
        result.push(await cache.match(request));
      }
    }
  }

  return result;
}

이렇게 하면 RequestResponse 객체의 속성을 사용하여 항목을 필터링할 수 있습니다. 대규모 데이터 세트를 검색하는 경우 속도가 느려집니다.

색인 만들기

자체 검색을 구현하는 다른 방법은 검색할 수 있는 항목의 별도 색인을 유지하고 색인을 IndexedDB에 저장하는 것입니다. 이는 IndexedDB가 설계된 작업 유형이므로 항목 수가 많을 때 훨씬 더 우수한 성능을 발휘합니다.

검색 가능한 속성과 함께 Request의 URL을 저장하면 검색 후 올바른 캐시 항목을 쉽게 검색할 수 있습니다.

항목 삭제

캐시에서 항목을 삭제하려면 다음 단계를 따르세요.

cache.delete(request);

여기서 요청은 Request 또는 URL 문자열일 수 있습니다. 이 메서드는 cache.match와 동일한 옵션 객체도 사용합니다. 따라서 동일한 URL에 대해 여러 개의 Request/Response 쌍을 삭제할 수 있습니다.

cache.delete('/example/file.txt', {ignoreVary: true, ignoreSearch: true});

캐시 삭제

캐시를 삭제하려면 caches.delete(name)를 호출합니다. 이 함수는 캐시가 존재하고 삭제된 경우 true로 확인되는 Promise를 반환하고 그렇지 않은 경우에는 false를 반환합니다.

감사합니다.

이 도움말의 원본 버전을 작성하고 WebFundamentals에 처음 게시한 맷 스케일스님께 감사드립니다.