How to Develop a PWA (Progressive Web App): Claude Code 활용 가이드
develop a pwa (progressive web app): Claude Code 활용. 실용적인 코드 예시와 단계별 가이드를 포함합니다.
PWAとは
PWA(Progressive Web App)はWebの技術でネイティブ앱に近い体験を提供するアプローチです。Claude Code를 활용하면 Service Workerやマニフェストの설정を효율적으로구현할 수 있습니다。기본적인使い方はClaude Code入門가이드를 참고하세요.
Web App Manifest
{
"name": "My PWA Application",
"short_name": "MyPWA",
"description": "高速で信頼性の高いWebアプリケーション",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#3b82f6",
"orientation": "portrait-primary",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icons/icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
Service Worker の구현
// sw.ts
const CACHE_NAME = "app-cache-v1";
const STATIC_ASSETS = [
"/",
"/offline.html",
"/styles/main.css",
"/scripts/app.js",
"/icons/icon-192.png",
];
// Cache static assets on install
self.addEventListener("install", (event: ExtendableEvent) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(STATIC_ASSETS);
})
);
// Activate immediately
(self as any).skipWaiting();
});
// Delete old caches
self.addEventListener("activate", (event: ExtendableEvent) => {
event.waitUntil(
caches.keys().then((keys) => {
return Promise.all(
keys
.filter((key) => key !== CACHE_NAME)
.map((key) => caches.delete(key))
);
})
);
(self as any).clients.claim();
});
캐시戦略
// Stale While Revalidate 戦略
self.addEventListener("fetch", (event: FetchEvent) => {
const { request } = event;
const url = new URL(request.url);
// API 요청: Network First
if (url.pathname.startsWith("/api/")) {
event.respondWith(networkFirst(request));
return;
}
// 静的アセット: Cache First
if (request.destination === "image" || url.pathname.match(/\.(css|js)$/)) {
event.respondWith(cacheFirst(request));
return;
}
// HTML페이지: Stale While Revalidate
event.respondWith(staleWhileRevalidate(request));
});
async function networkFirst(request: Request): Promise<Response> {
try {
const response = await fetch(request);
const cache = await caches.open(CACHE_NAME);
cache.put(request, response.clone());
return response;
} catch {
const cached = await caches.match(request);
return cached || new Response('{"error": "offline"}', {
status: 503,
headers: { "Content-Type": "application/json" },
});
}
}
async function cacheFirst(request: Request): Promise<Response> {
const cached = await caches.match(request);
if (cached) return cached;
const response = await fetch(request);
const cache = await caches.open(CACHE_NAME);
cache.put(request, response.clone());
return response;
}
async function staleWhileRevalidate(
request: Request
): Promise<Response> {
const cache = await caches.open(CACHE_NAME);
const cached = await cache.match(request);
const fetchPromise = fetch(request).then((response) => {
cache.put(request, response.clone());
return response;
});
return cached || fetchPromise;
}
オフライン페이지
// オフライン検出컴포넌트
import { useState, useEffect } from "react";
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener("online", handleOnline);
window.addEventListener("offline", handleOffline);
return () => {
window.removeEventListener("online", handleOnline);
window.removeEventListener("offline", handleOffline);
};
}, []);
return isOnline;
}
function OfflineBanner() {
const isOnline = useOnlineStatus();
if (isOnline) return null;
return (
<div className="bg-yellow-500 text-white text-center p-2">
オフラインです。一部の機能が制限されています。
</div>
);
}
Service Worker の등록
// Service Worker등록
async function registerServiceWorker() {
if (!("serviceWorker" in navigator)) {
console.log("Service Worker not supported");
return;
}
try {
const registration = await navigator.serviceWorker.register(
"/sw.js",
{ scope: "/" }
);
// 업데이트チェック
registration.addEventListener("updatefound", () => {
const newWorker = registration.installing;
newWorker?.addEventListener("statechange", () => {
if (
newWorker.state === "installed" &&
navigator.serviceWorker.controller
) {
// 新しいバージョンが利用可能
showUpdateNotification();
}
});
});
console.log("Service Worker registered:", registration.scope);
} catch (error) {
console.error("Registration failed:", error);
}
}
Claude Code로の구현プロンプト
PWA개발をClaude Code에依頼する例です。성능에 대해서는코드 분할・지연 로딩も合わせて参照して주세요。
既存のReactアプリをPWA化して。
- Web App Manifestの作成
- Service Workerでオフライン対応
- 静的アセットはCache First、APIはNetwork First
- アプリ更新通知の実装
- Lighthouse PWAスコア100点を目指して
PWA의 상세 정보는web.dev PWA가이드를 참고하세요.Claude Codeの最新情報は공식 문서에서 확인할 수 있습니다.
정리
PWAはWebとネイティブの良いところ取りができる技術です。Claude Code를 활용하면 Service Workerの캐시戦略からオフライン대응まで、一貫したPWA구현を효율적으로進められます。
Claude Code 워크플로우를 한 단계 업그레이드하세요
지금 바로 Claude Code에 복사해 쓸 수 있는 검증된 프롬프트 템플릿 50선.
이 글을 작성한 사람
Masa
Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.
관련 글
Claude Code로 리팩토링을 자동화하는 방법
Claude Code를 활용해 코드 리팩토링을 효율적으로 자동화하는 방법을 알아봅니다. 실전 프롬프트와 구체적인 리팩토링 패턴을 소개합니다.
Claude Code로 사이드 프로젝트 개발 속도를 극대화하는 방법 [예제 포함]
Claude Code를 활용해 개인 프로젝트 개발 속도를 획기적으로 높이는 방법을 알아봅니다. 실전 예제와 아이디어부터 배포까지의 워크플로를 포함합니다.
Complete CORS Configuration Guide: Claude Code 활용 가이드
complete cors configuration guide: Claude Code 활용. 실용적인 팁과 코드 예시를 포함합니다.