넥스트 프로젝트에 pwa 적용하기
서론
최근 간단한 사이드 프로젝트를 하나를 일단락 하였다.현재 데스크톱, 태블릿, 모바일 모두 브라우저 사이즈마다 반응형을 고려해서 개발했다. 따라 기기마다의 편리성을 높였고, 범용성과 사용성을 더 고려하기위해 pwa를 도입했다.
pwa를 사용시, 모든 플랫폼에서 일관된 경험을 제공할 수 있고 성능적인 향상도 고려할 수 있었기 때문이다.
전제
- React (ver-18.2)
- Next.js (ver-14.2) - approuter
- next-pwa (ver-5.6)
Progressive Web App(PWA)란?
Progressive Web App(PWA)은 웹 기술(HTML, CSS, JavaScript)로 구축되었지만 네이티브 앱과 같은 사용자 경험을 제공하는 웹 애플리케이션이다. 이름에서 알 수 있듯이 "progressive"(점진적)이라는 개념이 핵심인데, 이는 사용자의 브라우저와 기기 능력에 따라 점진적으로 기능이 향상된다는 의미다.
PWA의 핵심 특징
- 설치 가능(Installable): 홈 화면이나 앱 서랍에 추가할 수 있어 앱처럼 실행
- 오프라인 작동(Offline functionality): 인터넷 연결이 없거나 불안정한 상황에서도 기본 기능을 사용
- 반응형(Responsive): 다양한 화면 크기와 기기에 맞게 조정
- 앱과 유사한 상호작용(App-like interactions): 앱처럼 부드러운 애니메이션과 탐색 경험을 제공
- 항상 최신 상태(Always up-to-date): 서비스 워커를 통해 자동으로 최신 버전으로 업데이트
- 안전(Secure): HTTPS를 통해 제공되어 보안이 유지.
- 발견 가능(Discoverable): 웹 애플리케이션 매니페스트와 서비스 워커 등록 덕분에 검색 엔진에서 앱으로 인식
- 재참여 유도(Re-engageable): 푸시 알림을 통해 사용자와 다시 연결
- 링크 공유 가능(Linkable): URL을 통해 쉽게 공유하고 설치 과정 없이 즉시 접근
PWA의 기술적 구성 요소
- 웹 앱 매니페스트(Web App Manifest): JSON 파일로, 앱의 이름, 아이콘, 시작 URL, 테마 색상 등 앱의 외관과 동작에 관한 정보를 담고 있다. 웹 애플리케이션이 사용자의 기기에 설치될 때 어떻게 보이고 동작해야 하는지에 대한 정보를 브라우저에 제공한다.(앱 이름, 앱 아이콘, 시작url, 표시 모드(전체화면 등), 테마 색상과 변경색상, 앱 방향)
- 서비스 워커(Service Worker): 백그라운드에서 실행되는 JavaScript 파일로, 오프라인 기능, 자원 캐싱, 푸시 알림 등을 가능
- HTTPS: 보안 연결을 제공하며, 이는 서비스 워커 사용을 위한 필수 요건
서비스 워커란?
PWA의 가장 핵심적인 기술 중 하나다. 이는 웹 브라우저가 백그라운드에서 실행하는 특별한 유형의 JavaScript 워커(worker)로, 웹 페이지와는 별도로 실행된다.
- 브라우저 백그라운드에서 실행: 웹 페이지와 별도의 스레드에서 실행되므로 메인 스레드를 차단하지 않는다.
- 네트워크 프록시 역할: 웹 애플리케이션과 네트워크 사이에 위치하여 요청을 가로채고 수정할 수 있다.
- 이벤트 기반 활성화: 특정 이벤트(설치, 활성화, 페이지 요청 등)에 반응하여 실행된다.
- 프로그래밍 가능한 캐시: 필요한 리소스를 캐시하고 관리하는 방법을 프로그래밍적으로 결정할 수 있다.
- 생명주기 독립성: 웹 페이지가 닫혀도 계속 실행될 수 있으며, 필요할 때 다시 시작된다.
- JavaScript DOM 접근 불가: 보안상의 이유로 DOM에 직접 접근할 수 없다.
넥스트 프로젝트 개발시, 서비스 워커 관련설정은 next-pwa 라이브러리로 쉽게 설정 가능
next-pwa 가이드
1. 설치 명령어
npm i next-pwa
2. Next.config 파일 수정
const withPWA = require('next-pwa')({
dest: 'public', // 서비스 워커 파일이 생성될 위치
disable: process.env.NODE_ENV === 'development', // 개발 환경에서는 pwa 기능 비활성화
register: true, // 서비스 워커 자동 등록
skipWaiting: true // 새 서비스워커가 즉시 활성화
});
/** @type {import('next').NextConfig} */
const nextConfig = {
// 기존 설정이 있다면 여기에 유지
};
module.exports = withPWA(nextConfig);
++) 개발환경에서 테스트 시, disable : false로 개발환경에서도 테스트 가능
그러나 넥스트 핫리로딩 기능과 서비스 워커 캐싱 충돌 할 수 있어 업데이트 코드 즉시 반영안되는 에러 발생가능성이 있다.
3. 웹 매니페스트 파일 생성하기.
public 폴더에 manifest.json 파일 생성
{
"name": "내 PWA 앱",
"short_name": "PWA앱",
"description": "Next.js로 만든 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"
},
{
"src": "/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/icons/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
}
]
}
아이콘은 192와 512 크기는 필수적으로 필요. (아래의 사이트에서 빠르게 변환 가능)
https://www.resizepixel.com/ko/resize-image/
ResizePixel - 온라인 이미지 편집기
온라인 무료 이미지 자르기, 크기 조절, 좌우반전, 회전, 변환, 압축 편집기
www.resizepixel.com
4. HTML 메타데이터 추가
/app/layout.tsx 최상단 레이아웃에 필요한 메타 태그 추가.
import type { Metadata, Viewport } from 'next';
export const metadata: Metadata = {
title: 'DEVING',
description: '개발자들만의 다양한 모임을 즐겨요!',
icons: {
icon: '/logo.svg',
},
manifest: '/manifest.json',
applicationName: 'Deving-together',
appleWebApp: {
capable: true,
statusBarStyle: 'default',
title: 'Deving',
},
};
export const viewport: Viewport = {
themeColor: '#000000',
width: 'device-width',
initialScale: 1,
maximumScale: 1
};
그리고 apple 기기를 위한 추가 메타 태그
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ko" className={pretendard.variable}>
<head>
{/* Apple 기기를 위한 추가 메타 태그 */}
<link rel="apple-touch-icon" href="/icons/logo_192.png" />
</head>
<body className="bg-BG">
//...
</body>
</html>
);
}
여기까지만 하면 기본적인 pwa를위한 셋팅은 끝났다.
그렇다면 주소창 옆에
다음과 같은아이콘이 보일 것이고,
(++ pwa 다운로드 아이콘찾기)
Chrome 또는 Edge 브라우저 사용 시:
- 브라우저의 주소 표시줄 오른쪽 끝에 설치 아이콘이 표시됩니다. 이 아이콘은 작은 컴퓨터 모니터와 화살표 형태(📥)로 나타납니다.
- 이 아이콘을 클릭하면 "설치" 버튼이 있는 작은 프롬프트가 나타납니다.
- "설치" 버튼을 클릭하면 앱이 데스크톱에 설치됩니다.
- 설치 후에는 시작 메뉴나 바탕화면에서 앱을 실행할 수 있습니다.
설치 아이콘이 보이지 않는 경우:
- 브라우저 주소 표시줄 오른쪽의 메뉴(⋮)를 클릭합니다.
- 메뉴에서 "설치..." 또는 "앱으로 설치..." 옵션을 찾아 클릭합니다.
Firefox 브라우저 사용 시:
Firefox는 PWA 설치를 위한 네이티브 인터페이스를 제공하지 않지만, 아래 방법으로 유사한 경험을 제공합니다:
- 주소 표시줄 오른쪽의 메뉴 버튼(≡)을 클릭합니다.
- "사이트 정보" 또는 "페이지 정보"를 클릭합니다.
- "바탕화면에 추가" 옵션을 선택합니다.
모바일 기기에서 설치하기
Android (Chrome 브라우저):
- 사이트를 방문한 후, 브라우저 메뉴(⋮)를 탭합니다.
- "앱 설치" 또는 "홈 화면에 추가" 옵션을 선택합니다.
- 표시되는 프롬프트에서 "설치" 버튼을 탭합니다.
- 설치 후에는 홈 화면이나 앱 서랍에서 앱을 찾을 수 있습니다.
iOS (Safari 브라우저):
- 사이트를 방문한 후, 공유 버튼(□↑)을 탭합니다.
- 스크롤하여 "홈 화면에 추가" 옵션을 찾아 탭합니다.
- 앱 이름을 확인하고 "추가" 버튼을 탭합니다.
- 설치 후에는 홈 화면에서 앱 아이콘을 찾을 수 있습니다.
설치를 진행하면 바탕화면에 앱아이콘이 추가된다.이제 네이티브 앱과 유사한 경험을 웹에서도 사용자에게 제공할 수 있게된다.
이제 다음은, 이 설치를 사용자에게 안내하고 더욱 접근성을 높이는 커스텀 프롬프트를 만들 차례이다.
프롬프트 템플릿
'use client';
import { useEffect, useState } from 'react';
export default function InstallPWA() {
const [supportsPWA, setSupportsPWA] = useState(false);
const [promptInstall, setPromptInstall] = useState<Event | null>(null);
useEffect(() => {
const handler = (e: Event) => {
e.preventDefault();
setSupportsPWA(true);
setPromptInstall(e);
};
window.addEventListener('beforeinstallprompt', handler);
return () => window.removeEventListener('beforeinstallprompt', handler);
}, []);
const onClick = (evt: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
evt.preventDefault();
if (!promptInstall) {
return;
}
// 설치 프롬프트 표시
(promptInstall as any).prompt();
};
if (!supportsPWA) {
return null;
}
return (
<button
className="install-button"
onClick={onClick}
>
앱 설치하기
</button>
);
}
필자는 프로젝트 루트에, 앱 설치 버튼을 추가 및 배너를 사용하여 사용자의 클릭을 유도해 라우트를 옮겨, 다운로드 페이지에서 직접 설치할 수 있게 진행하였다.
해당 페이지에선, 사용자의 브라우저를 감지하여 적절한 설치 안내를 제공하면 된다.
이제 남은 것은 설치 여부와 사용 패턴을 추적하는 시스템을 통해 앱 개선을 위한 데이터 축적만 남은 단계인 것 같다.(google Analytics)