---
id: web-service-worker-patterns
title: Service Worker 캐싱 전략
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [web, service-worker, pwa, caching, offline, vibe-coding]
tech_stack: { language: "TypeScript / Workbox", applicable_to: ["Web", "PWA"] }
applied_in: []
aliases: [cache-first, network-first, stale-while-revalidate, PWA]
---

# Service Worker 캐싱 전략

> Service Worker = 네트워크 인터셉터. 5가지 표준 전략 — Cache-First / Network-First / Stale-While-Revalidate / Network-Only / Cache-Only — 자원 종류에 따라 골라 쓴다. 한 SW 안에 여러 전략 혼용 OK.

## 📖 핵심 개념
- `install` 이벤트: precache (빌드 시 알려진 자원).
- `fetch` 이벤트: 매 요청 인터셉트.
- `activate` 이벤트: 옛 캐시 청소.
- 자원 별 다른 정책 적용.

## 💻 코드 패턴 (Workbox 기반)

```ts
// sw.ts
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';

// 빌드 시 자동 주입
precacheAndRoute(self.__WB_MANIFEST);

// 1. 정적 자산 — Cache-First (해시 파일명이라 immutable)
registerRoute(
  ({ request }) => ['style', 'script', 'font'].includes(request.destination),
  new CacheFirst({
    cacheName: 'static-v1',
    plugins: [new ExpirationPlugin({ maxAgeSeconds: 30 * 24 * 60 * 60 })],
  })
);

// 2. 이미지 — Stale-While-Revalidate
registerRoute(
  ({ request }) => request.destination === 'image',
  new StaleWhileRevalidate({
    cacheName: 'images-v1',
    plugins: [new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 7 * 24 * 60 * 60 })],
  })
);

// 3. API — Network-First with timeout
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({
    cacheName: 'api-v1',
    networkTimeoutSeconds: 3,
  })
);
```

### 5가지 전략 요약
| 전략 | 흐름 | 사용처 |
|---|---|---|
| Cache-First | 캐시 → 없으면 네트워크 | hashed 정적 자산 |
| Network-First | 네트워크 → 실패시 캐시 | 자주 변하는 데이터 |
| Stale-While-Revalidate | 캐시 즉시 + 백그라운드 재검증 | 이미지, 자주 보지만 약간 stale OK |
| Network-Only | 캐시 안 씀 | 결제, 민감 데이터 |
| Cache-Only | 네트워크 안 씀 | 오프라인 전용 자원 |

## 🤔 의사결정 기준
| 자원 | 전략 |
|---|---|
| JS/CSS (hashed) | Cache-First + 만료 30d |
| 이미지 | Stale-While-Revalidate + 100 entries cap |
| HTML | Network-First (3s timeout) → cached fallback |
| API GET (read) | Network-First or stale-while-revalidate |
| API POST (write) | Network-Only (절대 캐시 X) |
| Offline page | Cache-Only |

## ❌ 안티패턴
- **모든 요청 Cache-First**: API 응답이 영원히 stale. 사용자 답답.
- **버전 관리 없는 캐시 이름**: 새 deploy 가 옛 캐시 못 청소. `static-v1` → `static-v2`.
- **POST 요청 캐싱**: 사고. 명시적 Network-Only.
- **manifest 없는 precache**: 빌드 타임 정보 안 들어가서 누락.
- **`activate` 에서 옛 캐시 안 지움**: 무한 누적 → quota exceeded.
- **localhost 에서 SW 등록 후 디버깅 어려움**: chrome devtools "Update on reload" + "Bypass for network" 활용.
- **사용자 알림 없이 새 SW 활성화**: 페이지가 갑자기 다른 버전. `skipWaiting` + `clientsClaim` 신중히. 보통 사용자에게 "새 버전 있음, 새로고침" prompt.

## 🤖 LLM 활용 힌트
- Workbox 또는 vite-plugin-pwa 사용 가정.
- 자원 별 전략 매핑 표를 먼저 만들고 코드 작성.
- 새 SW 활성화는 사용자 동의 후 (UX 보호).

## 🔗 관련 문서
- [[Web_HTTP_Cache_Headers]]
- [[Backpressure_Patterns]]
