프로그레시브 웹 앱을 최적화하는 방법: 기본을 넘어서

게시 됨: 2022-03-10
빠른 요약 ↬ 프로그레시브 웹 애플리케이션은 사용자 참여를 높이고 비용을 효과적으로 줄이는 것으로 입증되었습니다. 최신 PWA를 구축하려면 사용자의 기대에 부응하기 위한 핵심 설정 이상의 것이 필요합니다. 따라서 오프라인 기능에서 사용자 친화적인 권한 요청에 이르기까지 PWA에 최신 특성을 추가하는 방법을 직접 살펴보겠습니다.

프로그레시브 웹 애플리케이션(PWA)은 2020년에도 여전히 인기를 얻고 있습니다. 더 높은 전환율, 고객 참여, 감소된 페이지 로딩 속도, 개발 및 오버헤드 비용 절감의 이점을 고려할 때 이는 놀라운 일이 아닙니다.

Twitter, Uber, Tinder, Pinterest, Forbes와 같은 PWA로 성공을 누리고 있는 존경받는 기업도 볼 수 있습니다. 그리고 그들은 모두 프로그레시브 앱을 구현함으로써 얻을 수 있는 엄청난 이점을 자랑합니다.

좋은 소식은 PWA 개발이 예산이 큰 회사만 감당할 수 있는 일이 아니라는 것입니다. 이러한 응용 프로그램은 중소기업과 일반 기업에 동등하게 서비스를 제공하며 만들기가 그렇게 복잡하지 않습니다.

Smashing Magazine에서 PWA의 핵심 구축에 중점을 둔 포괄적인 Beginner's Guide To Progressive Web Apps를 찾을 수 있습니다.

그러나 한 단계 더 나아가 오프라인 기능, 네트워크 기반 최적화, 교차 장치 사용자 경험, SEO 기능, 비침해 알림 및 요청과 같은 최신 품질을 PWA에 배포하는 방법을 알아보겠습니다. 또한 PWA에 이러한 팁을 구현할 수 있도록 보다 구체적인 가이드에 대한 예제 코드 또는 참조를 찾을 수 있습니다.

프로그레시브 웹 애플리케이션(PWA)에 대한 간략한 개요

기본 사항을 건너뛰지 말고 PWA의 핵심을 빠르게 살펴보겠습니다.

PWA 란 무엇입니까?

"프로그레시브 웹 앱(PWA)은 최신 API로 구축 및 향상되어 단일 코드베이스로 누구에게나 어디서나 모든 장치에 도달하면서 향상된 기능, 안정성 및 설치 가능성을 제공합니다."

— Google 개발자

즉, PWA는 사용자가 독립 실행형 응용 프로그램으로 사용할 수 있는 웹 사이트입니다. PWA는 설치가 필요하지 않고 다양한 장치와 함께 사용할 수 있다는 점에서 주로 기본 앱과 다릅니다. 기본 앱은 주로 모바일 장치용으로 제작되었습니다.

PWA는 어떻게 작동합니까?

PWA의 핵심은 웹 앱 매니페스트, 서비스 워커 및 애플리케이션 셸의 세 가지 구성 요소로 구성됩니다. 위에서 언급한 초보자 가이드에서 빌드에 대한 자세한 지침을 찾을 수 있습니다.

다음은 이러한 구성 요소가 하는 일입니다.

웹 앱 매니페스트

웹 앱 매니페스트는 웹 사이트를 전체 화면 모드에서 독립 실행형 애플리케이션으로 실행하기 위한 핵심입니다. PWA의 모양을 정의하고 다양한 장치에 맞게 최적화하고 응용 프로그램 설치 후 표시되는 아이콘을 할당할 수 있습니다.

서비스 워커

서비스 워커는 캐시된 데이터를 가져오거나 사용자에게 인터넷 연결 부재에 대해 알려줌으로써 PWA의 오프라인 사용을 가능하게 합니다. 서비스 워커는 서버 연결이 복원되면 최신 데이터도 검색합니다.

애플리케이션 셸 아키텍처

