지피지기 백전백퇴

웹 애플리케이션에서의 데이터 미리 읽기


문제

출퇴근 길에 태블릿으로 웹툰/소설이라도 좀 읽을라 치면 가끔 느린 인터넷이 속터질 때가 있다. 몇페이지 정도 미리 로딩해두도록 하면 상대적으로 체감 딜레이가 줄어들지 않을까?

어떻게?

브라우저가 똑똑하게 뭔가를 미리 다운로드받는건 언감생심이다. 각 리소스 형태에 맞춰서 캐시가 되도록 적당히 코드를 짜야 한다.

쉬운맛: fetch

이건 뭐 그냥 나중에 fetch할 것들을 미리 판단해두고 그냥 미리 호출한 다음에 결과값 Promise를 나중에 가져다 쓰면 된다.

const cache = new Map<string, Promise<Response>>();

// At some point prior to the actual load...
cache.set("https://example.com/resource", fetch("https://example.com/resource"));

// When the resource is needed...
const resource = await cache.get("https://example.com/resource");

// ... or being clever...
const resource = await (() => {
  const resource = cache.get("https://example.com/resource");
  if (!resource) {
    cache.set("https://example.com/resource", fetch("https://example.com/resource"));
  }
  return resource;
})();

어려운맛: 이미지

<img> 태그를 써서 불러들이는 이미지의 경우 HTML 태그이기 때문에 브라우저가 메모리 관리를 담당한다. 이런 것들을 자바스크립트에서 캐시하려면 이미지 전체를 URLEncode해서, data:image/png;base64,... 형식의 긴 문자열로 캐시할 수도 있겠지만 브라우저가 자체적으로 구현한 캐시에 비하면 크게 느려질 것 같은 느낌이다. (하지만 실제로 벤치마크를 해보진 않았습니다;;)

하지만 언제나 방법은 있다. 브라우저에게 이미지를 미리 불러오도록 하고, 브라우저의 자체 캐시 관리 기능을 활용(아니, 그냥 기도메타라고 봐도 무방)하는 것이다.

const img = new Image();
img.src = "https://example.com/image.png";

이렇게 하면 브라우저가 이미지를 미리 불러오고, 캐시에 저장해둔다. 실제 <img> 태그를 사용하는 시점에 브라우저 캐시가 남아있다면 추가적인 네트워크 요청 없이 기존에 캐시된 이미지를 다시 사용하게 된다.

다만, 캐시 관리를 브라우저에게 일임하는 것이기 때문에 이미지가 몇개나 캐시되는지는 브라우저 맘대로라는 문제가 있다. 최악의 경우 이미지 10000개를 미리 로드했는데 브라우저가 이미 가장 예전 캐시를 날려버리면 바로 다음에 읽어야 할 이미지가 날아가서 캐시가 없는 것보다 못한 상황이 발생할 수 있다는 셈이다.

결론

그냥 인터넷 빠른거 써라…