어쩌다 내가 PWA를 맡게 되었을까?
<웨이브드>는 모바일 기반 웹 서비스. 기획 의도 상 사용자가 모바일 디바이스를 기반으로 서비스를 접근하는 것이 더 적절하다고 판단했고, 화면 역시 모바일 디바이스를 기준으로 제작하고 있었다. 그러다 보니 디자이너분들 눈에 특히 아쉬운 것이다. "앱으로는 못할까요?" 죄송합니다 제가 웹 개발자예요..
그래도 찾아보니 PWA라는 기술이 있는데 이걸 도입하면 웹을 앱처럼 사용하는 것이 가능하다는 내용을 확인. 문제는 PWA를 사용해 본 인원은 고사하고 뭔 지조 차도 다들 잘 모른다는 사실. 솔직히 처음 들었다.
이 당시 우리는 MVP 모델 완성이라는 목표만으로도 굉장히 쥐어 짜이고 있었기 때문에 "일단은 우선순위를 뒤로 미루고 우리 여유 생기면 도입해요 🥺"라는 마법의 문장으로 일단락했다.
그리고 와버렸다.
리팩토링 스프린트.
리팩토링이라는 목표에 걸맞게 이전에 하지 못한 작업들 위주로 작업을 하게 됐는데 여기에 PWA가 빠질 수 없었다. 서비스가 만들어내고 직접 테스트해보다 보니 앱처럼 동작할 수 있으면 분명 베스트가 될 것임을 모두가 완전히 실감해 버렸기 때문. 나 역시 도입을 긍정적으로 생각하면서 재밌겠다 생각했다.
그렇다고 혼자 맡을 생각은 아니었는데...😨 대왕부담
지금부터는 Next.js 프로젝트에 어떻게 PWA를 도입했는가에 대한 이야기를 써내려 갈 것이다.
PWA(Progressive Web App)
PWA란 웹 애플리케이션의 일종으로 직역하자면 점진적인 웹 애플리케이션(?)
정리된 표현으로는 웹 기술로 만드는 애플리케이션을 의미한다.
HTML, CSS, JavaScript와 같은 웹 기술을 사용해서 개발하지만 네이티브 애플리케이션처럼 보이게 한다.
(* Native Application: 특정 운영체제나 플랫폼에서 직접 실행되도록 개발한 소프트웨어 애플리케이션. 흔히 말하는 앱)
디바이스 브라우저의 "홈 화면에 추가" 기능이 앱 설치와 같은 역할을 한다. 이렇게 설치된 앱을 이용하면 바로 서비스에 진입할 수 있다. 그리고 브라우저 창과 같은 요소를 생략하기 때문에 조금 더 넓은 화면을 이용할 수 있다. 진짜 앱 같은 모습..!
PWA는 의외로 이미 많은 서비스에서 도입되고 있는 기술이며 대표적인 예시로 인프런이 있다.
(1) Next.js에 도입하기 위해서는? next-pwa
<웨이브드>는 Next.js 13 버전을 사용하고 있다.
그리고 다행히 Next.js 프로젝트에 PWA를 쉽게 도입할 수 있도록 하는 라이브러리 next-pwa를 발견할 수 있었다.
(* next-pwa와 ducanh-next-pwa는 서로 다른 라이브러리임을 참고)
프로젝트에 next-pwa를 설치한다.
$ npm install next-pwa
(2) next.config.mjs를 환경에 맞게 수정하기
기존에 작성되어 있던 next.js의 설정인 const nextConfig = { } 부분을 중심으로 PWA를 적용하기 위한 설정을 한다.
// next.config.mjs
import withPWA from 'next-pwa';
const isProd = process.env.NODE_ENV === 'production'; // 배포 버전에만 PWA 활성화
const pwaConfig = withPWA({
dest: 'public',
disable: !isProd,
runtimeCaching: [],
});
/** @type {import('next').NextConfig} */
const nextConfig = { /* ... */ }
export default pwaConfig(nextConfig);
pwaConfig로 PWA 옵션을 지정하는데 여기에서 나는 배포 버전에만 PWA를 활성화하기 위한 조건을 적용했다. 그리고 API 응답을 가로채는 service worker로 인해 API 호출 에러가 발생하는 문제가 있었는데, 캐싱을 빈배열([])로 초기화해 줌으로써 해결했다.
마지막으로 export 할 때 nextConfig를 pwaConfig로 감싸는 형식으로 옵션을 적용한다.
+) 나의 경우는 위 파일을 수정하는 과정에서 eslint 경고가 발생하여 해당 파일에서 린팅을 하지 않도록 해제해 줬다
// .eslintrc.json
{
"ignorePatterns": ["jest.*.js", "next.config.mjs"],
}
(3) manifest.json 생성하기
PWA에서 manifest 파일은 핵심 구성 요소이다. 애플리케이션이 사용자의 홈 화면에 설치될 때, 또는 브라우저에서 실행될 때 앱의 외관과 동작 방식을 정의해 줌으로써 단순한 웹 페이지가 아닌 네이티브 앱처럼 동작할 수 있게 한다. 또한 이 파일을 통해 사용자들이 일관되는 설치 및 실행 경험을 얻을 수 있다.
manifest 파일의 주요 용도를 몇 개 정리하자면 다음과 같다.
- 앱 아이콘 및 이름 정의
- 디스플레이 설정
- 시작 URL 지정
- 테마 및 배경색 설정
- 메타데이터 제공
이런 manifest를 쉽게 작성할 수 있도록 돕는 웹 서비스가 있어 활용했다.
https://simicart.com/manifest-generator.html/
+) 실제 프로젝트를 진행할 때에는 위 서비스를 이용했으나 현재 종료되어, 대신에 아래 서비스를 이용하면 될 듯하다.
https://progressier.com/pwa-manifest-generator
그리고 위를 통해 만들어진 파일을 manifest.json로 명명하고 프로젝트의 public 폴더에 저장한다.
// manifest.json
{
"name": "웨이브드 (WAVED)", // 설치 배너에 표시되는 대표 이름, 검색 키워드
"short_name": "WAVED", // 앱 이름
"description": "WAVED와 함께 챌린지의 파도를 넘어 취업으로",
"theme_color": "#2470FF", // URL바, 시스템바에 설정할 색상
"background_color": "#FFFFFF", // 갤럭시 스플래시 스크린에 사용되는 색상
"display": "standalone", // standalone = 상단 URL바를 제거한 확장 형태
"orientation": "portrait", // portrait = 세로모드
"scope": "/",
"start_url": "/?app=pwa", // PWA 실행 최초 경로. query string 적용하여 통계에 이용 가능
"application_scope": "/", // PWA를 적용할 범위
}
아이콘 생성하기
manifest.json에 작성해야 하는 요소 중 위에서 작성하지 않은 것이 하나 있는데 바로 icons 다. 홈 화면에 설치되면서 대표로 보일 아이콘 이미지로, 각 디바이스 환경에 맞춰서 다양한 크기로 사용되기에 손상 없이 깔끔한 아이콘을 보여주기 위한 다양한 사이즈의 아이콘 이미지 파일을 필요로 한다.
이 역시 손쉽게 대신 만들어주는 웹 서비스가 있어 활용했다.
https://www.pwabuilder.com/imageGenerator
아이콘 이미지를 하나 제출하면 사이즈 별로 이미지들을 생성해 주는 서비스이다.
나의 경우는 512x512 사이즈의 PNG 파일 하나를 제출해서 다수의 이미지를 얻어냈고, 이 역시 public 폴더에 저장했다.
icons까지 적용하고 나서 manifest.json 작성을 마쳤다.
// manifest.json
{
"name": "웨이브드 (WAVED)", // 설치 배너에 표시되는 대표 이름, 검색 키워드
"short_name": "WAVED", // 앱 이름
"description": "WAVED와 함께 챌린지의 파도를 넘어 취업으로",
"theme_color": "#2470FF", // URL바, 시스템바에 설정할 색상
"background_color": "#FFFFFF", // 갤럭시 스플래시 스크린에 사용되는 색상
"display": "standalone", // standalone = 상단 URL바를 제거한 확장 형태
"orientation": "portrait", // portrait = 세로모드
"scope": "/",
"start_url": "/?app=pwa", // PWA 실행 최초 경로. query string 적용하여 통계에 이용 가능
"application_scope": "/", // PWA를 적용할 범위
"icons": [
{
"src": "/app-assets/logo/48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "/app-assets/logo/72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "/app-assets/logo/96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "/app-assets/logo/128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "/app-assets/logo/144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "/app-assets/logo/192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/app-assets/logo/512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}
(4) Splash Screen 적용하기
스플래쉬란? 사용자가 앱을 실행할 때, 앱을 로드하는 동안 잠깐 동안 표시되는 화면을 의미한다. 초기 실행 전용 로딩스피너라고 생각하면 된다. 네이티브 앱에서는 일반적으로 이때 서비스의 이름, 로고, 슬로건 등을 노출하는데 PWA에서도 동일하다.
다만 PWA에는 약간의 특이점이 존재하는데, Android 운영체제에서는 스플래쉬 스크린이 자동으로 구성되는 반면 iOS 운영체제에서는 직접 설정해줘야 한다. 즉, 별도의 설정을 해주지 않으면 갤럭시에서는 스플래쉬 스크린이 뜨고 아이폰에서는 뜨지 않는다. 🙃
개인적인 견해인데 자동으로 만들어주는 쪽이 iOS일 줄 알았다. 무슨 편견이 있는 걸까
Android에서 자동으로 만들어지는 스플래쉬는 manifest.json을 참고한다.
background_color를 전체 배경색으로 적용하고 192x192 사이즈의 앱 아이콘을 화면 중앙에 노출, 아이콘의 하단에 short_name이 노출된다. 어쩔 수 없이 슬로건을 노출한다거나 하는 자체적인 옵션 지정이 불가능하다.
iOS를 위해서는 노출할 스플래쉬 이미지를 레포지토리 안에 따로 저장하고 지정해줘야 하며 아래와 같은 웹 서비스를 활용을 권장한다.
https://progressier.com/pwa-icons-and-ios-splash-screen-generator
위를 통해 만들어진 이미지들도 마저 public 폴더 안에 저장하고, 해당 이미지들을 불러오는 코드는 head 요소 안에서 작성해 주면 된다.
<!-- src/pages/_document.tsx -->
<link
rel="apple-touch-startup-image"
media="screen and (device-width: 430px) and (device-height: 932px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
href="/app-assets/splashscreens/iPhone_15_Pro_Max__iPhone_15_Plus__iPhone_14_Pro_Max_portrait.png"
/>
<link
rel="apple-touch-startup-image"
media="screen and (device-width: 393px) and (device-height: 852px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
href="/app-assets/splashscreens/iPhone_15_Pro__iPhone_15__iPhone_14_Pro_portrait.png"
/>
<link
rel="apple-touch-startup-image"
media="screen and (device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
href="/app-assets/splashscreens/iPhone_14_Plus__iPhone_13_Pro_Max__iPhone_12_Pro_Max_portrait.png"
/>
<link
rel="apple-touch-startup-image"
media="screen and (device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
href="/app-assets/splashscreens/iPhone_14__iPhone_13_Pro__iPhone_13__iPhone_12_Pro__iPhone_12_portrait.png"
/>
<link
rel="apple-touch-startup-image"
media="screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
href="/app-assets/splashscreens/iPhone_13_mini__iPhone_12_mini__iPhone_11_Pro__iPhone_XS__iPhone_X_portrait.png"
/>
<link
rel="apple-touch-startup-image"
media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
href="/app-assets/splashscreens/iPhone_11_Pro_Max__iPhone_XS_Max_portrait.png"
/>
<link
rel="apple-touch-startup-image"
media="screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
href="/app-assets/splashscreens/iPhone_11__iPhone_XR_portrait.png"
/>
<link
rel="apple-touch-startup-image"
media="screen and (device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)"
href="/app-assets/splashscreens/iPhone_8_Plus__iPhone_7_Plus__iPhone_6s_Plus__iPhone_6_Plus_portrait.png"
/>
<link
rel="apple-touch-startup-image"
media="screen and (device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)"
href="/app-assets/splashscreens/iPhone_8__iPhone_7__iPhone_6s__iPhone_6__4.7__iPhone_SE_portrait.png"
/>
PWA가 적용된 앱 확인하기
앞선 과정을 통해 PWA 적용이 완료됐다. 확인해 보자!
먼저 브라우저에서 서비스에 접속한 뒤 "홈 화면에 추가"를 통해 설치 역할을 대신해 준다. 특정 조건에서는 설치를 권장하는 듯한 표시가 노출되어 조금 더 쉽게 추가를 진행할 수 있다. 그다음 화면에 추가된 진입 경로를 통해 PWA가 적용된 버전으로 서비스를 이용이 가능하다.
가장 크게 보이는 차이인 화면을 가져왔다.
PWA를 적용함으로써 위아래에 위치하던 브라우저 도구창이 사라져 시원하게 넓어진 화면으로 이용할 수 있다! 👍
그리고 핸드폰에서 바로 실행시킬 수 있다는 점이 정말 큰 장점!
그 외에도 푸시 알림 등 다양한 기능을 접목할 수 도 있으니 기회가 될 때 더 다양한 기능을 활용해보고 싶어진다
작업했던 전체 내용은 아래 pull-request를 참고하면 볼 수 있다 🌊
참고
'프로젝트 > WAVED' 카테고리의 다른 글
[프로젝트][WAVED] 지표 확인을 위해 구글 애널리틱스(GA) 도입하기 (0) | 2024.05.07 |
---|---|
[프로젝트][WAVED] 가로 스크롤 컴포넌트 구현을 위한 삽질기 (0) | 2024.04.05 |
[프로젝트][WAVED] Next.js를 선택했다고 끝난게 아니다? 라우팅 방식과 렌더링 방식에 대한 고민들 (0) | 2024.03.05 |