응용 프로그램 셸은 사용자가 PWA에 액세스할 때 표시되는 것입니다. 사용자 인터페이스를 구동하는 데 필요한 최소한의 HTML, CSS 및 JavaScript입니다. PWA를 개발할 때 브라우저에서 애플리케이션 셸의 리소스와 자산을 캐시할 수 있습니다.

점프 후 더! 아래에서 계속 읽기 ↓

PWA에 최신 특성 배포

핵심 기능 외에도 최신 PWA는 사용자를 더욱 특별한 사용자 경험으로 이끄는 추가 특성을 수용합니다.

PWA의 몇 가지 특정 최신 특성을 살펴보고 이를 PWA에 추가하는 방법에 대해 알아보겠습니다. 다음 특성은 Google 개발자가 기본 PWA에 추가한 훌륭한 기능으로 간주됩니다.

응용 프로그램은 온라인과 마찬가지로 오프라인에서도 작동합니다.

PWA를 구축할 때 핵심의 일부로 맞춤형 오프라인 페이지를 개발할 수도 있습니다. 그러나 연결이 필요한 특정 지점까지 인터넷 연결 없이도 PWA가 계속 작동하면 훨씬 더 사용자 친화적입니다. 그렇지 않으면 사용자 경험은 Ankita Masand가 PWA의 문제점에 대한 기사에서 설명한 것처럼 케이크를 주문하는 시련만큼 실망스러울 수 있습니다.

캐시된 콘텐츠, 백그라운드 동기화 및 스켈레톤 화면을 사용하여 보다 중요한 사용자 경험을 얻을 수 있습니다. 하나하나 살펴보겠습니다.

IndexedDB 로 캐시된 콘텐츠

IndexedDB 는 PWA가 오프라인으로 작동하도록 하는 데 필요한 데이터를 캐시하고 검색하는 데 사용할 수 있는 브라우저 내 NoSQL 스토리지 시스템입니다.

그러나 모든 브라우저가 IndexedDB 를 지원하는 것은 아니므로 가장 먼저 해야 할 일은 사용자의 브라우저가 IndexedDB를 지원하는지 확인하는 것입니다.

 if (!('indexedDB' in window)) { console.log('This browser doesn\'t support IndexedDB'); return; }

그런 다음 IndexedDB API를 사용하여 캐시된 콘텐츠를 만들 수 있습니다. 다음은 데이터베이스를 열고 개체 저장소를 추가하고 이 저장소에 항목을 추가하는 Google 개발자의 예입니다.

 var db; var openRequest = indexedDB.open('test_db', 1); openRequest.onupgradeneeded = function(e) { var db = e.target.result; console.log('running onupgradeneeded'); if (!db.objectStoreNames.contains('store')) { var storeOS = db.createObjectStore('store', {keyPath: 'name'}); } }; openRequest.onsuccess = function(e) { console.log('running onsuccess'); db = e.target.result; addItem(); }; openRequest.onerror = function(e) { console.log('onerror!'); console.dir(e); }; function addItem() { var transaction = db.transaction(['store'], 'readwrite'); var store = transaction.objectStore('store'); var item = { name: 'banana', price: '$2.99', description: 'It is a purple banana!', created: new Date().getTime() }; var request = store.add(item); request.onerror = function(e) { console.log('Error', e.target.error.name); }; request.onsuccess = function(e) { console.log('Woot! Did it'); }; }

백그라운드 동기화

PWA가 백그라운드에서 데이터를 동기화하는 경우 사용자는 오프라인 상태에서 작업을 수행할 수 있으며, 이는 인터넷 연결이 복원될 때 실행됩니다. 간단한 예가 메시징 앱입니다. 사용자는 오프라인일 때 메시지를 보낼 때까지 기다릴 필요 없이 메시지를 보낼 수 있습니다. 연결이 복원되면 백그라운드 동기화가 자동으로 메시지를 보냅니다.

다음은 Jake Archibald의 백그라운드 동기화 기능을 개발하는 방법의 예입니다.

 // Register your service worker: navigator.serviceWorker.register('/sw.js'); // Then later, request a one-off sync: navigator.serviceWorker.ready.then(function(swRegistration) { return swRegistration.sync.register('myFirstSync'); });

