Workbox는 Service Worker를 쉽게 쓸 수 있게 약간의 추상화를 더해주는 라이브러리다.
구글 크롬 팀에서 만들었으며, 여러 개의 패키지로 분류되어 있어서 필요에 따라 일부만 갖다 쓰기에도 좋다고 한다.
Workbox의 큰 틀과 핵심적인 기능들에 대해 정리해보자.
fetch)workbox-routingfetch)workbox-strategiesinstall)workbox-precachingworkbox-buildfetch)fetch는 Service Worker에서 핵심적인 이벤트다.
네트워크 Request를 인터셉트해서 넘겨주기 때문에, Response를 캐시에서 가져올지 네트워크 요청을 보낼지 선택해서 처리할 수 있게 해준다.
// Service Worker에서
self.addEventListener("fetch", function (event) {
const requestUrl = new URL(event.request.url)
if (requestUrl.pathname.includes("/api/data")) {
event.respondWith(customResponse())
} else if (requestUrl.pathname.includes("/images")) {
event.respondWith(customImageResponse())
} else if (requestUrl.pathname.includes("/css")) {
event.respondWith(customCSSResponse())
} else {
event.respondWith(fetch(event.request))
}
})
다양한 Request를 받는 경우에는, 조건에 따라 처리하는 방식이 다를 것이고 그런 구분이 중요하다.
그럴 때 중요한 요소는, 어떤 Request를 받았냐와 그걸 어떻게 처리하느냐라고 할 수 있다.
workbox-routingworkbox-routing 패키지는 이럴 때 사용되는 "route"를 제공한다.
route는
어떤 Request를 받을 것이냐(matching)와그걸 어떻게 처리할 것이냐(handling)로 구성된다.
import { registerRoute } from "workbox-routing"
registerRoute("https://example.com/api/data", () => {
return new Response("Hello from the service worker!")
})
가장 대표적으로 쓰이는 메서드는 registerRoute()인데, matching 부분과 handler 부분을 순서대로 받는다.
matching에는 URL을 나타내는 string, 정규표현식, 함수 등이 올 수 있고,
handler 부분에는 함수나 객체가 올 수 있다고 한다.
자세한 내용은 문서 참고
어쨌든 라우팅은 fetch를 다루는 데에 있어서 핵심적이다.
fetch)그렇다면 handler 부분에 대해 조금 더 생각해보자.
fetch이벤트는 Response 객체를 돌려줘야하므로, 대표적으로캐시에서 가져오거나,네트워크 요청을 보내서처리할 수 있다.
workbox-strategies이때 캐시와 네트워크 중 어디에서 먼저 가져오고, 실패할 경우에 어떻게 할 것인지에 따라 다양한 로직들이 생성될 수 있다.
그 로직들에 이름을 붙여주고 패턴화한 걸 캐싱 전략이라고 한다.
대표적인 캐싱 전략들과 그에 대응되는 constructor들은 다음과 같다 :
CacheFirst)CacheOnly)NetworkFirst)NetworkOnly)StaleWhileRevalidate)NetworkFirst를 사용하는 예시를 보자 :
import { registerRoute } from "workbox-routing"
import { NetworkFirst } from "workbox-strategies"
registerRoute(
({ url }) => url.pathname.startsWith("/social-timeline/"),
new NetworkFirst()
)
자세한 설명 및 예시는 문서 참고
캐싱 전략에서는 플러그인을 사용할 수 있는데, 플러그인을 제공하는 패키지들은 다음과 같다 :
workbox-background-syncworkbox-broadcast-updateworkbox-cacheable-responseworkbox-expirationworkbox-range-requestsimport { registerRoute } from "workbox-routing"
import { CacheFirst } from "workbox-strategies"
import { ExpirationPlugin } from "workbox-expiration"
registerRoute(
({ request }) => request.destination === "image",
new CacheFirst({
cacheName: "image-cache",
plugins: [
new ExpirationPlugin({
// Only cache requests for a week
maxAgeSeconds: 7 * 24 * 60 * 60,
// Only cache 10 requests.
maxEntries: 10,
}),
],
})
)
이런 식으로 plugins에 넘겨주는 식으로 사용하는듯하다.
install)Service Worker의 또 다른 핵심 이벤트로는 install이 있다.
install은 Service Worker가 설치(다운로드, 업데이트) 되었을 때만 트리거 되며, 그래서 CacheStorage나 IndexedDB의 초기 세팅 등을 하는 용도로 사용된다.
// Service Worker에서
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open("v1").then((cache) => {
cache.addAll(["/", "/index.html", "/style.css", "/script.js"])
})
)
})
이런 초기 캐싱도 고정적이기 때문에 추상화할 수 있고, workbox-precaching 패키지가 그런 추상화를 제공한다.
workbox-precachingimport { precacheAndRoute } from "workbox-precaching"
precacheAndRoute(
[
{ url: "/index.html", revision: "383676" },
{ url: "/styles/app.0c9a31.css", revision: null },
{ url: "/scripts/app.0d5770.js", revision: null },
],
{
// Ignore all URL parameters.
ignoreURLParametersMatching: [/.*/],
}
)
대표적으로 쓰이는 메서드는 precacheAndRoute()이며, 이 메서드에 넘겨주는 배열을 precache manifest라고 부르기도 한다고 한다.
이 route는 Cache-First 전략을 사용한다.
revision은 versioning information인데, url에 포함되어있으면 null을 주면 된다.
workbox-precaching은 이 정보를 통해 효율적으로 캐시를 업데이트한다고 한다.
이런 revision 정보는 직접 작성하면 안되고, 다음과 같은 패키지들을 통해 생성해야 한다 :
workbox-buildworkbox-webpack-pluginworkbox-cliworkbox-buildworkbox-build는 node 기반 빌드 프로세스에서 Service Worker 전체를 생성하거나 Precaching할 리스트를 생성하게 해주는 패키지다.
2가지 모드가 있다 :
generateSWinjectManifest자세한 내용은 문서 참고