Chrome App 형태로 install 할 수 있는 나의 프로젝트 !
PWA는 웹과 네이티브 앱의 기능 모두의 이점을 갖도록 수 많은 특정 기술과 표준 패턴을 사용해 개발된 웹 앱입니다.
예를 들어, 웹 앱은 발견이 쉽습니다 — 어플리케이션을 설치하는 것보다 웹사이트에 방문하는 것이 훨씬 쉽고 빠르며, 링크로 웹 앱을 공유할수도 있습니다.
반면에, 네이티브 앱은 운영체제와 보다 잘 통합되므로 더 부드러운 사용자 경험을 제공할 수 있습니다. 네이티브 앱은 설치할 수 있으므로 오프라인에서 동작하며, 사용자는 홈 화면의 아이콘을 탭하여 브라우저를 사용하여 이동하는 것보다 선호하는 앱에 더 쉽게 접근할 수 있습니다.
PWA는 이들과 동일한 이점을 즐길 수 있는 웹 앱을 생성하는 능력을 제공합니다.
[출처 - MDN]
이렇게 모바일 어플의 형태로도 쉽게 제작을 하기위해 구글에서 고안이 된 새로운 형태의 App이라고 할 수 있다. PWA는 많은 장점을 가지고 있는데 대표적으로
Service Workers 를 사용한 캐싱 덕분에 앱을 설치한 후에 로딩 시간이 줄어들어 소중한 데이터와 시간을 절약.
앱 업데이트가 있을 때 변경된 컨텐츠만 업데이트 할 수 있음. 반면, 네이티브 앱의 경우, 아주 작은 수정에도 사용자가 어플리케이션 전체를 다시 다운로드하도록 강제함.
네이티브 플랫폼에 보다 통합된 외관과 느낌 — 홈 화면의 앱 아이콘, 전체 화면으로 실행되는 앱, 등.
시스템 알림 및 푸시 메시지를 통한 사용자의 재 참여. 참여율이 높은 사용자와 더 나은 전환율을 이끌어 냄.
가장 와닿는 장점은 적용하기 무척 쉽다는 점 !
이런식으로 어플화 시켜 사용자의 접근성을 높혀준다.
PWA를 적용하고 효과를 나타내기 위해선 몇가지를 주의해 주어야 하는데,
HTTPS를 운영해야 해야하고(Localhost도 가능하다.), manifest.json 와 Service-worker를 적용해야 한다. 나 같은 경우에는 firebase 배포를 통해 HTTPS조건을 만들어 주었다. (firebase는 FCM이라는 클라우드 메세징 기능이 있어 PWA앱 형태에서 푸시 기능을 쉽게 구현이 가능하여 PWA와 궁합이 좋은거 같다. 둘다 구글이 만들어서 그런가)
manifest.json 파일은 json 포맷 파일로서, 모든 웹 익스텐션이 포함하고 있어야 하는 파일이다. 익스텐션에는 여러가지가 있겠지만 예를들자면 이름, 초기주소, 아이콘 정보, 타입정보 등이 있다. 코드를 보면서 살펴보자
{
"name": "9bin first PWA",
"short_name": "9BIN",
"icons": [
{
"src": "/maskable_icon_x72.png",
"type": "image/png",
"sizes": "72x72"
},
{
"src": "/maskable_icon_x96.png",
"type": "image/png",
"sizes": "96x96"
},
{
"src": "/maskable_icon_x128.png",
"type": "image/png",
"sizes": "128x128"
},
{
"src": "/maskable_icon_x144.png",
"type": "image/png",
"sizes": "144x144"
},
{
"src": "/maskable_icon_x152.png",
"type": "image/png",
"sizes": "152x152"
},
{
"src": "/maskable_icon_x512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": "/",
"display": "standalone",
"background_color": "#FFFFFF",
"theme_color": "#f2f2f2"
}
형식은 JSON이라 익숙한 구조인데 새로보는 속성들이 있다.
name
: icon에 표시되는 이름,
short_name
: Web Application 이름의 짧은 버전. 공간이 충분하지 않아 full name 이 나올 수 없을 때 사용된다.
icons
: 아이콘를 표시하는 이미지로 src, type, size등의 정보가 필요하다. 앱은 특정 크기의 아이콘 사이즈가 필요한데 이것 또한 Maskable.app라는 구글에서 제공하는 아이콘 메이커가 있어서 간편하게 제작이 가능하다.
start_url
: 실행시에 시작되는 URL 주소
display
: 앱이 어떤식으로 실행될지 정하는 속성 (옵션 : fullscreen, minimul-ui, standalone, browser)
background_color
,theme_color
등등 상세한 파일정보를 제공할 수 있다.
이후 link태그로 연결 시켜 주면 된다.
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
이때 crossorigin태그를 통해 사파리에서도 작동이 가능하게 해준다.
성공적으로 manifest를 연결하면 개발자 도구의 application 탭에 연결된 manifest정보가 나타난다.
서비스 워커를 적용할 때 가장 어려웠다.
service-worker를 도입하게 된 계기는 푸시 알람 기능을 적용해 보고 싶었고 캐싱을 통해 오프라인에서도 작동하는 App을 만들고 싶었다.
Service Worker는 브라우저와 네트워크 사이의 가상 프록시입니다. 이는 프론트엔드 개발자들이 수년간 어려움을 겪었던 문제들을 결국 해결하였습니다(특히 웹 사이트의 자원을 적절히 캐싱하는 방법과, 사용자의 기기가 오프라인일 때 이를 사용할 수 있도록 하는 것 등).
Service Worker는 "단지" 오프라인 기능을 제공하는 것 이상으로 알림 처리, 독립된 스레드에서의 복잡한 계산 등 많은 것들을 할 수 있습니다. Service worker는 네트워크 요청을 제어하고 수정하며, 캐시로부터 반환된 커스텀 응답을 제공하거나 응답을 완전히 가공할 수도 있으므로 매우 강력합니다.
const CACHE_NAME = "version-1";
const urlsToCache = [
"index.html",
"favicon.ico",
];
self.addEventListener("install", function(event) { //캐싱기능
event.waitUntil(
caches.open(CACHE_NAME).then(function(cache) {
console.log("Opened cache");
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener("fetch", function(event) { //업데이트기능
event.respondWith(
caches.match(event.request).then(function(response) {
if (response) return response;
return fetch(event.request);
})
);
});
self.addEventListener("activate", function(event) {
const cacheWhitelist = [];
cacheWhitelist.push(CACHE_NAME);
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (!cacheWhitelist.includes(cacheName)) {
return caches.delete(cacheName);
}
})
);
})
);
});
self.addEventListener("push", event => { //푸시기능
const title = "공지사항";
const options = {
body: event.data.text(),
icon: "/maskable_icon_x152.png"
};
event.waitUntil(registration.showNotification(title, options));
});
SW파일을 만들어 주고, index의 script 안에서 불러와 주어야 한다.
<script>
if ("serviceWorker" in navigator) {
console.log("Service Worker and Push is supported");
window.addEventListener("load", function() {
navigator.serviceWorker
.register("/sw.js")
.then(function(registration) {
registration => {
registration.pushManager.subscribe({
userVisibleOnly: true
//applicationServerKey: 원래는 줘야함...
});
Notification.requestPermission(); //푸시 허용할지 창 띄움
console.log(
"ServiceWorker registration successful with scope: ",
registration
);
},
function(err) {
console.log("ServiceWorker registration failed: ", err);
};
});
});
}
</script>
SW를 성공적으로 적용했다면 개발자도구 application 탭에 serviceworker가 activated상태로 변하게 된다.
Service-worker를 적용하게 되면 오프라인 상태에서도 캐싱이 된 정보는 작동이 가능해지고 firebase FCM키를 이용하여 웹 푸쉬 알람 기능까지 적용이 가능해 진다.
근사해졌다.
이번 프로젝트를 통해 빠르게 변화하고 발전하고 있는 웹앱 형태를 몸으로 느껴볼 수 있었다. PWA를 이용하여 많은 공수가 필요 없이 앱의 형태로 만들 수 있다는 사실에 놀라웠다.
비록 디바이스의 고유한 특징들(모바일기기 내장 기능)을 필요로 하는 앱의 경우에는 네이티브나 앱 개발을 통해 만들겠지만 빠르게 진화하는 앱이 언젠가는 모두 구현이 가능한 형태로 발전할 것 같다.