그런 다음 /sw.js 에서 이벤트를 수신합니다.

 self.addEventListener('sync', function(event) { if (event.tag == 'myFirstSync') { event.waitUntil(doSomeStuff()); } });

스켈레톤 스크린

스켈레톤 스크린을 사용하는 주요 이점 중 하나는 사용자가 유휴 상태에 있는 것이 아니라 작동하는 애플리케이션을 인식한다는 것입니다. 사용자가 연결되어 있지 않은 동안 골격 화면은 콘텐츠 없이 인터페이스를 그립니다. 그런 다음 연결이 복원되면 채워집니다.

Code My UI에는 PWA용 스켈레톤 화면을 만드는 데 사용할 수 있는 몇 가지 훌륭한 코드 조각이 있습니다.

Code My UI의 스켈레톤 화면 예
Code My UI의 스켈레톤 화면 예. (큰 미리보기)

네트워크 사용량 기반 최적화

PWA의 핵심 이점은 사용자에게 더 빠른 경험을 제공한다는 것입니다. PWA가 캐시 우선 네트워킹을 사용하고, 리소스의 우선 순위를 지정하고, 네트워크 품질을 기반으로 적응형 로딩을 사용하도록 하여 로딩 속도를 더욱 최적화할 수 있습니다.

이를 PWA로 개발하는 방법을 살펴보겠습니다.

먼저 캐시한 다음 네트워크

캐시된 콘텐츠를 먼저 사용하면 PWA가 오프라인으로 작동할 수 있고 사용자가 네트워크 범위가 낮은 지역에서도 콘텐츠에 액세스할 수 있습니다. 콘텐츠를 캐시할 서비스 워커를 만든 다음 가져와서 그렇게 할 수 있습니다.

