1/* 2 * ë´ëª¨ ìë¹ì¤ì커 3 * 4 * ì¤ê³ ìì¹ â ì´ ì±ì ë¡ê·¸ì¸ë êµì¬ì íì ì 보를 ë¤ë£¬ë¤. ë°ë¼ì ì¬ì©ì ë°ì´í°ê° 5 * ë´ê¸¸ ì ìë ìëµì "ì ë" ìºìíì§ ìëë¤. ìºì ëìì ë°°í¬ë§ë¤ íì¼ ì´ë¦ì´ 6 * ë°ëë ì ì ìì(_next/static)ê³¼ í°í¸Â·ìì´ì½ì²ë¼ ê³ì ê³¼ 무ê´í ê²ë¿ì´ë¤. 7 * 8 * - íë©´ ì´ë(navigate) : íì ë¤í¸ìí¬. ì¤í¨íì ëë§ ì¤íë¼ì¸ ìë´ íë©´. 9 * ìëµ ìì²´ë ìºìíì§ ìëë¤(HTMLì ì¬ì©ì ì ë³´ê° ë¤ì´ê°ë¤). 10 * - /api/*, ìë² ì¡ì : ìëì§ ìëë¤. 11 * - RSC ìì²(?_rsc=) : ìëì§ ìëë¤. íë©´ ì¡°ê°ì ì¬ì©ì ë°ì´í°ê° ë¤ì´ê°ë¤. 12 * - ì ì ìì : ìºì ì°ì . íì¼ëª ì´ í´ìë¼ ë°°í¬ë¼ë ìì´ì§ ìëë¤. 13 * - í°í¸Â·ìì´ì½Â·ì´ë¯¸ì§ : ìºì를 먼ì ì£¼ê³ ë¤ìì ì¡°ì©í ê°±ì . 14 */ 15 16const VERSION = 'v1'; 17const PRECACHE = `naemo-precache-${VERSION}`; 18const ASSETS = `naemo-assets-${VERSION}`; 19const OFFLINE_URL = '/offline.html'; 20 21// ì¤íë¼ì¸ ìë´ íë©´ì ë¡ê·¸ì¸ ìíì 무ê´í ì ì íì¼ì´ë¼ 미리 ë°ìë¬ë ìì íë¤. 22const PRECACHE_URLS = [ 23 OFFLINE_URL, 24 '/icons/icon-192.png', 25]; 26 27self.addEventListener('install', (event) => { 28 event.waitUntil( 29 caches 30 .open(PRECACHE) 31 .then((cache) => cache.addAll(PRECACHE_URLS)) 32 // 미리 ë°ê¸°ì ì¤í¨í´ë ì¤ì¹ê¹ì§ ë§ì§ë ìëë¤. ì¤íë¼ì¸ íë©´ì´ ìì ë¿ 33 // ëë¨¸ì§ ëìì ë©ì©¡í´ì¼ íë¤. 34 .catch(() => undefined) 35 .then(() => self.skipWaiting()) 36 ); 37}); 38 39self.addEventListener('activate', (event) => { 40 event.waitUntil( 41 caches 42 .keys() 43 .then((keys) => 44 Promise.all( 45 keys 46 .filter((key) => key.startsWith('naemo-') && key !== PRECACHE && key !== ASSETS) 47 .map((key) => caches.delete(key)) 48 ) 49 ) 50 .then(() => self.clients.claim()) 51 ); 52}); 53 54/** ê³ì ê³¼ 무ê´í, ìºìí´ë ëë ì ì ììì¸ì§ */ 55function isCacheableAsset(url) { 56 if (url.pathname.startsWith('/_next/static/')) return true; 57 if (url.pathname.startsWith('/fonts/')) return true; 58 if (url.pathname.startsWith('/icons/')) return true; 59 // ì¬ì´ëë° ë±ì ì ëë©ì´ì ìì´ì½(Lottie JSON). ê³ì ê³¼ 무ê´í ì ì íì¼. 60 if (url.pathname.startsWith('/lordicons/')) return true; 61 return /\.(?:woff2?|ttf|otf|png|jpe?g|svg|webp|avif|ico)$/i.test(url.pathname); 62} 63 64/** ì ë ê±´ë리면 ì ëë ê²½ë¡ì¸ì§ */ 65function isOffLimits(url, request) { 66 if (url.pathname.startsWith('/api/')) return true; 67 // ê²°ì ë ì¸ë¶ 리ë¤ì´ë í¸ê° ì¤ê°ë 구ê°ì´ë¼ ì¤ê°ì ë¼ì´ë¤ì§ ìëë¤. 68 if (url.pathname.startsWith('/payment')) return true; 69 // React Server Components ìëµ â ì¬ì©ì ë°ì´í°ê° ë¤ì´ìë¤. 70 if (url.searchParams.has('_rsc')) return true; 71 if (request.headers.get('RSC') === '1') return true; 72 return false; 73} 74 75self.addEventListener('fetch', (event) => { 76 const { request } = event; 77 78 // ì¡°íê° ìë ìì²(ìë² ì¡ì POST ë±)ì ê·¸ëë¡ íë ¤ë³´ë¸ë¤. 79 if (request.method !== 'GET') return; 80 81 let url; 82 try { 83 url = new URL(request.url); 84 } catch { 85 return; 86 } 87 88 if (url.origin !== self.location.origin) return; 89 if (isOffLimits(url, request)) return; 90 91 // íë©´ ì´ë: ë¤í¸ìí¬ë§ ì°ê³ , ì¤í¨íë©´ ì¤íë¼ì¸ ìë´ë¥¼ ë³´ì¬ì¤ë¤. 92 if (request.mode === 'navigate') { 93 event.respondWith( 94 fetch(request).catch(async () => { 95 const cached = await caches.match(OFFLINE_URL, { cacheName: PRECACHE }); 96 return ( 97 cached || 98 new Response('ì°ê²°ì´ ëê²¼ìµëë¤.', { 99 status: 503, 100 headers: { 'Content-Type': 'text/plain; charset=utf-8' }, 101 }) 102 ); 103 }) 104 ); 105 return; 106 } 107 108 if (!isCacheableAsset(url)) return; 109 110 event.respondWith( 111 caches.open(ASSETS).then(async (cache) => { 112 const cached = await cache.match(request); 113 114 const network = fetch(request) 115 .then((response) => { 116 // ì ì ìëµë§ ì ì¥íë¤. 리ë¤ì´ë í¸ë ì¤ë¥ë¥¼ ìºìì ë£ì¼ë©´ 117 // ë¤ìë¶í° ê·¸ ìíë¡ êµ³ëë¤. 118 if (response && response.status === 200 && response.type === 'basic') { 119 cache.put(request, response.clone()); 120 } 121 return response; 122 }) 123 .catch(() => undefined); 124 125 // ìºìê° ìì¼ë©´ ì¦ì ì£¼ê³ , ê°±ì ì ë¤ìì ì§ííë¤. 126 if (cached) { 127 event.waitUntil(network); 128 return cached; 129 } 130 131 const response = await network; 132 return response || Response.error(); 133 }) 134 ); 135}); 136 137// ì ë²ì ì´ ì¬ë¼ìì ë íì´ì§ê° ìì²íë©´ ì¦ì êµì²´íë¤. 138self.addEventListener('message', (event) => {
139 if (event.data === 'SKIP_WAITING') self.skipWaiting(); 140});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.