๊ณ ๊ธ ์๋น์ค ์์ปค ํจํด์ ํ์ํ์ฌ ํ๋ก๊ทธ๋ ์๋ธ ์น ์ฑ์ ์ฑ๋ฅ, ์์ ์ฑ ๋ฐ ์ฐธ์ฌ๋๋ฅผ ์ ์ธ๊ณ์ ์ผ๋ก ์ต์ ํํ์ธ์. ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ, ์ฌ์ ์บ์ฑ ์ ๋ต, ์ฝํ ์ธ ์ ๋ฐ์ดํธ ๋ฉ์ปค๋์ง๊ณผ ๊ฐ์ ๊ธฐ์ ์ ๋ฐฐ์ฐ์ธ์.
ํ๋ก๊ทธ๋ ์๋ธ ์น ์ฑ: ๊ธ๋ก๋ฒ ์ฑ๊ณต์ ์ํ ๊ณ ๊ธ ์๋น์ค ์์ปค ํจํด
ํ๋ก๊ทธ๋ ์๋ธ ์น ์ฑ(PWA)์ ๋ธ๋ผ์ฐ์ ๋ด์์ ์ง์ ์ฑ๊ณผ ์ ์ฌํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ฉฐ ์ฐ๋ฆฌ๊ฐ ์น์ ๊ฒฝํํ๋ ๋ฐฉ์์ ํ๋ช ์ ์ผ์ผ์ผฐ์ต๋๋ค. PWA ๊ธฐ๋ฅ์ ํต์ฌ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์คํ๋๋ ์คํฌ๋ฆฝํธ์ธ ์๋น์ค ์์ปค(Service Worker)๋ก, ์คํ๋ผ์ธ ์ก์ธ์ค, ํธ์ ์๋ฆผ, ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ์ ๊ฐ์ ๊ธฐ๋ฅ์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค. ๊ธฐ๋ณธ์ ์ธ ์๋น์ค ์์ปค ๊ตฌํ์ ๋น๊ต์ ๊ฐ๋จํ์ง๋ง, ํนํ ๊ธ๋ก๋ฒ ์ฌ์ฉ์๋ฅผ ๋์์ผ๋ก ํ ๋ ์ง์ ์ผ๋ก ๊ฒฌ๊ณ ํ๊ณ ๋งค๋ ฅ์ ์ธ PWA๋ฅผ ๊ตฌ์ถํ๋ ค๋ฉด ๊ณ ๊ธ ํจํด์ ํ์ฉํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
๊ธฐ๋ณธ ์ฌํญ ์ดํดํ๊ธฐ: ์๋น์ค ์์ปค ๋ค์ ๋ณด๊ธฐ
๊ณ ๊ธ ํจํด์ ๋ํด ์์๋ณด๊ธฐ ์ ์ ์๋น์ค ์์ปค์ ํต์ฌ ๊ฐ๋ ์ ๊ฐ๋ตํ๊ฒ ๋ณต์ตํด ๋ณด๊ฒ ์ต๋๋ค.
- ์๋น์ค ์์ปค๋ ์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ์ด๋ฉฐ ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ๋คํธ์ํฌ ์ฌ์ด์ ํ๋ก์ ์ญํ ์ ํฉ๋๋ค.
- ๋ณ๋์ ์ค๋ ๋์์ ์คํ๋์ด ๋ฉ์ธ ๋ธ๋ผ์ฐ์ ์ค๋ ๋์ ๋ ๋ฆฝ์ ์ด๋ฏ๋ก ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ์ฐจ๋จํ์ง ์์ต๋๋ค.
- ์๋น์ค ์์ปค๋ ๊ฐ๋ ฅํ API์ ์ ๊ทผํ ์ ์์ผ๋ฉฐ, ์ฌ๊ธฐ์๋ Cache API, Fetch API, Push API๊ฐ ํฌํจ๋ฉ๋๋ค.
- ์๋ช ์ฃผ๊ธฐ(Lifecycle)๋ฅผ ๊ฐ์ง๋๋ค: ๋ฑ๋ก, ์ค์น, ํ์ฑํ, ์ข ๋ฃ.
์ด๋ฌํ ์ํคํ ์ฒ๋ฅผ ํตํด ์๋น์ค ์์ปค๋ ๋คํธ์ํฌ ์์ฒญ์ ๊ฐ๋ก์ฑ๊ณ , ๋ฆฌ์์ค๋ฅผ ์บ์ํ๋ฉฐ, ์คํ๋ผ์ธ์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ๊ณตํ๊ณ , ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ ์ ๊ด๋ฆฌํ์ฌ ํนํ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ๋ถ์์ ํ ์ง์ญ์์ ์ฌ์ฉ์ ๊ฒฝํ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค. ์ธ๋์ ์๊ณจ ์ง์ญ ์ฌ์ฉ์๊ฐ ๊ฐํ์ ์ธ 2G ์ฐ๊ฒฐ ์ํ์์๋ ๋ด์ค PWA์ ์ ์ํ๋ ๊ฒ์ ์์ํด ๋ณด์ธ์. ์ ๊ตฌํ๋ ์๋น์ค ์์ปค๊ฐ ์ด๋ฅผ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
๊ณ ๊ธ ์บ์ฑ ์ ๋ต: ๊ธฐ๋ณธ ์ฌ์ ์บ์ฑ์ ๋์ด์
์บ์ฑ์ ์๋น์ค ์์ปค์ ๊ฐ์ฅ ์ค์ํ ๊ธฐ๋ฅ์ด๋ผ๊ณ ํ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ ์ฌ์ ์บ์ฑ(์ค์น ์ค์ ํ์ ์์ ์ ์บ์ฑํ๋ ๊ฒ)๋ ์ข์ ์์์ ์ด์ง๋ง, ์ต์ ์ ์ฑ๋ฅ๊ณผ ํจ์จ์ ์ธ ๋ฆฌ์์ค ๊ด๋ฆฌ๋ฅผ ์ํด์๋ ๊ณ ๊ธ ์บ์ฑ ์ ๋ต์ด ํ์ํฉ๋๋ค. ์ฝํ ์ธ ์ ํ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ ๋ต์ด ์ ํฉํฉ๋๋ค.
์บ์ ์ฐ์ , ๋คํธ์ํฌ ํด๋ฐฑ (Cache-First, Network-Fallback)
์ด ์ ๋ต์ ์บ์๋ฅผ ์ฐ์ ์ํฉ๋๋ค. ์๋น์ค ์์ปค๋ ๋จผ์ ์์ฒญ๋ ๋ฆฌ์์ค๊ฐ ์บ์์ ์๋์ง ํ์ธํฉ๋๋ค. ์์ผ๋ฉด ์บ์๋ ๋ฒ์ ์ ์ฆ์ ์ ๊ณตํฉ๋๋ค. ์์ผ๋ฉด ์๋น์ค ์์ปค๋ ๋คํธ์ํฌ์์ ๋ฆฌ์์ค๋ฅผ ๊ฐ์ ธ์ ๋์ค์ ์ฌ์ฉํ ์ ์๋๋ก ์บ์ํ ๋ค์ ์ฌ์ฉ์์๊ฒ ์ ๊ณตํฉ๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ๋ฐ์ด๋ ์คํ๋ผ์ธ ์ง์๊ณผ ์์ฃผ ์ก์ธ์คํ๋ ์ฝํ ์ธ ์ ๋ํ ๋น ๋ฅธ ๋ก๋ฉ ์๊ฐ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ฏธ์ง, ํฐํธ, ์คํ์ผ์ํธ์ ๊ฐ์ ์ ์ ์์ ์ ์ ํฉํฉ๋๋ค.
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request).then(response => {
return caches.open('dynamic-cache').then(cache => {
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
๋คํธ์ํฌ ์ฐ์ , ์บ์ ํด๋ฐฑ (Network-First, Cache-Fallback)
์ด ์ ๋ต์ ๋คํธ์ํฌ๋ฅผ ์ฐ์ ์ํฉ๋๋ค. ์๋น์ค ์์ปค๋ ๋จผ์ ๋คํธ์ํฌ์์ ๋ฆฌ์์ค๋ฅผ ๊ฐ์ ธ์ค๋ ค๊ณ ์๋ํฉ๋๋ค. ๋คํธ์ํฌ ์์ฒญ์ด ์ฑ๊ณตํ๋ฉด ๋ฆฌ์์ค๊ฐ ์ฌ์ฉ์์๊ฒ ์ ๊ณต๋๊ณ ๋์ค์ ์ฌ์ฉํ ์ ์๋๋ก ์บ์๋ฉ๋๋ค. ๋คํธ์ํฌ ์์ฒญ์ด ์คํจํ๋ฉด(์: ์ธํฐ๋ท ์ฐ๊ฒฐ ์์) ์๋น์ค ์์ปค๋ ์บ์๋ก ํด๋ฐฑํฉ๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉ์๊ฐ ์จ๋ผ์ธ์ผ ๋ ํญ์ ์ต์ ์ฝํ ์ธ ๋ฅผ ๋ฐ๋๋ก ๋ณด์ฅํ๋ฉด์๋ ์บ์๋ ๋ฒ์ ์ ๋ํ ์คํ๋ผ์ธ ์ก์ธ์ค๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋ด์ค ๊ธฐ์ฌ๋ ์์ ๋ฏธ๋์ด ํผ๋์ ๊ฐ์ด ์์ฃผ ๋ณ๊ฒฝ๋๋ ๋์ ์ฝํ ์ธ ์ ์ด์์ ์ ๋๋ค.
self.addEventListener('fetch', event => {
event.respondWith(
fetch(event.request).then(response => {
return caches.open('dynamic-cache').then(cache => {
cache.put(event.request, response.clone());
return response;
});
}).catch(error => {
return caches.match(event.request);
})
);
});
์บ์ ์ ์ฉ (Cache-Only)
์ด ์ ๋ต์ ์บ์์์๋ง ๋ฆฌ์์ค๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋ฆฌ์์ค๊ฐ ์บ์์ ์์ผ๋ฉด ์์ฒญ์ ์คํจํฉ๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ํต์ฌ ์ ํ๋ฆฌ์ผ์ด์ ํ์ผ์ด๋ ์ฌ์ ์ค์น๋ ๋ฆฌ์์ค์ ๊ฐ์ด ์ ์ ์ด๊ณ ๋ณ๊ฒฝ๋ ๊ฐ๋ฅ์ฑ์ด ์๋ ๊ฒ์ผ๋ก ์๋ ค์ง ์์ ์ ์ ํฉํฉ๋๋ค.
๋คํธ์ํฌ ์ ์ฉ (Network-Only)
์ด ์ ๋ต์ ํญ์ ๋คํธ์ํฌ์์ ๋ฆฌ์์ค๋ฅผ ๊ฐ์ ธ์ค๋ฉฐ ์บ์๋ฅผ ์์ ํ ์ฐํํฉ๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ ์ค์๊ฐ ์ ๋ณด์ ๊ฐ์ด ์ ๋ ์บ์ํด์๋ ์ ๋๋ ๋ฆฌ์์ค์ ์ ํฉํฉ๋๋ค.
์ฌ๊ฒ์ฆ ์ค์๋ ์ค๋๋ ๊ฒ ์ฌ์ฉ (Stale-While-Revalidate)
์ด ์ ๋ต์ ์บ์๋ ๋ฒ์ ์ ๋ฆฌ์์ค๋ฅผ ์ฆ์ ์ ๊ณตํ๋ฉด์ ๋์์ ๋คํธ์ํฌ์์ ์ต์ ๋ฒ์ ์ ๊ฐ์ ธ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์บ์๋ฅผ ์ ๋ฐ์ดํธํฉ๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ๋งค์ฐ ๋น ๋ฅธ ์ด๊ธฐ ๋ก๋ฉ ์๊ฐ์ ์ ๊ณตํ๋ ๋์์ ์ฌ์ฉ์๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํ ์ฆ์ ๊ฐ์ฅ ์ต์ ์ฝํ ์ธ ๋ฅผ ๋ฐ์ ์ ์๋๋ก ๋ณด์ฅํฉ๋๋ค. ์๋์ ์ต์ ์ฑ ์ฌ์ด์ ํ๋ฅญํ ํํ์ ์ผ๋ก, ์ฝ๊ฐ์ ์ง์ฐ์ด ํ์ฉ๋๋ ์์ฃผ ์ ๋ฐ์ดํธ๋๋ ์ฝํ ์ธ ์ ์์ฃผ ์ฌ์ฉ๋ฉ๋๋ค. ์ ์์๊ฑฐ๋ PWA์์ ์ํ ๋ชฉ๋ก์ ํ์ํ๋ ๊ฒ์ ์์ํด ๋ณด์ธ์. ์ฌ์ฉ์๋ ์บ์๋ ๊ฐ๊ฒฉ์ ์ฆ์ ๋ณผ ์ ์์ผ๋ฉฐ, ์ต์ ๊ฐ๊ฒฉ์ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๊ฐ์ ธ์ ์บ์๋ฉ๋๋ค.
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
const fetchPromise = fetch(event.request).then(networkResponse => {
caches.open('dynamic-cache').then(cache => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
});
return response || fetchPromise;
})
);
});
๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ: ๊ฐํ์ ์ธ ๋คํธ์ํฌ ์ฒ๋ฆฌ
๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ ์๋น์ค ์์ปค๊ฐ ๊ธฐ๊ธฐ์ ์์ ์ ์ธ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ์์ ๋๊น์ง ์์ ์ ์ฐ๊ธฐํ ์ ์๊ฒ ํด์ค๋๋ค. ์ด๋ ๋คํธ์ํฌ ์ก์ธ์ค๊ฐ ํ์ํ์ง๋ง ์๊ฐ์ด ์ค์ํ์ง ์์ ์์ (์: ์์ ์ ์ถ ๋๋ ์๋ฒ ๋ฐ์ดํฐ ์ ๋ฐ์ดํธ)์ ํนํ ์ ์ฉํฉ๋๋ค. ์ธ๋๋ค์์์ ์ฌ์ฉ์๊ฐ ๋ถ์์ ํ ๋ชจ๋ฐ์ผ ๋ฐ์ดํฐ ์ง์ญ์ ์ฌํํ๋ฉด์ PWA์์ ์ฐ๋ฝ์ฒ ์์์ ์์ฑํ๋ ๊ฒฝ์ฐ๋ฅผ ์๊ฐํด ๋ณด์ธ์. ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ ์์ ์ ์ถ์ด ๋๊ธฐ์ด์ ์ถ๊ฐ๋๊ณ ์ฐ๊ฒฐ์ด ๋ค์ ์ค์ ๋ ๋ ์๋์ผ๋ก ์ ์ก๋๋๋ก ๋ณด์ฅํฉ๋๋ค.
๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ๋จผ์ ์๋น์ค ์์ปค์ ๋ฑ๋กํด์ผ ํฉ๋๋ค:
self.addEventListener('sync', event => {
if (event.tag === 'my-background-sync') {
event.waitUntil(doSomeBackgroundTask());
}
});
๊ทธ๋ฐ ๋ค์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ฅผ ์์ฒญํ ์ ์์ต๋๋ค:
navigator.serviceWorker.ready.then(swRegistration => {
return swRegistration.sync.register('my-background-sync');
});
`event.tag`๋ฅผ ์ฌ์ฉํ๋ฉด ๋ค๋ฅธ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ ์์ฒญ์ ๊ตฌ๋ณํ ์ ์์ต๋๋ค. `event.waitUntil()` ๋ฉ์๋๋ ๋ธ๋ผ์ฐ์ ์๊ฒ ์์ ์ด ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ ธ๋ค๊ฐ ์๋น์ค ์์ปค๋ฅผ ์ข ๋ฃํ๋๋ก ์ง์ํฉ๋๋ค.
ํธ์ ์๋ฆผ: ์ฌ์ฉ์์๊ฒ ๋ฅ๋์ ์ผ๋ก ๋ค๊ฐ๊ฐ๊ธฐ
ํธ์ ์๋ฆผ์ ์ฌ์ฉํ๋ฉด ์น ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ธ๋ผ์ฐ์ ์์ ํ๋ฐํ๊ฒ ์คํ๋๊ณ ์์ง ์์ ๋๋ ์๋น์ค ์์ปค๊ฐ ์ฌ์ฉ์์๊ฒ ๋ฉ์์ง๋ฅผ ๋ณด๋ผ ์ ์์ต๋๋ค. ์ด๋ ์ฌ์ฉ์๋ฅผ ๋ค์ ์ฐธ์ฌ์ํค๊ณ ์๊ธฐ์ ์ ํ ์ ๋ณด๋ฅผ ์ ๋ฌํ๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ ๋๋ค. ๋ธ๋ผ์ง์ ์ฌ์ฉ์๊ฐ ๊ทธ๋ ์ฌ์ดํธ๋ฅผ ๋ฐฉ๋ฌธํ์ง ์์๋๋ผ๋ ์ข์ํ๋ ์ ์์๊ฑฐ๋ PWA์์ ๋ฐ์ง ์ธ์ผ์ ๋ํ ์๋ฆผ์ ๋ฐ๋ ๊ฒ์ ์์ํด ๋ณด์ธ์. ํธ์ ์๋ฆผ์ ํธ๋ํฝ์ ์ ๋ํ๊ณ ์ ํ์จ์ ๋์ผ ์ ์์ต๋๋ค.
ํธ์ ์๋ฆผ์ ์ฌ์ฉํ๋ ค๋ฉด ๋จผ์ ์ฌ์ฉ์๋ก๋ถํฐ ๊ถํ์ ์ป์ด์ผ ํฉ๋๋ค:
navigator.serviceWorker.ready.then(swRegistration => {
return swRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: 'YOUR_PUBLIC_VAPID_KEY'
});
}).then(subscription => {
// Send subscription details to your server
});
๋ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ํธ์ ์๋น์ค์ ์์ ํ๊ฒ ์๋ณํ๊ธฐ ์ํด VAPID(Voluntary Application Server Identification) ํค ์์ด ํ์ํฉ๋๋ค. ๊ณต๊ฐ ํค๋ ๊ตฌ๋ ์์ฒญ์ ํฌํจ๋๋ฉฐ, ๊ฐ์ธ ํค๋ ์๋ฒ์์ ํธ์ ์๋ฆผ ํ์ด๋ก๋๋ฅผ ์๋ช ํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
๊ตฌ๋ ์ ๋ณด๋ฅผ ์ป์ผ๋ฉด web-push์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฒ์์ ํธ์ ์๋ฆผ์ ๋ณด๋ผ ์ ์์ต๋๋ค:
const webpush = require('web-push');
webpush.setVapidDetails(
'mailto:your_email@example.com',
'YOUR_PUBLIC_VAPID_KEY',
'YOUR_PRIVATE_VAPID_KEY'
);
const pushSubscription = {
endpoint: '...', // User's subscription endpoint
keys: { p256dh: '...', auth: '...' } // User's encryption keys
};
const payload = JSON.stringify({
title: 'New Notification!',
body: 'Check out this awesome offer!',
icon: '/images/icon.png'
});
webpush.sendNotification(pushSubscription, payload)
.catch(error => console.error(error));
ํด๋ผ์ด์ธํธ ์ธก์ ์๋น์ค ์์ปค์์๋ ํธ์ ์๋ฆผ ์ด๋ฒคํธ๋ฅผ ์์ ํ ์ ์์ต๋๋ค:
self.addEventListener('push', event => {
const payload = event.data.json();
event.waitUntil(
self.registration.showNotification(payload.title, {
body: payload.body,
icon: payload.icon
})
);
});
์ฝํ ์ธ ์ ๋ฐ์ดํธ ์ฒ๋ฆฌ: ์ฌ์ฉ์๊ฐ ์ต์ ๋ฒ์ ์ ๋ณด๋๋ก ๋ณด์ฅํ๊ธฐ
์บ์ฑ์ ๊ณผ์ ์ค ํ๋๋ ์ฌ์ฉ์๊ฐ ์ฝํ ์ธ ์ ์ต์ ๋ฒ์ ์ ๋ณด๋๋ก ๋ณด์ฅํ๋ ๊ฒ์ ๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ฌ๋ฌ ์ ๋ต์ ์ฌ์ฉํ ์ ์์ต๋๋ค:
๋ฒ์ ์ด ์ง์ ๋ ์์
์์ ์ ํ์ผ ์ด๋ฆ์ ๋ฒ์ ๋ฒํธ๋ฅผ ํฌํจ์ํต๋๋ค(์: `style.v1.css`, `script.v2.js`). ์์ ์ ์ ๋ฐ์ดํธํ ๋ ๋ฒ์ ๋ฒํธ๋ฅผ ๋ณ๊ฒฝํฉ๋๋ค. ์๋น์ค ์์ปค๋ ์ ๋ฐ์ดํธ๋ ์์ ์ ์๋ก์ด ๋ฆฌ์์ค๋ก ์ทจ๊ธํ๊ณ ๊ทธ์ ๋ฐ๋ผ ์บ์ํฉ๋๋ค. ์ด ์ ๋ต์ ๊ฑฐ์ ๋ณ๊ฒฝ๋์ง ์๋ ์ ์ ์์ ์ ํนํ ํจ๊ณผ์ ์ ๋๋ค. ์๋ฅผ ๋ค์ด, ๋ฐ๋ฌผ๊ด PWA๋ ๋ฐฉ๋ฌธ์๊ฐ ํญ์ ์ต์ ์ ๋ณด์ ์ ๊ทผํ ์ ์๋๋ก ์ ์๋ฌผ์ ์ด๋ฏธ์ง์ ์ค๋ช ์ ๋ฒ์ ์ ์ง์ ํ ์ ์์ต๋๋ค.
์บ์ ๋ฌดํจํ (Cache Busting)
์์ ์ URL์ ์ฟผ๋ฆฌ ๋ฌธ์์ด์ ์ถ๊ฐํฉ๋๋ค(์: `style.css?v=1`, `script.js?v=2`). ์ฟผ๋ฆฌ ๋ฌธ์์ด์ ์บ์ ๋ฒ์คํฐ ์ญํ ์ ํ์ฌ ๋ธ๋ผ์ฐ์ ๊ฐ ์์ ์ ์ต์ ๋ฒ์ ์ ๊ฐ์ ๋ก ๊ฐ์ ธ์ค๊ฒ ํฉ๋๋ค. ์ด๋ ๋ฒ์ ์ด ์ง์ ๋ ์์ ๊ณผ ์ ์ฌํ์ง๋ง ํ์ผ ์์ฒด์ ์ด๋ฆ์ ๋ฐ๊พธ๋ ๊ฒ์ ํผํ ์ ์์ต๋๋ค.
์๋น์ค ์์ปค ์ ๋ฐ์ดํธ
์๋น์ค ์์ปค ์์ฒด๋ ์ ๋ฐ์ดํธ๋ ์ ์์ต๋๋ค. ๋ธ๋ผ์ฐ์ ๊ฐ ์๋น์ค ์์ปค์ ์ ๋ฒ์ ์ ๊ฐ์งํ๋ฉด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ค์นํฉ๋๋ค. ์ ์๋น์ค ์์ปค๋ ์ฌ์ฉ์๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ซ์๋ค๊ฐ ๋ค์ ์ด ๋ ์ ์ด๊ถ์ ๊ฐ๊ฒ ๋ฉ๋๋ค. ์ฆ๊ฐ์ ์ธ ์ ๋ฐ์ดํธ๋ฅผ ๊ฐ์ ํ๋ ค๋ฉด ์ค์น ์ด๋ฒคํธ์์ `self.skipWaiting()`์, ํ์ฑํ ์ด๋ฒคํธ์์ `self.clients.claim()`์ ํธ์ถํ ์ ์์ต๋๋ค. ์ด ์ ๊ทผ ๋ฐฉ์์ ์ด์ ์๋น์ค ์์ปค๊ฐ ์ ์ดํ๋ ๋ชจ๋ ํด๋ผ์ด์ธํธ๊ฐ ์ฆ์ ์ ์๋น์ค ์์ปค์ ์ํด ์ ์ด๋๋๋ก ๋ณด์ฅํฉ๋๋ค.
self.addEventListener('install', event => {
// Force the waiting service worker to become the active service worker.
self.skipWaiting();
});
self.addEventListener('activate', event => {
// Become available to all matching pages
event.waitUntil(self.clients.claim());
});
๊ตญ์ ํ ๋ฐ ํ์งํ ๊ณ ๋ ค ์ฌํญ
๊ธ๋ก๋ฒ ์ฌ์ฉ์๋ฅผ ์ํ PWA๋ฅผ ๊ตฌ์ถํ ๋ ๊ตญ์ ํ(i18n)์ ํ์งํ(l10n)๋ ๋งค์ฐ ์ค์ํฉ๋๋ค. ์๋น์ค ์์ปค๋ ํ์งํ๋ ์ฝํ ์ธ ๋ฅผ ํจ์จ์ ์ผ๋ก ์ ๋ฌํ๋ ๋ฐ ์ค์ํ ์ญํ ์ ํฉ๋๋ค.
ํ์งํ๋ ๋ฆฌ์์ค ์บ์ฑ
์ฌ์ฉ์์ ์ธ์ด์ ๋ฐ๋ผ ๋ค๋ฅธ ๋ฒ์ ์ ๋ฆฌ์์ค๋ฅผ ์บ์ํฉ๋๋ค. ์์ฒญ์ `Accept-Language` ํค๋๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์์ ์ ํธ ์ธ์ด๋ฅผ ๊ฒฐ์ ํ๊ณ ์ ์ ํ ์บ์๋ ๋ฒ์ ์ ์ ๊ณตํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ํ๋์ค ์ฌ์ฉ์๊ฐ ๊ธฐ์ฌ๋ฅผ ์์ฒญํ๋ฉด ์๋น์ค ์์ปค๋ ์บ์์์ ํ๋์ค์ด ๋ฒ์ ์ ๊ธฐ์ฌ๋ฅผ ์ฐ์ ์ ์ผ๋ก ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค. ์ธ์ด๋ณ๋ก ๋ค๋ฅธ ์บ์ ์ด๋ฆ์ด๋ ํค๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋์ ์ฝํ ์ธ ํ์งํ
์ฝํ ์ธ ๊ฐ ๋์ ์ผ๋ก ์์ฑ๋๋ ๊ฒฝ์ฐ, ๊ตญ์ ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(์: i18next)๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์์ ๋ก์ผ์ผ์ ๋ฐ๋ผ ๋ ์ง, ์ซ์, ํตํ๋ฅผ ํ์ํํฉ๋๋ค. ์๋น์ค ์์ปค๋ ํ์งํ๋ ๋ฐ์ดํฐ๋ฅผ ์บ์ํ๊ณ ์คํ๋ผ์ธ์์ ์ฌ์ฉ์์๊ฒ ์ ๊ณตํ ์ ์์ต๋๋ค. ํญ๊ณตํธ ๊ฐ๊ฒฉ์ ํ์ํ๋ ์ฌํ PWA๋ฅผ ์๊ฐํด ๋ณด์ธ์. ์๋น์ค ์์ปค๋ ๊ฐ๊ฒฉ์ด ์ฌ์ฉ์์ ํ์ง ํตํ์ ํ์์ผ๋ก ํ์๋๋๋ก ๋ณด์ฅํด์ผ ํฉ๋๋ค.
์คํ๋ผ์ธ ์ธ์ด ํฉ
ํ ์คํธ ์ฝํ ์ธ ๊ฐ ๋ง์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ ์คํ๋ผ์ธ ์ธ์ด ํฉ ์ ๊ณต์ ๊ณ ๋ คํด ๋ณด์ธ์. ์ฌ์ฉ์๋ ์ ํธํ๋ ์ธ์ด์ ์ธ์ด ํฉ์ ๋ค์ด๋ก๋ํ์ฌ ๋ชจ๊ตญ์ด๋ก ์คํ๋ผ์ธ์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฝํ ์ธ ์ ์ ๊ทผํ ์ ์์ต๋๋ค. ์ด๋ ์ธํฐ๋ท ์ฐ๊ฒฐ์ด ์ ํ์ ์ด๊ฑฐ๋ ๋ถ์์ ํ ์ง์ญ์์ ํนํ ์ ์ฉํ ์ ์์ต๋๋ค.
์๋น์ค ์์ปค ๋๋ฒ๊น ๋ฐ ํ ์คํธ
์๋น์ค ์์ปค๋ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์คํ๋๊ณ ๋ณต์กํ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ๋๋ฒ๊น ์ด ์ด๋ ค์ธ ์ ์์ต๋๋ค. ๋ค์์ ์๋น์ค ์์ปค๋ฅผ ๋๋ฒ๊น ํ๊ณ ํ ์คํธํ๊ธฐ ์ํ ๋ช ๊ฐ์ง ํ์ ๋๋ค:
- Chrome DevTools ์ฌ์ฉ: Chrome DevTools๋ ์๋น์ค ์์ปค๋ฅผ ๊ฒ์ฌํ๊ธฐ ์ํ ์ ์ฉ ์น์ ์ ์ ๊ณตํฉ๋๋ค. ์๋น์ค ์์ปค์ ์ํ, ๋ก๊ทธ, ์บ์ ์ ์ฅ์, ๋คํธ์ํฌ ์์ฒญ์ ๋ณผ ์ ์์ต๋๋ค.
- `console.log()` ๋ฌธ ์ฌ์ฉ: ์๋น์ค ์์ปค์ `console.log()` ๋ฌธ์ ์ถ๊ฐํ์ฌ ์คํ ํ๋ฆ์ ์ถ์ ํ๊ณ ์ ์ฌ์ ์ธ ๋ฌธ์ ๋ฅผ ์๋ณํฉ๋๋ค.
- `debugger` ๋ฌธ ์ฌ์ฉ: ์๋น์ค ์์ปค ์ฝ๋์ `debugger` ๋ฌธ์ ์ฝ์ ํ์ฌ ์คํ์ ์ผ์ ์ค์งํ๊ณ ํ์ฌ ์ํ๋ฅผ ๊ฒ์ฌํฉ๋๋ค.
- ๋ค์ํ ๊ธฐ๊ธฐ ๋ฐ ๋คํธ์ํฌ ์กฐ๊ฑด์์ ํ ์คํธ: ๋ค์ํ ๊ธฐ๊ธฐ ๋ฐ ๋คํธ์ํฌ ์กฐ๊ฑด์์ ์๋น์ค ์์ปค๋ฅผ ํ ์คํธํ์ฌ ๋ชจ๋ ์๋๋ฆฌ์ค์์ ์์๋๋ก ์๋ํ๋์ง ํ์ธํฉ๋๋ค. Chrome DevTools์ ๋คํธ์ํฌ ์ค๋กํ๋ง ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ๋ค์ํ ๋คํธ์ํฌ ์๋์ ์คํ๋ผ์ธ ์กฐ๊ฑด์ ์๋ฎฌ๋ ์ด์ ํฉ๋๋ค.
- ํ ์คํ ํ๋ ์์ํฌ ์ฌ์ฉ: Workbox์ ํ ์คํ ๋๊ตฌ๋ Jest์ ๊ฐ์ ํ ์คํ ํ๋ ์์ํฌ๋ฅผ ํ์ฉํ์ฌ ์๋น์ค ์์ปค์ ๋ํ ๋จ์ ๋ฐ ํตํฉ ํ ์คํธ๋ฅผ ์์ฑํฉ๋๋ค.
์ฑ๋ฅ ์ต์ ํ ํ
์๋น์ค ์์ปค์ ์ฑ๋ฅ์ ์ต์ ํํ๋ ๊ฒ์ ์ํํ๊ณ ๋ฐ์์ด ๋น ๋ฅธ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ๋ ๋ฐ ์ค์ํฉ๋๋ค.
- ์๋น์ค ์์ปค ์ฝ๋๋ฅผ ๊ฐ๋ณ๊ฒ ์ ์ง: ์๋น์ค ์์ปค์ ์ฝ๋ ์์ ์ต์ํํ์ฌ ์์ ์๊ฐ๊ณผ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ค์ ๋๋ค.
- ํจ์จ์ ์ธ ์บ์ฑ ์ ๋ต ์ฌ์ฉ: ์ฝํ ์ธ ์ ๊ฐ์ฅ ์ ํฉํ ์บ์ฑ ์ ๋ต์ ์ ํํ์ฌ ๋คํธ์ํฌ ์์ฒญ์ ์ต์ํํ๊ณ ์บ์ ํํธ์จ์ ๊ทน๋ํํฉ๋๋ค.
- ์บ์ ์ ์ฅ์ ์ต์ ํ: Cache API๋ฅผ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ์ฌ ๋ฆฌ์์ค๋ฅผ ๋น ๋ฅด๊ฒ ์ ์ฅํ๊ณ ๊ฒ์ํฉ๋๋ค. ์บ์์ ๋ถํ์ํ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ์ง ๋ง์ธ์.
- ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ฅผ ์ ์คํ๊ฒ ์ฌ์ฉ: ์ฌ์ฉ์ ๊ฒฝํ์ ์ํฅ์ ๋ฏธ์น์ง ์๋๋ก ์๊ฐ์ด ์ค์ํ์ง ์์ ์์ ์๋ง ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ์๋น์ค ์์ปค ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง: ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ์๋น์ค ์์ปค์ ์ฑ๋ฅ์ ์ถ์ ํ๊ณ ์ ์ฌ์ ์ธ ๋ณ๋ชฉ ํ์์ ์๋ณํฉ๋๋ค.
๋ณด์ ๊ณ ๋ ค ์ฌํญ
์๋น์ค ์์ปค๋ ๋์ ๊ถํ์ผ๋ก ์๋ํ๋ฉฐ ์์ ํ๊ฒ ๊ตฌํ๋์ง ์์ผ๋ฉด ์ ์ฉ๋ ์ ์์ต๋๋ค. ๋ค์์ ๋ช ์ฌํด์ผ ํ ๋ช ๊ฐ์ง ๋ณด์ ๊ณ ๋ ค ์ฌํญ์ ๋๋ค:
- HTTPS๋ฅผ ํตํด PWA ์ ๊ณต: ์๋น์ค ์์ปค๋ HTTPS๋ฅผ ํตํด ์ ๊ณต๋๋ ํ์ด์ง์์๋ง ๋ฑ๋กํ ์ ์์ต๋๋ค. ์ด๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ์๋น์ค ์์ปค ๊ฐ์ ํต์ ์ด ์ํธํ๋๋๋ก ๋ณด์ฅํฉ๋๋ค.
- ์ฌ์ฉ์ ์ ๋ ฅ ์ ํจ์ฑ ๊ฒ์ฌ: ๋ชจ๋ ์ฌ์ฉ์ ์ ๋ ฅ์ ๊ฒ์ฆํ์ฌ ์ฌ์ดํธ ๊ฐ ์คํฌ๋ฆฝํ (XSS) ๊ณต๊ฒฉ์ ๋ฐฉ์งํฉ๋๋ค.
- ๋ฐ์ดํฐ ์ด๊ท (Sanitize): ์ธ๋ถ ์์ค์์ ๊ฐ์ ธ์จ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์ด๊ท ํ์ฌ ์ฝ๋ ์ฃผ์ ๊ณต๊ฒฉ์ ๋ฐฉ์งํฉ๋๋ค.
- ์ฝํ ์ธ ๋ณด์ ์ ์ฑ (CSP) ์ฌ์ฉ: CSP๋ฅผ ์ฌ์ฉํ์ฌ PWA๊ฐ ๋ฆฌ์์ค๋ฅผ ๋ก๋ํ ์ ์๋ ์์ค๋ฅผ ์ ํํฉ๋๋ค.
- ์ ๊ธฐ์ ์ผ๋ก ์๋น์ค ์์ปค ์ ๋ฐ์ดํธ: ์๋น์ค ์์ปค๋ฅผ ์ต์ ๋ณด์ ํจ์น๋ก ์ต์ ์ํ๋ก ์ ์งํฉ๋๋ค.
๊ณ ๊ธ ์๋น์ค ์์ปค ๊ตฌํ์ ์ค์ ์ฌ๋ก
๋ช๋ช ํ์ฌ๋ค์ PWA์ ์ฑ๋ฅ๊ณผ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ๊ธฐ ์ํด ๊ณ ๊ธ ์๋น์ค ์์ปค ํจํด์ ์ฑ๊ณต์ ์ผ๋ก ๊ตฌํํ์ต๋๋ค. ๋ค์์ ๋ช ๊ฐ์ง ์์ ๋๋ค:
- Google Maps Go: Google Maps Go๋ ์ ์ฌ์ ๊ธฐ๊ธฐ์ ๋ถ์์ ํ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ์ํด ์ค๊ณ๋ Google ์ง๋์ ๊ฒฝ๋ ๋ฒ์ ์ ๋๋ค. ๊ณ ๊ธ ์บ์ฑ ์ ๋ต์ ์ฌ์ฉํ์ฌ ์ง๋์ ๊ธธ์ฐพ๊ธฐ์ ๋ํ ์คํ๋ผ์ธ ์ก์ธ์ค๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ฐ๊ฒฐ ์ํ๊ฐ ์ข์ง ์์ ์ง์ญ์ ์ฌ์ฉ์๋ ํจ๊ณผ์ ์ผ๋ก ๊ธธ์ ์ฐพ์ ์ ์์ต๋๋ค.
- Twitter Lite: Twitter Lite๋ ๋น ๋ฅด๊ณ ๋ฐ์ดํฐ ํจ์จ์ ์ธ ํธ์ํฐ ๊ฒฝํ์ ์ ๊ณตํ๋ PWA์ ๋๋ค. ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ๊ธฐ๊ฐ ์์ ์ ์ธ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ๊ฐ์ง ๋ ํธ์์ ์ ๋ก๋ํฉ๋๋ค. ์ด๋ฅผ ํตํด ๊ฐํ์ ์ธ ์ฐ๊ฒฐ์ด ์๋ ์ง์ญ์ ์ฌ์ฉ์๊ฐ ์ค๋จ ์์ด ํธ์ํฐ๋ฅผ ๊ณ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- Starbucks PWA: ์คํ๋ฒ ์ค์ PWA๋ ์ฌ์ฉ์๊ฐ ์คํ๋ผ์ธ์ผ ๋๋ ๋ฉ๋ด๋ฅผ ํ์ํ๊ณ , ์ฃผ๋ฌธํ๊ณ , ๊ฒฐ์ ํ ์ ์๊ฒ ํด์ค๋๋ค. ํธ์ ์๋ฆผ์ ์ฌ์ฉํ์ฌ ์ฃผ๋ฌธ์ด ํฝ์ ์ค๋น๊ฐ ๋์์ ๋ ์ฌ์ฉ์์๊ฒ ์๋ฆฝ๋๋ค. ์ด๋ ๊ณ ๊ฐ ๊ฒฝํ์ ๊ฐ์ ํ๊ณ ๊ณ ๊ฐ ์ฐธ์ฌ๋ฅผ ์ฆ๊ฐ์ํต๋๋ค.
๊ฒฐ๋ก : ๊ธ๋ก๋ฒ PWA ์ฑ๊ณต์ ์ํ ๊ณ ๊ธ ์๋น์ค ์์ปค ํจํด ์์ฉ
๊ณ ๊ธ ์๋น์ค ์์ปค ํจํด์ ๋ค์ํ ๊ธ๋ก๋ฒ ํ๊ฒฝ์์ ์ฑ๊ณตํ ์ ์๋ ๊ฒฌ๊ณ ํ๊ณ , ๋งค๋ ฅ์ ์ด๋ฉฐ, ์ฑ๋ฅ์ด ๋ฐ์ด๋ PWA๋ฅผ ๊ตฌ์ถํ๋ ๋ฐ ํ์์ ์ ๋๋ค. ์บ์ฑ ์ ๋ต, ๋ฐฑ๊ทธ๋ผ์ด๋ ๋๊ธฐํ, ํธ์ ์๋ฆผ, ์ฝํ ์ธ ์ ๋ฐ์ดํธ ๋ฉ์ปค๋์ฆ์ ๋ง์คํฐํจ์ผ๋ก์จ ๋คํธ์ํฌ ์ํ๋ ์์น์ ๊ด๊ณ์์ด ์ํํ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ๋ PWA๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ๊ตญ์ ํ์ ํ์งํ๋ฅผ ์ฐ์ ์ํจ์ผ๋ก์จ ์ ์ธ๊ณ ์ฌ์ฉ์๋ค์ด PWA์ ์ ๊ทผํ๊ณ ๊ด๋ จ์ฑ์ ๋๋ ์ ์๋๋ก ๋ณด์ฅํ ์ ์์ต๋๋ค. ์น์ด ๊ณ์ ๋ฐ์ ํจ์ ๋ฐ๋ผ ์๋น์ค ์์ปค๋ ์ต์์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ๋ ๋ฐ ์ ์ ๋ ์ค์ํ ์ญํ ์ ํ ๊ฒ์ ๋๋ค. ์ด๋ฌํ ๊ณ ๊ธ ํจํด์ ์์ฉํ์ฌ ์๋๋ฅผ ์์๊ฐ๊ณ ์ง์ ์ผ๋ก ๊ธ๋ก๋ฒํ ๋๋ฌ ๋ฒ์์ ์ํฅ๋ ฅ์ ๊ฐ์ง PWA๋ฅผ ๊ตฌ์ถํ์ญ์์ค. ๋จ์ํ PWA๋ฅผ ๋ง๋๋ ๋ฐ ๊ทธ์น์ง ๋ง๊ณ , *์ด๋์๋ * ์๋ํ๋ PWA๋ฅผ ๋ง๋์ญ์์ค.