Next.js 및 Nuxt.js에서 PWA와 오프라인 기능을 활용한 웹 애플리케이션 구축
Min-jun Kim
Dev Intern · Leapcell

소개: 예상을 뛰어넘는 웹 경험 향상
오늘날 빠르게 변화하는 디지털 환경에서 사용자는 단순한 기능적 웹사이트 이상의 것을 기대합니다. 네트워크 상태에 관계없이 원활하고 성능이 뛰어나며 안정적인 경험을 요구합니다. 기존의 웹 애플리케이션은 종종 이 부분에서 한계를 보이며, 사용자들을 "인터넷 연결 없음" 메시지나 불안정한 네트워크에서의 느린 성능에 좌절하게 만듭니다. 바로 여기서 점진적 웹 앱(PWA)이 등장합니다. PWA는 오프라인 액세스, 푸시 알림, 앱과 같은 설치 기능과 같은 기능을 제공함으로써 네이티브 애플리케이션과 웹 브라우저 간의 격차를 해소합니다. Next.js 및 Nuxt.js와 같은 최신 JavaScript 프레임워크를 활용하는 개발자에게 PWA 기능 통합은 단순한 개선이 아니라 진정으로 뛰어난 사용자 경험을 제공하기 위한 중요한 단계입니다. 이 글에서는 Next.js 및 Nuxt.js 애플리케이션에 완전한 PWA 및 오프라인 지원을 추가하는 과정을 안내하여, 이들을 강력하고 복원력 있으며 매력적인 플랫폼으로 변화시킬 것입니다.
핵심 개념 및 구현 전략
구체적인 내용을 살펴보기 전에, 견고하고 복원력 있는 웹 애플리케이션 구축의 핵심인 PWA 및 오프라인 기능의 토대를 이루는 주요 개념에 대한 기초적인 이해를 갖추도록 합시다.
주요 용어 이해
- 점진적 웹 앱 (PWA): PWA는 최신 웹 기능을 활용하여 사용자에게 앱과 같은 경험을 제공하는 웹 애플리케이션의 한 유형입니다. PWA는 안정적이며(즉시 로드되고 불확실한 네트워크 조건에서도 "가동 중단"을 절대 표시하지 않음), 빠르며(매끄러운 애니메이션으로 사용자 상호 작용에 빠르게 응답함) 매력적입니다(기기에서 자연스러운 앱처럼 느껴지며 몰입감 있는 사용자 경험을 제공합니다).
- 서비스 워커 (Service Worker): 서비스 워커는 메인 브라우저 스레드와 분리되어 백그라운드에서 실행되는 JavaScript 파일로, 프로그래밍 가능한 네트워크 프록시 역할을 합니다. 애플리케이션의 네트워크 요청을 가로채고, 리소스를 캐시하며, 푸시 알림을 처리하여 오프라인 기능 및 기타 PWA 기능을 가능하게 합니다.
- 웹 앱 매니페스트 (Web App Manifest): 브라우저에 애플리케이션(이름, 작성자, 아이콘, 설명 등)에 대한 정보를 제공하는 JSON 파일입니다. 매니페스트는 "홈 화면에 추가" 기능에 필수적이며 설치 시 PWA가 사용자에게 어떻게 표시되는지를 결정합니다.
- 캐싱 전략 (Caching Strategies): 서비스 워커가 에셋을 저장하고 검색하는 데 사용하는 다양한 접근 방식입니다. 일반적인 전략은 다음과 같습니다.
- Cache-First: 가능한 경우 캐시된 콘텐츠를 제공하고, 그렇지 않으면 네트워크로 이동합니다.
- Network-First: 네트워크에서 가져오려고 시도하고, 실패하면 캐시로 대체합니다.
- Stale-While-Revalidate: 즉시 캐시된 콘텐츠를 제공하는 동시에 네트워크에서 최신 버전을 가져와 향후 요청을 위해 캐시를 업데이트합니다.
- Cache Only: 캐시에서만 콘텐츠를 제공합니다.
- Network Only: 항상 네트워크로 이동하고 캐시를 사용하지 않습니다.
Next.js에서 PWA 구현
Next.js는 주로 next-pwa
패키지를 통해 PWA 통합을 위한 간소화된 접근 방식을 제공합니다.
먼저 패키지를 설치하세요:
npm install next-pwa # 또는 yarn add next-pwa
다음으로 next.config.js
를 구성하세요:
// next.config.js const withPWA = require('next-pwa')({ dest: 'public', register: true, skipWaiting: true, disable: process.env.NODE_ENV === 'development', // 개발 환경에서 PWA 비활성화 }); module.exports = withPWA({ // 귀하의 Next.js 구성 });
이 구성은 next-pwa
에게 서비스 워커를 public
디렉토리에 출력하고, 자동으로 등록하며, 즉각적인 업데이트를 위해 skipWaiting
을 활성화하도록 지시합니다. 또한 활발한 개발 중에 캐싱 문제를 피하기 위해 개발 중 PWA를 비활성화하는 것이 좋습니다.
마지막으로 public
디렉토리에 manifest.json
파일을 만드세요:
// public/manifest.json { "name": "My Next.js PWA App", "short_name": "Next PWA", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#000000", "icons": [ { "src": "/icons/icon-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icons/icon-512x512.png", "sizes": "512x512", "type": "image/png" } ] }
_document.js
(또는 선호한다면 _app.js
)에서 매니페스트를 연결하세요:
// pages/_document.js import Document, { Html, Head, Main, NextScript } from 'next/document'; class MyDocument extends Document { render() { return ( <Html lang="en"> <Head> <link rel="manifest" href="/manifest.json" /> <link rel="apple-touch-icon" href="/icons/icon-192x192.png"></link> <meta name="theme-color" content="#317EFB" /> </Head> <body> <Main /> <NextScript /> </body> </Html> ); } } export default MyDocument;
이러한 단계를 통해 Next.js 애플리케이션은 서비스 워커를 생성하고 등록하여 기본 오프라인 캐싱 및 PWA 기능을 가능하게 합니다.
Nuxt.js에서 PWA 구현
Nuxt.js는 내장된 PWA 모듈을 제공하여 통합을 더욱 간단하게 만듭니다.
먼저 @nuxtjs/pwa
모듈이 설치되었는지 확인하세요:
npm install @nuxtjs/pwa # 또는 yarn add @nuxtjs/pwa
다음으로 nuxt.config.js
파일에서 모듈을 구성하세요:
// nuxt.config.js export default { modules: [ '@nuxtjs/pwa', ], pwa: { meta: { title: 'My Nuxt PWA App', author: 'Me', theme_color: '#42b883', }, manifest: { name: 'My Nuxt PWA App', short_name: 'Nuxt PWA', lang: 'en', display: 'standalone', background_color: '#ffffff', theme_color: '#42b883', start_url: '/', icons: [ { src: '/icons/icon-192x192.png', sizes: '192x192', type: 'image/png' }, { src: '/icons/icon-512x512.png', sizes: '512x512', type: 'image/png' } ] }, workbox: { runtimeCaching: [ { urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i, handler: 'CacheFirst', method: 'GET', strategyOptions: { cacheName: 'google-fonts-cache', cacheableResponse: { statuses: [0, 200] }, }, }, { urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/i, handler: 'CacheFirst', method: 'GET', strategyOptions: { cacheName: 'images-cache', cacheableResponse: { statuses: [0, 200] }, }, }, { urlPattern: '/', // 오프라인 액세스를 위해 루트 경로 캐시 handler: 'NetworkFirst', options: { cacheName: 'html-cache', expiration: { maxAgeSeconds: 60 * 60 * 24 * 7, // 7일 }, cacheableResponse: { statuses: [0, 200] } } } ], // 기타 workbox 옵션 } }, // 기타 Nuxt.js 구성 };
@nuxtjs/pwa
모듈은 매니페스트를 자동으로 생성하고 서비스 워커를 등록합니다. workbox
속성을 사용하면 Workbox를 사용하여 각별한 PWA 기능을 지원하는 정교한 캐싱 전략을 정의할 수 있습니다. 이 예제에서는 CacheFirst
전략으로 Google 글꼴과 이미지를 캐시하고, NetworkFirst
전략으로 루트 HTML을 캐시하여 오프라인 가용성과 온라인 시 최신 콘텐츠를 보장합니다.
고급 오프라인 지원 및 캐싱 전략
Next.js와 Nuxt.js 모두에서 고급 오프라인 지원은 서비스 워커의 캐싱 전략을 조정하는 데 달려 있습니다. Workbox 라이브러리( next-pwa
및 @nuxtjs/pwa
에서 내부적으로 사용됨)는 강력한 기능을 제공합니다.
/api/data
API 엔드포인트가 동적 콘텐츠를 제공하는 시나리오를 고려해 보세요. 사용자가 온라인일 때 가장 최신 정보를 볼 수 있도록 NetworkFirst
전략을 구현하고, 인터넷이 없을 때는 이전 데이터를 액세스할 수 있도록 하려면 다음과 같이 할 수 있습니다.
Next.js ( workbox.config.js
또는 사용자 지정 시 service-worker.js
를 통해):
next-pwa
의 경우 next.config.js
의 runtimeCaching
이 제공하는 것보다 더 세밀한 제어가 필요한 경우, service-worker.js
를 수정하거나 직접 생성하여 Workbox를 직접 사용할 수 있습니다. 그러나 대부분의 경우 next.config.js
의 runtimeCaching
은 동적 경로에 충분합니다.
// next.config.js const withPWA = require('next-pwa')({ dest: 'public', register: true, skipWaiting: true, disable: process.env.NODE_ENV === 'development', runtimeCaching: [ // ... 기존 캐시 { urlPattern: /^https:\/\/your-api\.com\/api\/data/i, handler: 'NetworkFirst', options: { cacheName: 'api-data-cache', expiration: { maxEntries: 10, // 최대 10개의 별도 API 응답 캐시 maxAgeSeconds: 60 * 5, // 5분 동안 캐시 }, cacheableResponse: { statuses: [0, 200], }, }, }, ], }); module.exports = withPWA({ /* ... */ });
Nuxt.js ( nuxt.config.js
):
Nuxt.js의 PWA 모듈은 Workbox의 runtimeCaching
을 쉽게 지원합니다.
// nuxt.config.js export default { pwa: { workbox: { runtimeCaching: [ // ... 기존 캐시 { urlPattern: /^https:\/\/your-api\.com\/api\/data/i, handler: 'NetworkFirst', options: { cacheName: 'api-data-cache', expiration: { maxEntries: 10, maxAgeSeconds: 60 * 5, }, cacheableResponse: { statuses: [0, 200], }, }, }, ], }, }, };
이 예제들은 NetworkFirst
캐싱 전략을 사용하여 API에서 동적 콘텐츠를 캐싱하는 방법을 보여줍니다. 이는 비교적 최신 상태여야 하지만 오프라인에서도 사용할 수 있어야 하는 데이터에 이상적입니다. 마찬가지로 스타일시트, 스크립트 또는 애플리케이션 내의 특정 경로와 같은 다른 에셋에 대한 패턴을 정의할 수 있습니다.
애플리케이션 시나리오
- 콘텐츠가 많은 블로그 또는 뉴스 사이트: 기사 및 이미지를 오프라인 읽기를 위해 캐시합니다.
- 전자 상거래 제품 목록: 사용자가 인터넷 없이도 제품을 탐색할 수 있도록 하고, 이전에 본 항목을 표시합니다.
- 작업 관리 앱: 사용자가 오프라인에서 작업을 보고 추가할 수 있도록 하고, 연결이 복원되면 동기화합니다.
- 정적 마케팅 페이지: 네트워크 조건에 관계없이 즉각적인 로딩 및 가용성을 보장합니다.
결론: 복원력 있고 사용자 중심적인 웹 애플리케이션 구축
Next.js 및 Nuxt.js 애플리케이션에 완전한 PWA 및 오프라인 지원을 통합하는 것은 더 이상 선택 사항이 아니라 복원력 있고 고성능이며 진정으로 사용자 중심적인 웹 경험을 구축하기 위한 중요한 단계입니다. 서비스 워커, 웹 앱 매니페스트 및 정교한 캐싱 전략을 활용하면 애플리케이션이 네이티브 앱과 같은 안정성과 속도를 제공하도록 지원할 수 있습니다. PWA를 채택하면 웹 애플리케이션이 네트워크 조건에 관계없이 완벽하게 작동하고 매력적으로 유지될 수 있으며, 궁극적으로 사용자 만족도와 유지율을 높일 수 있습니다.