PWA는 웹 앱과 네이티브 앱의 장점을 모두 제공하는 진보된 형태의 웹 애플리케이션 개발이라고 볼 수 있다.
먼저 웹 앱의 장점은 뛰어난 접근성이라고 볼 수 있다. 앱을 설치하는 것보다 웹사이트에 방문하는 것이 훨씬 쉽고 빠르며, 브라우저가 설치된 어떠한 장치에서도 접근 가능하다. 또한, 새로운 컨텐츠를 빠르게 배포할 수 있고, 링크로 손쉽게 공유할 수도 있다. 모바일 사용자의 수가 나날이 늘어가고 있는 가운데 무신사의 경우 웹 배포를 중단하고 모바일에 집중하겠다고 했다. 그만큼 모바일 사용자의 수가 늘어나고 있음을 알 수 있다.
그래서 웹 앱의 장점을 알 수 있는 PWA 적용해보도록 하자!
설치하기
1. next-pwa 설치하기
npm install next-pwa
설치하다가 중간에 설치도 안되고 오류만 나길래 별 쌩쇼를 다했는데 .next와 node_modules폴더를 삭제하고 다시 npm install과 위의 명령어를 치니까 잘 되었다. 만약에 하다가 안되면 위의 폴더를 삭제해서 설치하는 것을 추천한다.
2. next.config.js 파일 수정하기
const withPlugins = require("next-compose-plugins");
const withPWA = require('next-pwa')({
dest: 'public',
register: true,
skipWaiting: true,
});
const nextConfig = {
...
};
export default withPWA(nextConfig);
위의 파일때문에 고생했는데 dest:’public’만 있으면 pwa 구현할 때 잘 되는 것 같다.
3. public 폴더에 manifest.json 파일 생성해주기
브라우저가 이 웹은 PWA라는 것을 알려주기 위해서 아래와 같은 manifest를 작성한다.
PWA Manifest Generator | SimiCart
위의 사이트를 참고하여 manifest를 작성하였다.
- Name : PWA의 이름
- Short name : 앱 이름을 표시할 공간이 충분하지 않을 때 나타나는 이름
- Display : homescreen icon을 통해 실행될 때 app 화면이 보여지는 형태
- browser : 일반 브라우저와 동일하게 보입니다.
- standalone : 다른 앱들처럼 최상단에 상태표시줄을 제외한 전체화면으로 보입니다.
- fullscreen : 상태표시줄도 제외한 전체화면으로 보여줍니다.(ex. 게임)
- minimul-ui : fullscreen과 비슷하지만 뒤로가기, 새로고침등 최소한의 영역만 제공합니다.(모바일 크롬 전용)
- Description : app의 description 정보
- Application Scope(=scope) : app 내에서 PWA 기능을 적용할 범위
- Start url : app이 최초 실행 될 때 load되는 file의 경로
- Background color : 스플래시 배경색
- Theme color : 전체적인 테마색
그러고 나면 generate manifest를 눌러 manifest와 icon.png가 사이즈별로 생긴다. manifest의 확장자가 webmanifest로 되어 있기 때문에 이를 json형식으로 변경해준다.
manifest.json이 이 형식으로 되어 있다.
{
"name": "앱 이름",
"short_name": "앱 이름",
"icons": [
{
"src": "/아이콘-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/아이콘-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/아이콘-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#FFFFFF",
"background_color": "#FFFFFF",
"start_url": "/",
"display": "standalone",
"orientation": "portrait"
}
나중에 pwa로 변경하고 나서 휴대폰 배경화면이나 pc에 설치할 것을 대비하여 아이콘 사이즈별로 다양하게 넣어주면 된다.
4. 메타 태그 적용하기
위에서 작성한 manifest를 적용해주기 위해서 파일을 만든다. 내 경우에 src/app/layout.tsx에 manifest를 넣어주고자 하였다. 여기서 애를 많이 먹었는데, _document.tsx파일을 만들어서 적용하라는 말이 대부분이었기 때문이다.
💡 _document.tsx파일을 만들었는데 적용이 안된다면 layout.tsx파일을 만들어서 적용하는 것을 추천한다.
아래는 layout.tsx파일이다.
import type { Metadata } from "next";
export const metadata: Metadata = {
// manifest: "/manifest.json",
title: "유희왕 무물",
description: "유희왕에게 뭐든지 물어보세요!",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<head>
<link rel="manifest" href="/manifest.json" />
</head>
<body className={inter.className}>{children}</body>
</html>
);
}
manifest를 적용하는 방법은 두 가지가 존재한다.
💡 기본적으로 link 태그는 <head></head>사이에 넣어줘야 적용이 된다.
1) link 태그로 적용하기
<link rel="manifest" href="/manifest.json" />
2) metadata에 넣기
export const metadata: Metadata = {
manifest: "/manifest.json",
title: "유희왕 무물",
description: "유희왕에게 뭐든지 물어보세요!",
};
위에서 보이는 것처럼 metadata에 manifest를 집어넣어서 적용하면 된다. 둘 다 적용해봤는데 둘 다 되므로 본인이 편한 것으로 적용하면 된다.
3) IOS 스플래시 메타태그 만들기
스플래시 메타태그는 1~2초동안의 로딩시간에 유저의 불쾌함을 줄이면서 앱 브랜딩하기 위하여 집어넣는 이미지이다.
Online Splash Screen Generator - Apprene.com
위의 사이트는 스플래시 이미지를 만들어주는 사이트이다. 내 경우 디자인적 요소가 많이 떨어지는 인간이므로 내 대표 이미지를 가운데에 넣어주고 생성해주었다.
위의 사이트는 파비콘, 안드로이드, 애플 등에 맞는 이미지를 생성해주는 사이트이다.
위의 사이트들을 이용하여 이미지를 만들어준다.
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/iphone5_splash.png"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
></link>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/iphone6_splash.png"
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
></link>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/iphoneplus_splash.png"
media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
></link>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/iphonex_splash.png"
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
></link>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/ipad_splash.png"
media="(min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)"
></link>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/ipadpro1_splash.png"
media="(min-device-width: 834px) and (max-device-width: 834px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)"
></link>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/ipadpro2_splash.png"
media="(min-device-width: 1024px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)"
></link>
만든 스플래시 이미지를 layout.tsx에 집어넣어 준다.
5. icon png 수정하기
아까 위에서 icon.png를 manifest에 넣어주었는데, 이를 수정하고자 한다. 위에서 스플래시 이미지를 만들어주는 곳에 만들어진 이미지를 manifest.json에 넣어준다.
다 적용된 코드는 아래와 같다.
manifest.json
{
"theme_color": "#f69435",
"background_color": "#f69435",
"display": "fullscreen",
"scope": "/",
"start_url": "/",
"name": "유희왕무물",
"short_name": "유희왕무물",
"description": "유희왕에게 뭐든지 물어보세요!",
"icons": [
{
"src": "/images/icons/apple-icon-57x57.png",
"sizes": "57x57",
"type": "image/png"
},
{
"src": "/images/icons/apple-icon-60x60.png",
"sizes": "60x60",
"type": "image/png"
},
{
"src": "/images/icons/apple-icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/images/icons/apple-icon-76x76.png",
"sizes": "76x76",
"type": "image/png"
},
{
"src": "/images/icons/apple-icon-114x114.png",
"sizes": "114x114",
"type": "image/png"
},
{
"src": "/images/icons/apple-icon-120x120.png",
"sizes": "120x120",
"type": "image/png"
},
{
"src": "/images/icons/apple-icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/images/icons/apple-icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "/images/icons/andriod-icon-36x36.png",
"sizes": "36x36",
"type": "image/png"
},
{
"src": "/images/icons/andriod-icon-48x48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/images/icons/andriod-icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/images/icons/andriod-icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},{
"src": "/images/icons/andriod-icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/images/icons/andriod-icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}
]
}
내 경우에 pc에만 아이콘이 적용되고, 아이폰에 적용이 안되어서 이미지를 사이즈별로 여러개 넣어주고자 하였다.
layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
// manifest: "/manifest.json",
title: "유희왕 무물",
description: "유희왕에게 뭐든지 물어보세요!",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<head>
<link rel="manifest" href="/manifest.json" />
<link
href="/images/favicons/favicon-16x16.png"
rel="icon"
type="image/png"
sizes="16x16"
/>
<link
href="/images/favicons/favicon-32x32.png"
rel="icon"
type="image/png"
sizes="32x32"
/>
<link rel="apple-touch-icon" href="/images/icons/icon-192x192.png" />
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/iphone5_splash.png"
media="(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/iphone6_splash.png"
media="(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/iphoneplus_splash.png"
media="(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/iphonex_splash.png"
media="(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/ipad_splash.png"
media="(min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/ipadpro1_splash.png"
media="(min-device-width: 834px) and (max-device-width: 834px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<link
rel="apple-touch-startup-image"
href="/images/splashscreens/ipadpro2_splash.png"
media="(min-device-width: 1024px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 2) and (orientation: portrait)"
/>
<meta name="msapplication-TileColor" content="#FF98BA" />
</head>
<body className={inter.className}>{children}</body>
</html>
);
}
6. 빌드 및 실행하기
npm run build && npm run dev
위의 명령어를 사용하여 빌드를 하여 사이트의 코드와 이미지를 압축시켜준다. 그리고 이를 실행시킨다.
왼쪽 사진은 pwa가 적용되기 전이고, 오른쪽 사진은 적용된 사진이다. 그래서 오른쪽 사진의 맨 왼쪽 아이콘을 보면 앱을 다운로드를 할 수 있다.
위와 같이 앱으로 실행된다.
위의 사진은 pc에서 다운했을 때의 모습이다.
왼쪽 사진은 폰에서 홈 화면 추가시에 오른쪽 사진같이 나온다.
장점
모바일 기기에서 사용하는 웹 앱과는 달리 네이티브 앱과 유사한 기능을 제공할 수 있다. PWA는 앱 다운로드와 업데이트없이 바로 웹 브라우저를 통하여 앱을 사용가능하다.
또한, 오프라인에서도 사용이 가능하다. 이는 모바일 기기의 네트워크가 불안할 경우 웹 페이지가 로딩되는 동안 오프라인에서 캐시된 데이터를 사용할 수 있고, 나중에 네트워크 연결이 되면 새로운 데이터를 불러와 업데이트를 할 수 있다.
PWA는 네이티브 앱과 비슷하게 푸시알림과 카메라, 마이크 등의 모바일 기기 자체의 기능도 사용할 수 있다.
느낀점
내 입장에서는 배워야 할 언어도 많고, 웹을 한 사람이라면 앱도 배우고 싶은 언어 중 하나라고 생각한다. 그렇다고 앱에 들어가기전에 PWA로 앱 찍먹하고 개발해보면 좋을 듯하다라고 생각한다.
👇🏻 참고 사이트
https://velog.io/@yiseungyun/광卫-ㅇr님-누구㈏-㉠ㅏ능-Next.js-프로젝트를-5분만에-앱으로
https://velog.io/@ghenmaru/Next-PWA-웹앱-만들기
https://yozm.wishket.com/magazine/detail/1969/
https://brunch.co.kr/@kangsigner/1
https://velog.io/@sangpok/PWA-아이콘과-스플래쉬-화면w.-webmanifest