PWA 프로젝트 셋업하기
패키지 설치
yarn add next-pwa
yarn add -D webpack
next.config.mjs 설정
import withPWAInit from "next-pwa";
const withPWA = withPWAInit({
dest: "public",
});
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default withPWA(nextConfig);
public/manifest.json 설정
{
"name": "My Next.js PWA",
"short_name": "NextPWA",
"description": "My awesome Next.js PWA!",
"icons": [
{
"src": "/test_icon.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "/test_icon.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": "/",
"background_color": "#ffffff",
"theme_color": "#000000",
"display": "standalone"
}
- 아이콘은 사이즈별로 준비해야 함. 위 예제처럼 하면 안 됨. (안 될 건 없지만 의도한 대로 작동 안 할 수 있음.)
- 더불어 public 폴더에 png 아이콘을 사이즈별로 준비한다.
- 이 외에도 다양한 환경에 대응하고 싶다면 객체를 추가해서 사이즈 별로 대응 가능.
app/layout.tsx 설정
import Footer from "@/components/public/Footer";
import Header from "@/components/public/Header";
import type { Metadata, Viewport } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
// 이 부분
export const viewport: Viewport = {
themeColor: "black",
width: "device-width",
initialScale: 1,
maximumScale: 1,
userScalable: false,
viewportFit: "cover",
};
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
manifest: "/manifest.json",
icons: {
icon: "/test_icon.png",
shortcut: "/test_icon.png",
apple: "/test_icon.png",
other: {
rel: "apple-touch-icon-precomposed",
url: "/test_icon.png",
},
},
};
// 이 부분
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<Header />
{children}
<Footer />
</body>
</html>
);
}
이곳의 메타데이터 타이틀은 앱 이름임.
public/sw.js
import { clientsClaim } from 'workbox-core';
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { NetworkFirst, CacheFirst } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
clientsClaim();
// self.__WB_MANIFEST is injected by workbox-build during the build process
precacheAndRoute(self.__WB_MANIFEST || []);
// Cache CSS, JS, and web worker requests with a network-first strategy.
registerRoute(
({ request }) => request.destination === 'style' || request.destination === 'script' || request.destination === 'worker',
new NetworkFirst({
cacheName: 'static-resources',
})
);
// Cache image files with a cache-first strategy.
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
new ExpirationPlugin({
maxEntries: 50,
}),
],
})
);
// Cache API calls with a network-first strategy.
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new NetworkFirst({
cacheName: 'api',
networkTimeoutSeconds: 10,
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200],
}),
],
})
);
// Cache the start URL with a network-first strategy.
registerRoute(
'/',
new NetworkFirst({
cacheName: 'start-url',
plugins: [
{
cacheWillUpdate: async ({ request, response }) => {
if (response && response.type === 'opaqueredirect') {
return new Response(response.body, {
status: 200,
statusText: 'OK',
headers: response.headers,
});
}
return response;
},
},
],
})
);
// Cache everything else with a network-only strategy.
registerRoute(
({ request }) => true,
new CacheFirst({
cacheName: 'catch-all',
})
);
이 파일은 자동으로 셋업 되지만, 빌드 시 인코딩 되어 다음부터 개발 서버를 돌리면 제대로 동작하지 않을 수 있음. 그럴 때는 이 파일을 계속 스니펫처럼 복-붙해야 할 수 있으니 올려 둠.
(필요 시) PWA 접속 유무 점검하는 유틸 함수 작성
// utils/pwa/isPWA.ts (자유)
export const isPWA = (): boolean => {
return (
window.matchMedia("(display-mode: standalone)").matches ||
(window.navigator as any).standalone === true
);
};
(필요 시) PWA 접속 유무 점검하는 커스텀 훅 작성
// hooks/useCheckPwa.ts (자유)
import { useEffect, useState } from 'react';
const useCheckPwa = (): boolean => {
const [isPwa, setIsPwa] = useState(false);
useEffect(() => {
const checkPwa = (): boolean => {
return window.matchMedia('(display-mode: standalone)').matches || (window.navigator as any).standalone === true;
};
setIsPwa(checkPwa());
}, []);
return isPwa;
};
export default useCheckPwa;
- 이렇게 만들어진 훅을 import해서 상태의 값에 따라 추가 로직을 작성하면 됨.
- 단 한계는 useEffect 훅으로 작성되었기 때문에 화면이 그려지고 나서 로직이 작동하기에 화면에서 한 번 깜빡임이 있을 수 있음. 보완이 필요한 추가 로직임.
'Programing > Next.js' 카테고리의 다른 글
Next.js 프로젝트 생성 (0) | 2024.07.17 |
---|---|
Next.js를 쓰는 이유가 무엇인가? (0) | 2024.07.17 |
App Routes 만드는 기본 방법 (0) | 2024.07.05 |
App Router란 무엇인가? (0) | 2024.07.05 |
Next.js의 라우팅에서 사용되는 필수 용어 정리 (0) | 2024.07.04 |
댓글