다음은 서비스 워커를 사용한 정적 HTML 캐싱에 대한 Jeff Posnick의 예입니다.

 self.addEventListener('fetch', event => { if (event.request.mode === 'navigate') { // See /web/fundamentals/getting-started/primers/async-functions // for an async/await primer. event.respondWith(async function() { // Optional: Normalize the incoming URL by removing query parameters. // Instead of https://example.com/page?key=value, // use https://example.com/page when reading and writing to the cache. // For static HTML documents, it's unlikely your query parameters will // affect the HTML returned. But if you do use query parameters that // uniquely determine your HTML, modify this code to retain them. const normalizedUrl = new URL(event.request.url); normalizedUrl.search = ''; // Create promises for both the network response, // and a copy of the response that can be used in the cache. const fetchResponseP = fetch(normalizedUrl); const fetchResponseCloneP = fetchResponseP.then(r => r.clone()); // event.waitUntil() ensures that the service worker is kept alive // long enough to complete the cache update. event.waitUntil(async function() { const cache = await caches.open('my-cache-name'); await cache.put(normalizedUrl, await fetchResponseCloneP); }()); // Prefer the cached response, falling back to the fetch response. return (await caches.match(normalizedUrl)) || fetchResponseP; }()); } });

리소스 우선 순위 지정

기본적으로 PWA는 라이트 특성으로 인해 유사한 기본 앱보다 성능이 더 뛰어납니다. 또한, PWA는 브라우저의 캐시를 사용하기 때문에 어떤 리소스가 우선적으로 사용되고 사용되기 전에 렌더링되어야 하는지 나타낼 수 있습니다. 동적 콘텐츠를 가져오기 전에 업데이트해야 하므로 주로 정적 요소에서 작동합니다.

HTML에서 <link> 문자열을 사용하여 요소의 우선 순위를 지정할 수 있습니다. rel=”preconnect”rel=”dns-prefetch.”

Maximiliano Firtman은 브라우저 엔진 내에서 웹 글꼴의 우선 순위를 지정하여 이에 대한 간단한 예를 제공합니다.

 <link rel=”preload” as=”font” href=”font.woff” crossorigin>

적응 로딩 구현

Wi-Fi 및 4G의 인터넷 속도는 어디에서나 액세스할 수 없으며 사용자는 여전히 2G 및 3G 연결을 통해 인터넷에 접속합니다. 가능한 한 많은 사람들이 PWA에 액세스할 수 있기를 원하므로 더 낮은 인터넷 속도에서도 수행하도록 최적화할 수 있습니다.

사용자의 연결 유형에 따라 PWA 요소를 로드하는 적응형 로딩을 구현하여 이를 달성할 수 있습니다.

Google의 적응형 로딩 일러스트레이션.
Google의 적응형 로딩 일러스트레이션. (큰 미리보기)

가장 간단한 방법은 캐싱 전략을 위한 수많은 기성 플러그인이 포함된 Google의 Workbox 도구를 사용하는 것입니다.

사용자 지정 캐싱 전략을 정의한다고 가정합니다. 다음은 Demian Renzulli와 Jeff Posnick의 예시로 수행할 수 있는 방법입니다.

 const adaptiveLoadingPlugin = { requestWillFetch: async ({request}) => { const urlParts = request.url.split('/'); let imageQuality; switch ( navigator && navigator.connection ? navigator.connection.effectiveType : '' ) { //... case '3g': imageQuality = 'q_30'; break; //... } const newUrl = urlParts .splice(urlParts.length - 1, 0, imageQuality) .join('/') .replace('.jpg', '.png'); const newRequest = new Request(newUrl.href, {headers: request.headers}); return newRequest; }, };

다음으로 이미지 URL(예: /img/ )과 일치하는 정규 표현식이 포함된 cacheFirst 전략에 플러그인을 전달합니다.

 workbox.routing.registerRoute( new RegExp('/img/'), workbox.strategies.cacheFirst({ cacheName: 'images', plugins: [ adaptiveLoadingPlugin, workbox.expiration.Plugin({ maxEntries: 50, purgeOnQuotaError: true, }), ], }), );

모든 플랫폼에서 탁월한 사용자 경험

우수한 PWA는 브라우저, 모바일 및 태블릿에서 원활하게 작동합니다. Android 기기를 사용하는 것이 인터넷에 액세스하는 가장 인기 있는 방법(시장 점유율 38.9%)이지만 모든 플랫폼에 대해 애플리케이션을 최적화하는 것은 PWA 핵심 기능 개발의 일부입니다.

PWA가 로드될 때 점프를 줄이고 PWA가 모든 입력 방법에서 작동하는지 확인하는 등 사용성을 높이고 뛰어난 사용자 경험을 제공하기 위한 추가 조치를 취할 수 있습니다.

각 측면에 접근하는 방법은 다음과 같습니다.

"점프" 콘텐츠 로딩 줄이기

고속 인터넷을 사용하더라도 사이트의 요소가 순서대로 로드되기 때문에 로드하는 동안 사이트의 콘텐츠가 이동할 수 있습니다. 이 효과는 연결 속도가 느릴수록 더욱 악화되며 사용자 경험을 크게 해칩니다.

로드하는 동안 콘텐츠를 이동시키는 가장 일반적인 요소는 일반적으로 이미지가 더 크고 콘텐츠를 로드할 때 우선 순위가 아니기 때문에 이미지입니다. HTML 구조가 렌더링된 후 우선 순위를 지정할 수 있는 더 작은 자리 표시자 이미지를 사용하여 "지연 로딩"으로 이 문제를 해결할 수 있습니다.

다음은 JavaScript에서 실제 이미지보다 먼저 로드되는 경량 이미지를 추가하는 방법에 대한 Mozilla 개발자의 예입니다.

 <img src='data/img/placeholder.png' data-src='data/img/SLUG.jpg' alt='NAME'>

app.js 파일은 다음과 같이 data-src 속성을 처리합니다.

 let imagesToLoad = document.querySelectorAll('img[data-src]'); const loadImages = (image) => { image.setAttribute('src', image.getAttribute('data-src')); image.onload = () => { image.removeAttribute('data-src'); }; };

그런 다음 루프를 만듭니다.

 imagesToLoad.forEach((img) => { loadImages(img); });

Smashing Magazine에서 다른 요소와 함께 콘텐츠 점프를 줄이는 방법에 대한 철저한 가이드도 확인할 수 있습니다.

PWA는 모든 입력 방법과 함께 작동합니다.

PWA가 다양한 장치와 어떻게 작동해야 하는지 살펴보았습니다. 한 단계 더 나아가 사용자가 이러한 장치에서 사용할 수 있는 터치, 마우스 및 스타일러스와 같은 다른 입력 방법도 고려해야 합니다.

PWA에 Pointer Events API를 추가하면 주로 이 문제를 해결할 수 있습니다. 다음은 Google 개발자에 따라 접근할 수 있는 방법입니다.

먼저 브라우저가 포인터 이벤트를 지원하는지 확인하십시오.

 if (window.PointerEvent) { // Yay, we can use pointer events! } else { // Back to mouse and touch events, I guess. }

다음으로 다양한 입력 방법이 수행할 수 있는 작업을 정의할 수 있습니다.

 switch(ev.pointerType) { case 'mouse': // Do nothing. break; case 'touch': // Allow drag gesture. break; case 'pen': // Also allow drag gesture. break; default: // Getting an empty string means the browser doesn't know // what device type it is. Let's assume mouse and do nothing. break; }

대부분의 브라우저에는 이미 터치 지원 기능이 있으므로 다른 것을 추가할 필요가 없습니다.

검색을 통해 검색 가능

기본 애플리케이션에 비해 PWA의 주요 이점 중 하나는 PWA가 본질적으로 웹사이트이고 검색 엔진이 이를 색인화할 수 있다는 것입니다. 이를 통해 PWA 콘텐츠를 보다 쉽게 ​​검색할 수 있도록 SEO 전략을 배포할 수 있습니다.

PWA의 각 URL에 SEO 최적화 활동의 기준이 되는 고유하고 설명적인 제목과 메타 설명이 있는지 확인하는 것으로 시작할 수 있습니다.

PWA를 검색 가능하게 만들기 위해 취할 수 있는 몇 가지 다른 단계를 살펴보겠습니다.

PWA의 검색 가능성 분석

Google의 Search Console에는 사이트(PWA)를 분석하고 결과를 보고하는 훌륭한 도구가 있습니다. 이를 사용하여 사이트의 기본 스캔을 실행하고 취약점을 찾아 수정할 수 있습니다.

또는 Chrome 브라우저에서 Lighthouse를 사용하여 SEO 감사를 실행할 수 있습니다.

먼저 대상 URL로 이동합니다. 그런 다음 Control+Shift+J (Mac에서는 Command+Option+J )를 눌러 개발자 도구 메뉴를 엽니다. Lighthouse 탭을 선택하고 SEO 카테고리 상자를 선택한 다음 보고서를 생성합니다.

Google 크롬 브라우저 Lighthouse 개발자 도구 카테고리 SEO 스크린샷.
Google 크롬 브라우저 Lighthouse 개발자 도구 카테고리 SEO 스크린샷. (큰 미리보기)

구조화된 데이터 사용

Google의 검색 엔진은 구조화된 데이터를 사용하여 웹페이지에서 콘텐츠의 목적을 이해합니다.

구조화된 데이터는 페이지에 대한 정보를 제공하고 페이지 콘텐츠를 분류하기 위한 표준화된 형식입니다. 예를 들어, 레시피 페이지에서 재료, 조리 시간 및 온도, 칼로리 등은 무엇입니까?
- Google

코딩을 시작하기 전에 Google은 일반적인 구조화된 데이터 오류의 유용한 목록과 이러한 오류를 수정하기 위한 관련 가이드라인도 마련했습니다. 이 자료를 공부하면 무엇을 피해야 하는지에 대한 좋은 기준을 얻을 수 있습니다.

Frederick O'Brien은 Smashing Magazine, Baking Structured Data Into Design Process 에서 처음부터 구조화된 데이터를 구축하는 방법을 설명하는 훌륭한 가이드를 작성했습니다.

사용자 친화적인 알림 및 권한 요청

마지막으로 알림 및 권한 요청을 최적화하여 사용자 경험을 향상시켜 사용자에게 서비스를 제공할 수 있습니다.

일반적으로 상식에 의존할 수 있지만 방해가 되지 않는 푸시 알림을 생성하고 사용자에게 메시지 수신 거부 옵션을 제공하는 것과 같이 구현할 수 있는 실용적인 팁도 있습니다.

통신을 위한 미묘한 권한 요청

웹 사이트와 사용자 간의 통신을 자동화하는 두 가지 최신 방법이 있습니다. 바로 챗봇과 알림입니다.

PWA 컨텍스트에서 챗봇의 주요 장점은 사용자와 상호 작용하기 위해 사용자의 권한이 필요하지 않다는 것입니다. 그러나 사용하는 챗봇 애플리케이션에 따라 사용자는 미묘한 메시지를 놓칠 수 있습니다. 반면 알림은 사용자의 허가가 필요하지만 훨씬 더 눈에 잘 띕니다.

챗봇을 별도의 타사 애플리케이션으로 추가할 수 있으므로 사용자 친화적인 푸시 알림을 만드는 데 집중합시다. 처음에 푸시 알림을 만드는 방법에 대한 가이드가 필요한 경우 Indrek Lasn의 훌륭한 가이드가 있습니다.

비침해적 권한 요청을 생성하는 가장 간단한 방법은 이중 요청을 사용하는 것입니다. 즉, 사용자 OS의 기본 상호 작용 위에 사이트에 대한 사용자 지정 상호 작용을 포함합니다.

Matt Gaunt는 다음 이미지와 함께 이 효과에 대한 완벽한 일러스트레이션을 제공합니다.

컨텍스트를 제공하지 않는 기본 알림 권한 요청은 다음과 같습니다.

잘못된 UX 권한 요청 예시
Permission UX에 의한 잘못된 UX 권한 요청 예시입니다. (큰 미리보기)

그리고 위에서 설명한 기본 알림 권한 앞에 추가된 사용자 지정 상호 작용은 다음과 같습니다.

좋은 UX 권한 요청 예시
Permission UX의 좋은 UX 권한 요청 예시. (큰 미리보기)

OS의 기본 알림 앞에 사용자 지정 알림을 추가하여 알림의 목적을 사용자에게 보다 명확하게 설명할 수 있습니다. 이렇게 하면 사용자가 사이트 알림을 수신할 가능성이 높아집니다.

사용자가 알림을 거부할 수 있도록 설정

사용자의 경우 사이트의 푸시 알림을 비활성화하는 것은 사용 중인 장치에 관계없이 매우 성가신 일입니다. 따라서 사용자에게 메시지에서 선택 해제할 수 있는 옵션을 제공하면 사용자 경험 측면에서 큰 도움이 됩니다.

다음은 코드 및 UI에 구독 취소 기능을 구현하는 방법에 대한 Matt Gaunt의 예입니다.

먼저 pushButton 의 클릭 리스너를 정의합니다.

 pushButton.addEventListener('click', function() { pushButton.disabled = true; if (isSubscribed) { unsubscribeUser(); } else { subscribeUser(); } });

그런 다음 새 기능을 추가합니다.

 function unsubscribeUser() { swRegistration.pushManager.getSubscription() .then(function(subscription) { if (subscription) { // TODO: Tell application server to delete subscription return subscription.unsubscribe(); } }) .catch(function(error) { console.log('Error unsubscribing', error); }) .then(function() { updateSubscriptionOnServer(null); console.log('User is unsubscribed.'); isSubscribed = false; updateBtn(); }); }

다음은 사용자가 선택하는 경우 알림을 비활성화하기 위해 구독 버튼을 성공적으로 구현한 후 콘솔에서 보이는 방법입니다.

성공적인 알림 기능 활성화/비활성화의 Console.log 예
Matt Gaunt의 성공적인 알림 기능 활성화/비활성화의 Console.log 예. (큰 미리보기)

결론

PWA는 사이트 트래픽을 늘리고 사용자 경험을 확장하는 데 매우 강력할 수 있습니다. PWA를 더욱 최적화하기 위해 이 문서에 설명된 이러한 최신 솔루션을 사용하여 성능과 기능을 향상할 수 있습니다.

또한 한 번에 모든 것을 구현할 필요도 없습니다. 이러한 각 제안은 PWA를 발전시키는 데 도움이 되므로 가장 관련성이 높은 옵션을 자유롭게 선택하십시오.

추가 리소스

  • Google의 프로그레시브 웹 앱 교육
  • web.dev의 프로그레시브 웹 앱
  • Mozilla의 프로그레시브 웹 앱(PWA)