μλΉμ€ μμ»€κ° νμ΄μ§ λ‘λ μμ²μ μΈν°μ νΈνμ¬ μΊμ± μ λ΅, μ€νλΌμΈ κΈ°λ₯, μ΅μ μΉ μ ν리μΌμ΄μ μ μ±λ₯ ν₯μμ μ§μνλ λ°©λ²μ μ΄ν΄λ΄ λλ€.
νλ‘ νΈμλ μλΉμ€ μ컀 νμ: ν₯μλ μ¬μ©μ κ²½νμ μν νμ΄μ§ λ‘λ μΈν°μ νΈ
μλΉμ€ μ컀λ λ€νΈμν¬ μμ²μ μΈν°μ νΈνκ³ , 리μμ€λ₯Ό μΊμνλ©°, μΉ μ ν리μΌμ΄μ μ μ€νλΌμΈ κΈ°λ₯μ μ 곡ν μ μλ κ°λ ₯ν κΈ°μ μ λλ€. κ°μ₯ μν₯λ ₯ μλ κΈ°λ₯ μ€ νλλ νμ΄μ§ λ‘λ μμ²μ μΈν°μ νΈνμ¬ μ±λ₯κ³Ό μ¬μ©μ κ²½νμ νκΈ°μ μΌλ‘ ν₯μμν€λ κ²μ λλ€. μ΄ κ²μλ¬Όμμλ μλΉμ€ μμ»€κ° νμ μμ²μ μ²λ¦¬νλ λ°©λ²μ μ΄ν΄λ³΄κ³ κ°λ°μλ₯Ό μν μ€μ©μ μΈ μμ μ μ€ν κ°λ₯ν ν΅μ°°λ ₯μ μ 곡ν©λλ€.
νμ μμ² μ΄ν΄
μ½λλ₯Ό μ΄ν΄λ³΄κΈ° μ μ μλΉμ€ μ컀 컨ν μ€νΈμμ "νμ μμ²"μ΄ λ¬΄μμΈμ§ μ μν΄ λ³΄κ² μ΅λλ€. νμ μμ²μ μ¬μ©μκ° μ νμ΄μ§λ‘ μ΄λνκ±°λ νμ¬ νμ΄μ§λ₯Ό μλ‘ κ³ μΉ¨νμ¬ μμλ μμ²μ λλ€. μ΄λ¬ν μμ²μ μΌλ°μ μΌλ‘ λ€μ μμ μ μν΄ νΈλ¦¬κ±°λ©λλ€.
- λ§ν¬ ν΄λ¦(
<a>νκ·Έ) - μ£Όμ νμμ€μ URL μ λ ₯
- νμ΄μ§ μλ‘ κ³ μΉ¨
- λΈλΌμ°μ μ λ€λ‘ λλ μμΌλ‘ λ²νΌ μ¬μ©
μλΉμ€ μ컀λ μ΄λ¬ν νμ μμ²μ μΈν°μ νΈνκ³ μ²λ¦¬ λ°©λ²μ κ²°μ ν μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ μ κ΅ν μΊμ± μ λ΅μ ꡬννκ³ , μ¬μ©μκ° μ€νλΌμΈ μνμΌ λ μΊμμμ μ½ν μΈ λ₯Ό μ 곡νκ³ , ν΄λΌμ΄μΈνΈ μΈ‘μμ νμ΄μ§λ₯Ό λμ μΌλ‘ μμ±ν μ μμ΅λλ€.
μλΉμ€ μ컀 λ±λ‘
첫 λ²μ§Έ λ¨κ³λ μλΉμ€ μ컀λ₯Ό λ±λ‘νλ κ²μ λλ€. μ΄λ μΌλ°μ μΌλ‘ κΈ°λ³Έ JavaScript νμΌμμ μνλ©λλ€.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(registration => {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch(error => {
console.error('Service Worker registration failed:', error);
});
}
μ΄ μ½λλ λΈλΌμ°μ κ° μλΉμ€ μ컀λ₯Ό μ§μνλμ§ νμΈνκ³ , μ§μνλ κ²½μ° /service-worker.js νμΌμ λ±λ‘ν©λλ€. μ΄ JavaScriptκ° νλ‘λμ
νκ²½μμ 보μ 컨ν
μ€νΈ(HTTPS)μμ μ€νλλμ§ νμΈνμΈμ.
μλΉμ€ μ컀μμ νμ μμ² μΈν°μ νΈ
service-worker.js νμΌ λ΄μμ fetch μ΄λ²€νΈλ₯Ό μμ ν μ μμ΅λλ€. μ΄ μ΄λ²€νΈλ νμ μμ²μ ν¬ν¨νμ¬ μ ν리μΌμ΄μ
μμ μννλ λͺ¨λ λ€νΈμν¬ μμ²μ λν΄ νΈλ¦¬κ±°λ©λλ€. μ΄λ¬ν μμ²μ νν°λ§νμ¬ νμ μμ²μ ꡬ체μ μΌλ‘ μ²λ¦¬ν μ μμ΅λλ€.
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(async () => {
try {
// First, try to use the navigation preload response if it's supported.
const preloadResponse = await event.preloadResponse;
if (preloadResponse) {
return preloadResponse;
}
// Always try the network first.
const networkResponse = await fetch(event.request);
return networkResponse;
} catch (error) {
// catch is only triggered if an exception is thrown, which is likely
// due to a network error.
// If fetching the HTML file fails, look for a fallback.
console.log('Fetch failed; returning offline page instead.', error);
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(OFFLINE_URL);
return cachedResponse || createErrorResponse(); // Fallback if offline page unavailable
}
});
}
});
μ΄ μ½λλ₯Ό λΆμν΄ λ³΄κ² μ΅λλ€.
event.request.mode === 'navigate': μ΄ μ‘°κ±΄μ μμ²μ΄ νμ μμ²μΈμ§ νμΈν©λλ€.event.respondWith(): μ΄ λ©μλλ λΈλΌμ°μ μ μμ² μ²λ¦¬ λ°©λ²μ μλ €μ€λλ€.Responseκ°μ²΄λ‘ νμΈλλ νλ‘λ―Έμ€λ₯Ό μ¬μ©ν©λλ€.event.preloadResponse: μ΄λ νμ μ¬μ λ‘λλΌλ λ©μ»€λμ¦μ λλ€. νμ±νλλ©΄ λΈλΌμ°μ κ° μλΉμ€ μμ»€κ° μμ ν νμ±νλκΈ° μ μ νμ μμ² κ°μ Έμ€κΈ°λ₯Ό μμν μ μμ΅λλ€. μλΉμ€ μ컀 μμ μκ°μ λ€νΈμν¬ μμ²κ³Ό κ²Ήμ³μ μλ ν₯μμ μ 곡ν©λλ€.fetch(event.request): λ€νΈμν¬μμ 리μμ€λ₯Ό κ°μ Έμ΅λλ€. λ€νΈμν¬λ₯Ό μ¬μ©ν μ μλ κ²½μ° νμ΄μ§λ νμμ κ°μ΄ μλ²μμ λ‘λλ©λλ€.caches.open(CACHE_NAME): μ§μ λ μ΄λ¦(CACHE_NAMEμ μλΉμ€ μ컀 νμΌμ λ€λ₯Έ κ³³μ μ μν΄μΌ ν¨)μΌλ‘ μΊμλ₯Ό μ½λλ€.cache.match(OFFLINE_URL):OFFLINE_URL(μ: μ€νλΌμΈ νμ΄μ§)κ³Ό μΌμΉνλ μΊμλ μλ΅μ μ°Ύμ΅λλ€.createErrorResponse(): μ€λ₯ μλ΅μ λ°ννλ μ¬μ©μ μ§μ ν¨μμ λλ€. μ΄ ν¨μλ₯Ό μ¬μ©μ μ§μ νμ¬ μ¬μ©μ μΉνμ μΈ μ€νλΌμΈ κ²½νμ μ 곡ν μ μμ΅λλ€.
νμ μμ²μ μν μΊμ± μ λ΅
μ΄μ μμ μμλ κΈ°λ³Έμ μΈ λ€νΈμν¬ μ°μ μ λ΅μ 보μ¬μ€λλ€. κ·Έλ¬λ μ ν리μΌμ΄μ μ μꡬ μ¬νμ λ°λΌ λ³΄λ€ μ κ΅ν μΊμ± μ λ΅μ ꡬνν μ μμ΅λλ€.
λ€νΈμν¬ μ°μ , μΊμλ‘ λ체
μ΄λ μ΄μ μμ μ νμλ μ λ΅μ λλ€. λ¨Όμ λ€νΈμν¬μμ 리μμ€λ₯Ό κ°μ Έμ€λ €κ³ μλν©λλ€. λ€νΈμν¬ μμ²μ΄ μ€ν¨νλ©΄(μ: μ¬μ©μκ° μ€νλΌμΈ μνμΈ κ²½μ°) μΊμλ‘ λ체λ©λλ€. μ΄λ μμ£Ό μ λ°μ΄νΈλλ μ½ν μΈ μ μ ν©ν μ λ΅μ λλ€.
μΊμ μ°μ , λ°±κ·ΈλΌμ΄λμμ μ λ°μ΄νΈ
μ΄ μ λ΅μ λ¨Όμ μΊμλ₯Ό νμΈν©λλ€. μΊμμμ 리μμ€λ₯Ό μ°ΎμΌλ©΄ μ¦μ λ°νλ©λλ€. λ°±κ·ΈλΌμ΄λμμ μλΉμ€ μ컀λ λ€νΈμν¬μμ κ°μ Έμ¨ μ΅μ λ²μ μ 리μμ€λ‘ μΊμλ₯Ό μ λ°μ΄νΈν©λλ€. μ΄λ κ² νλ©΄ λΉ λ₯Έ μ΄κΈ° λ‘λκ° μ 곡λκ³ μ¬μ©μκ° νμ μ΅μ μ½ν μΈ λ₯Ό μ¬μ©ν μ μμ΅λλ€.
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
if (cachedResponse) {
// Update the cache in the background.
event.waitUntil(
fetch(event.request).then(response => {
return caches.open(CACHE_NAME).then(cache => {
return cache.put(event.request, response.clone());
});
})
);
return cachedResponse;
}
// If not found in cache, fetch from network.
return fetch(event.request);
})
);
}
});
μΊμλ§
μ΄ μ λ΅μ μΊμμμλ§ μ½ν μΈ λ₯Ό μ 곡ν©λλ€. μΊμμμ 리μμ€λ₯Ό μ°Ύμ μ μμΌλ©΄ μμ²μ΄ μ€ν¨ν©λλ€. μ΄λ μ μ μ΄κ³ μ€νλΌμΈμμ μ¬μ©ν μ μλ κ²μΌλ‘ μλ €μ§ μμ°μ μ ν©ν©λλ€.
Stale-While-Revalidate
μΊμ μ°μ κ³Ό μ μ¬νμ§λ§ event.waitUntilλ‘ λ°±κ·ΈλΌμ΄λμμ μ
λ°μ΄νΈνλ λμ μΊμλ μλ΅(μ¬μ© κ°λ₯ν κ²½μ°)μ μ¦μ λ°ννκ³ λ€νΈμν¬μμ μ΅μ λ²μ μ κ°μ Έμ μΊμλ₯Ό μ
λ°μ΄νΈνλ €κ³ *νμ* μλν©λλ€. μ΄ μ κ·Ό λ°©μμ μ¬μ©μμκ² μΊμλ λ²μ μ΄ μ¦μ μ 곡λλ―λ‘ λ§€μ° λΉ λ₯Έ μ΄κΈ° λ‘λλ₯Ό μ 곡νμ§λ§, μΊμκ° λ€μ μμ²μ λλΉνμ¬ κ°μ₯ μ΅μ λ°μ΄ν°λ‘ μ
λ°μ΄νΈλλλ‘ λ³΄μ₯ν©λλ€. μ΄λ μ€μνμ§ μμ 리μμ€ λλ μλλ₯Ό μν΄ μ½κ° μ€λλ μ 보λ₯Ό μ μ νμνλ κ²μ΄ νμ©λλ μν©μ μ ν©ν©λλ€.
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(
caches.open(CACHE_NAME).then(cache => {
return cache.match(event.request).then(cachedResponse => {
const fetchedResponse = fetch(event.request).then(networkResponse => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
// Return the cached response if we have it, otherwise wait
// for the network.
return cachedResponse || fetchedResponse;
});
})
);
}
});
νμ μ¬μ λ‘λ
νμ μ¬μ λ‘λλ λΈλΌμ°μ κ° μλΉμ€ μμ»€κ° μμ ν νμ±νλκΈ° μ μ 리μμ€ κ°μ Έμ€κΈ°λ₯Ό μμν μ μλλ‘ νλ κΈ°λ₯μ λλ€. μ΄λ νΉν μ¬μ΄νΈλ₯Ό μ²μ λ°©λ¬Έν λ νμ μμ²μ μ±λ₯μ ν¬κ² ν₯μμν¬ μ μμ΅λλ€.
νμ μ¬μ λ‘λλ₯Ό νμ±ννλ €λ©΄ λ€μμ μνν΄μΌ ν©λλ€.
- μλΉμ€ μ컀μ
activateμ΄λ²€νΈμμ νμ±νν©λλ€. fetchμ΄λ²€νΈμμpreloadResponseλ₯Ό νμΈν©λλ€.
// In the activate event:
self.addEventListener('activate', event => {
event.waitUntil(self.registration.navigationPreload.enable());
});
// In the fetch event (as shown in the initial example):
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(async () => {
const preloadResponse = await event.preloadResponse;
if (preloadResponse) {
return preloadResponse;
}
// ... rest of your fetch logic ...
});
}
});
μ€νλΌμΈ μλλ¦¬μ€ μ²λ¦¬
μλΉμ€ μ컀λ₯Ό μ¬μ©νλ μ£Όμ μ΄μ μ€ νλλ μ€νλΌμΈ κΈ°λ₯μ μ 곡ν μ μλ€λ κ²μ λλ€. μ¬μ©μκ° μ€νλΌμΈ μνμΌ λ μ ν리μΌμ΄μ μ μΊμλ λ²μ μ μ 곡νκ±°λ μ¬μ©μ μ§μ μ€νλΌμΈ νμ΄μ§λ₯Ό νμν μ μμ΅λλ€.
μ€νλΌμΈ μλ리μ€λ₯Ό μ²λ¦¬νλ €λ©΄ λ€μμ μνν΄μΌ ν©λλ€.
- HTML, CSS, JavaScript λ° μ΄λ―Έμ§λ₯Ό ν¬ν¨νμ¬ νμν μμ°μ μΊμν©λλ€.
fetchμ΄λ²€νΈμμ λ€νΈμν¬ μ€λ₯λ₯Ό ν¬μ°©νκ³ μΊμλ μ€νλΌμΈ νμ΄μ§λ₯Ό μ 곡ν©λλ€.
// Define the offline page URL and cache name
const OFFLINE_URL = '/offline.html';
const CACHE_NAME = 'my-app-cache-v1';
// Install event: cache static assets
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
return cache.addAll([
'/',
'/index.html',
'/style.css',
'/app.js',
OFFLINE_URL // Cache the offline page
]);
})
);
self.skipWaiting(); // Immediately activate the service worker
});
// Fetch event: handle navigation requests and offline fallback
self.addEventListener('fetch', event => {
if (event.request.mode === 'navigate') {
event.respondWith(async () => {
try {
// First, try to use the navigation preload response if it's supported.
const preloadResponse = await event.preloadResponse;
if (preloadResponse) {
return preloadResponse;
}
// Always try the network first.
const networkResponse = await fetch(event.request);
return networkResponse;
} catch (error) {
// catch is only triggered if an exception is thrown, which is likely
// due to a network error.
// If fetching the HTML file fails, look for a fallback.
console.log('Fetch failed; returning offline page instead.', error);
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(OFFLINE_URL);
return cachedResponse || createErrorResponse(); // Fallback if offline page unavailable
}
});
}
});
function createErrorResponse() {
return new Response(
`Offline
You are currently offline. Please check your internet connection.
`, {
headers: { 'Content-Type': 'text/html' }
}
);
}
μ΄ μ½λλ install μ΄λ²€νΈ μ€μ offline.html νμ΄μ§λ₯Ό μΊμν©λλ€. κ·Έλ° λ€μ fetch μ΄λ²€νΈμμ λ€νΈμν¬ μμ²μ΄ μ€ν¨νλ©΄(catch λΈλ‘μ΄ μ€νλ¨) μΊμμμ offline.html νμ΄μ§λ₯Ό νμΈνκ³ λΈλΌμ°μ μ λ°νν©λλ€.
κ³ κΈ κΈ°μ λ° κ³ λ € μ¬ν
μΊμ μ€ν λ¦¬μ§ API μ§μ μ¬μ©
caches κ°μ²΄λ μΊμλ μλ΅μ κ΄λ¦¬νκΈ° μν κ°λ ₯ν APIλ₯Ό μ 곡ν©λλ€. cache.put(), cache.match() λ° cache.delete()μ κ°μ λ©μλλ₯Ό μ¬μ©νμ¬ μΊμλ₯Ό μ§μ μ‘°μν μ μμ΅λλ€. μ΄λ₯Ό ν΅ν΄ 리μμ€κ° μΊμλκ³ κ²μλλ λ°©μμ μΈλΆμ μΌλ‘ μ μ΄ν μ μμ΅λλ€.
λμ μΊμ±
μ μ μμ°μ μΊμνλ κ² μΈμλ API μλ΅κ³Ό κ°μ λμ μ½ν μΈ λ₯Ό μΊμν μλ μμ΅λλ€. μ΄λ νΉν μΈν°λ· μ°κ²°μ΄ λ리거λ λΆμμ ν μ¬μ©μμκ² μ ν리μΌμ΄μ μ μ±λ₯μ ν¬κ² ν₯μμν¬ μ μμ΅λλ€.
μΊμ λ²μ κ΄λ¦¬
μ ν리μΌμ΄μ
μ΄ λ³κ²½λ λ μΊμλ 리μμ€λ₯Ό μ
λ°μ΄νΈν μ μλλ‘ μΊμ λ²μ μ κ΄λ¦¬νλ κ²μ΄ μ€μν©λλ€. μΌλ°μ μΈ μ κ·Ό λ°©μμ CACHE_NAMEμ λ²μ λ²νΈλ₯Ό ν¬ν¨νλ κ²μ
λλ€. μ ν리μΌμ΄μ
μ μ
λ°μ΄νΈν λ λ²μ λ²νΈλ₯Ό μ¦κ°μν€λ©΄ λΈλΌμ°μ κ° μ 리μμ€λ₯Ό λ€μ΄λ‘λνλλ‘ κ°μ ν©λλ€.
const CACHE_NAME = 'my-app-cache-v2'; // Increment the version number
λν μ΄μ μΊμκ° λμ λμ΄ μ€ν λ¦¬μ§ κ³΅κ°μ λλΉνλ κ²μ λ°©μ§νκΈ° μν΄ μ΄μ μΊμλ₯Ό μ κ±°ν΄μΌ ν©λλ€. activate μ΄λ²€νΈμμ μ΄ μμ
μ μνν μ μμ΅λλ€.
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
λ°±κ·ΈλΌμ΄λ λκΈ°ν
μλΉμ€ μ컀λ λν μ¬μ©μκ° μμ μ μΈ μΈν°λ· μ°κ²°μ κ°μ§ λκΉμ§ μμ μ μ°κΈ°ν μ μλ λ°±κ·ΈλΌμ΄λ λκΈ°ν APIλ₯Ό μ 곡ν©λλ€. μ΄λ μ¬μ©μκ° μ€νλΌμΈ μνμΌ λ μμμ μ μΆνκ±°λ νμΌμ μ λ‘λνλ κ²κ³Ό κ°μ μλ리μ€μ μ μ©ν©λλ€.
νΈμ μλ¦Ό
μλΉμ€ μ컀λ νΈμ μλ¦Όμ ꡬννλ λ°μλ μ¬μ©ν μ μμΌλ©°, μ΄λ₯Ό ν΅ν΄ μ¬μ©μκ° μ ν리μΌμ΄μ μ νλ°νκ² μ¬μ©νμ§ μλλΌλ λ©μμ§λ₯Ό λ³΄λΌ μ μμ΅λλ€. μ΄λ μ¬μ©μμκ² μλ‘μ΄ μ½ν μΈ , μ λ°μ΄νΈ λλ μ€μν μ΄λ²€νΈλ₯Ό μ리λ λ° μ¬μ©ν μ μμ΅λλ€.
κ΅μ ν(i18n) λ° νμ§ν(L10n) κ³ λ € μ¬ν
κΈλ‘λ² μ ν리μΌμ΄μ μμ μλΉμ€ μ컀λ₯Ό ꡬνν λλ κ΅μ ν(i18n) λ° νμ§ν(L10n)λ₯Ό κ³ λ €νλ κ²μ΄ μ€μν©λλ€. λͺ κ°μ§ μ£Όμ μΈ‘λ©΄μ λ€μκ³Ό κ°μ΅λλ€.
- μΈμ΄ κ°μ§: μ¬μ©μμ μ νΈ μΈμ΄λ₯Ό κ°μ§νλ λ©μ»€λμ¦μ ꡬνν©λλ€. μ¬κΈ°μλ
Accept-LanguageHTTP ν€λ, μ¬μ©μ μ€μ λλ λΈλΌμ°μ APIλ₯Ό μ¬μ©νλ κ²μ΄ ν¬ν¨λ μ μμ΅λλ€. - νμ§νλ μ½ν
μΈ : μ€νλΌμΈ νμ΄μ§ λ° κΈ°ν μΊμλ μ½ν
μΈ μ νμ§νλ λ²μ μ μ μ₯ν©λλ€. κ°μ§λ μΈμ΄λ₯Ό μ¬μ©νμ¬ μ μ ν λ²μ μ μ 곡ν©λλ€. μλ₯Ό λ€μ΄ μμ΄(
/offline.en.html), μ€νμΈμ΄(/offline.es.html) λ° νλμ€μ΄(/offline.fr.html)μ λν λ³λμ μ€νλΌμΈ νμ΄μ§λ₯Ό κ°μ§ μ μμ΅λλ€. κ·Έλ¬λ©΄ μλΉμ€ μ컀λ μ¬μ©μμ μΈμ΄μ λ°λΌ μΊμνκ³ μ 곡ν μ¬λ°λ₯Έ νμΌμ λμ μΌλ‘ μ νν©λλ€. - λ μ§ λ° μκ° νμ: μ€νλΌμΈ νμ΄μ§μ νμλλ λͺ¨λ λ μ§μ μκ°μ΄ μ¬μ©μμ λ‘μΊμ λ°λΌ νμμ΄ μ§μ λμλμ§ νμΈν©λλ€. μ΄ λͺ©μ μ μν΄ JavaScriptμ
IntlAPIλ₯Ό μ¬μ©ν©λλ€. - ν΅ν νμ: μ ν리μΌμ΄μ
μ ν΅ν κ°μ΄ νμλλ©΄ μ¬μ©μμ λ‘μΊ λ° ν΅νμ λ°λΌ νμμ μ§μ ν©λλ€. λ€μ λ§νμ§λ§, ν΅ν νμμ μ§μ νλ λ°
IntlAPIλ₯Ό μ¬μ©ν©λλ€. - ν μ€νΈ λ°©ν₯: μλμ΄ λ° νλΈλ¦¬μ΄μ κ°μ΄ μ€λ₯Έμͺ½μμ μΌμͺ½(RTL)μΌλ‘ μ½λ μΈμ΄λ₯Ό κ³ λ €ν©λλ€. μ€νλΌμΈ νμ΄μ§ λ° μΊμλ μ½ν μΈ λ CSSλ₯Ό μ¬μ©νμ¬ RTL ν μ€νΈ λ°©ν₯μ μ§μν΄μΌ ν©λλ€.
- 리μμ€ λ‘λ: μ¬μ©μμ μΈμ΄μ λ°λΌ νμ§νλ 리μμ€(μ: μ΄λ―Έμ§, κΈκΌ΄)λ₯Ό λμ μΌλ‘ λ‘λν©λλ€.
μ: νμ§νλ μ€νλΌμΈ νμ΄μ§ μ ν
// Function to get the user's preferred language
function getPreferredLanguage() {
// This is a simplified example. In a real application,
// you would use a more robust language detection mechanism.
return navigator.language || navigator.userLanguage || 'en';
}
// Define a mapping of languages to offline page URLs
const offlinePageUrls = {
'en': '/offline.en.html',
'es': '/offline.es.html',
'fr': '/offline.fr.html'
};
// Get the user's preferred language
const preferredLanguage = getPreferredLanguage();
// Determine the offline page URL based on the preferred language
let offlineUrl = offlinePageUrls[preferredLanguage] || offlinePageUrls['en']; // Default to English if no match
// ... rest of your service worker code, using offlineUrl to cache and serve the appropriate offline page ...
ν μ€νΈ λ° λλ²κΉ
μλΉμ€ μ컀λ₯Ό ν μ€νΈνκ³ λλ²κΉ νλ κ²μ μ΄λ €μΈ μ μμ΅λλ€. λͺ κ°μ§ νμ λ€μκ³Ό κ°μ΅λλ€.
- Chrome DevTools μ¬μ©: Chrome DevToolsλ μλΉμ€ μ컀λ₯Ό κ²μ¬νκΈ° μν μ μ© ν¨λμ μ 곡ν©λλ€. μ΄ ν¨λμ μ¬μ©νμ¬ μλΉμ€ μ컀μ μνλ₯Ό λ³΄κ³ , μΊμλ 리μμ€λ₯Ό κ²μ¬νκ³ , λ€νΈμν¬ μμ²μ λλ²κΉ ν μ μμ΅λλ€.
- μλ‘ κ³ μΉ¨ μ μλΉμ€ μ컀 μ λ°μ΄νΈ μ¬μ©: Chrome DevTools -> Application -> Service Workersμμ "μλ‘ κ³ μΉ¨ μ μ λ°μ΄νΈ"λ₯Ό μ ννμ¬ νμ΄μ§λ₯Ό μλ‘ κ³ μΉ λλ§λ€ μλΉμ€ μμ»€κ° μ λ°μ΄νΈλλλ‘ κ°μ ν μ μμ΅λλ€. μ΄λ κ°λ° μ€μ λ§€μ° μ μ©ν©λλ€.
- μ€ν λ¦¬μ§ μ§μ°κΈ°: λλ‘λ μλΉμ€ μμ»€κ° μλͺ»λ μνκ° λ μ μμ΅λλ€. λΈλΌμ°μ μ μ€ν 리μ§(μΊμ ν¬ν¨)λ₯Ό μ§μ°λ©΄ μ΄λ¬ν λ¬Έμ λ₯Ό ν΄κ²°νλ λ° λμμ΄ λ μ μμ΅λλ€.
- μλΉμ€ μ컀 ν μ€νΈ λΌμ΄λΈλ¬λ¦¬ μ¬μ©: Workboxμ κ°μ΄ μλΉμ€ μ컀λ₯Ό ν μ€νΈνλ λ° λμμ΄ λλ μ¬λ¬ λΌμ΄λΈλ¬λ¦¬κ° μμ΅λλ€.
- μ€μ μ₯μΉμμ ν μ€νΈ: λ°μ€ν¬ν± λΈλΌμ°μ μμ μλΉμ€ μ컀λ₯Ό ν μ€νΈν μ μμ§λ§ μ€μ λͺ¨λ°μΌ μ₯μΉμμ ν μ€νΈνμ¬ λ€μν λ€νΈμν¬ μ‘°κ±΄μμ μ¬λ°λ₯΄κ² μλνλμ§ νμΈνλ κ²μ΄ μ€μν©λλ€.
κ²°λ‘
μλΉμ€ μμ»€λ‘ νμ΄μ§ λ‘λ μμ²μ μΈν°μ νΈνλ κ²μ μΉ μ ν리μΌμ΄μ μ μ¬μ©μ κ²½νμ ν₯μμν€λ κ°λ ₯ν κΈ°μ μ λλ€. μΊμ± μ λ΅μ ꡬννκ³ , μ€νλΌμΈ κΈ°λ₯μ μ 곡νκ³ , λ€νΈμν¬ μμ²μ μ΅μ νν¨μΌλ‘μ¨ μ±λ₯κ³Ό μ°Έμ¬λλ₯Ό ν¬κ² ν₯μμν¬ μ μμ΅λλ€. λͺ¨λ μ¬λμκ² μΌκ΄λκ³ μ¬μ©μ μΉνμ μΈ κ²½νμ 보μ₯νκΈ° μν΄ κΈλ‘λ² λμμ μν΄ κ°λ°ν λλ κ΅μ νλ₯Ό κ³ λ €νλ κ²μ μμ§ λ§μμμ€.
μ΄ κ°μ΄λλ μλΉμ€ μ컀 νμ μΈν°μ μ μ μ΄ν΄νκ³ κ΅¬ννκΈ° μν κ²¬κ³ ν κΈ°λ°μ μ 곡ν©λλ€. μ΄ κΈ°μ μ κ³μ νμνλ©΄μ μ΄ κΈ°μ μ κΈ°λ₯μ νμ©νμ¬ λ°μ΄λ μΉ κ²½νμ λ§λ€ μ μλ λ λ§μ λ°©λ²μ λ°κ²¬ν κ²μ λλ€.