Workbox
는 Service Worker를 쉽게 쓸 수 있게 약간의 추상화를 더해주는 라이브러리다.
구글 크롬 팀에서 만들었으며, 여러 개의 패키지로 분류되어 있어서 필요에 따라 일부만 갖다 쓰기에도 좋다고 한다.
Workbox의 큰 틀과 핵심적인 기능들에 대해 정리해보자.
fetch
)workbox-routing
fetch
)workbox-strategies
install
)workbox-precaching
workbox-build
fetch
)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-routing
workbox-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-sync
workbox-broadcast-update
workbox-cacheable-response
workbox-expiration
workbox-range-requests
import { 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-precaching
import { 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-build
workbox-webpack-plugin
workbox-cli
workbox-build
workbox-build
는 node 기반 빌드 프로세스에서 Service Worker 전체를 생성
하거나 Precaching할 리스트를 생성
하게 해주는 패키지다.
2가지 모드가 있다 :
generateSW
injectManifest
자세한 내용은 문서 참고