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는 창, 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)의 배열을 사용합니다. 이는 단일 요청이 캐시되지 않으면 Promise가 거부된다는 점을 제외하고 개별 요청의 cache.add 호출과 비슷하게 작동합니다.

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

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

cache.put

마지막으로 네트워크의 응답을 저장하거나 자체 Response를 만들고 저장할 수 있는 cache.put()가 있습니다. 2개의 매개변수가 필요합니다. 첫 번째는 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 객체와 일치하는 항목을 제외하고 요청이나 응답을 검색하는 방법을 제공하지 않습니다. 하지만 필터링을 사용하거나 색인을 만들어 자체 검색을 구현할 수 있습니다.

필터링

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

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 씨께 감사드립니다.