Cache API: 빠른 가이드

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

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

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

어느 지역에서 제공되나요?

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

const cacheAvailable = 'caches' in self;

브라우저 지원

  • 40
  • 16
  • 41
  • 11.1

소스

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

저장할 수 있는 항목

캐시는 각각 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);

이러한 경우 새 항목이 일치하는 기존 항목을 덮어씁니다. 이때 retrieving 섹션에 설명된 것과 동일한 일치 규칙이 사용됩니다.

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 객체 생성자는 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)를 호출하여 Request로 변환합니다. 이 함수는 일치하는 항목이 있으면 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로 확인되고 그렇지 않은 경우 false로 확인되는 Promise를 반환합니다.

감사합니다.

WebFundamentals에 처음 게재된 이 문서의 원본 버전을 작성해 주신 Mat Scales님께 감사드립니다.