개발자 / 미래기술

“브라우저 보편성+네이티브 기능성” 프로그레시브 웹 앱의 이해와 맛보기

Matthew Tyson | InfoWorld 2024.05.24
프로그레시브 웹 앱은 웹 브라우저의 보편성과 네이티브 애플리케이션의 풍부한 기능을 결합한 최신 웹 개발 방식이다. 서비스 워커(service worker) 같은 특수한 기능은 일반적인 웹 UI에 비해 개발의 복잡성을 증가시키지만, 웹 브라우저 내에서 네이티브와 같은 기능을 여러 기기에 걸쳐 제공한다는 강력한 이점이 있다.
 
ⓒ Getty Image Bank
 

프로그레시브 웹 앱의 특징

일반적인 웹 브라우저 애플리케이션과 노트북이나 휴대폰에 설치된 앱의 차이를 생각해 보면 프로그레시브 웹 앱의 가치를 알 수 있다. 설치형 앱의 가장 큰 특징은 네트워크에 연결하지 않아도 기기에서 실행된다는 점이다. 프로그레시브 웹 앱은 브라우저 내에서 유사한 오프라인 기능을 지원한다. 또한 프로그레시브 웹 앱은 브라우저 기반 웹 애플리케이션과 달리 애플리케이션 유형에 따라 크게 달라지며, 애플리케이션의 기능이 구현 방식을 결정하는 데 큰 역할을 한다.

프로그레시브 웹 앱의 일반적인 특징은 다음과 같다.
 
  • 오프라인 기능
  • 동기화를 포함한 백그라운드 기능
  • 홈페이지 "설치" 가능
  • 앱이 실행되고 있지 않을 때를 포함한 푸시 알림 및 경고
  • 공격적인 캐싱을 통해 간헐적인 네트워크 문제를 해결
  • 다양한 기기를 지원하는 반응형 모바일 우선 레이아웃
  • 링크를 통해 북마크 및 공유 가능

프로그레시브 웹 앱의 좋은 사용례는 웹에 배포된 애플리케이션을 전환해 오프라인과 같은 기능을 구현하는 것이다. 예를 들어 구글 문서 도구는 네트워크를 사용할 수 없을 때 '오프라인 모드'를 지원하는 브라우저 기반 앱이다. 기본적으로 이 앱은 모든 내용을 브라우저에 로컬로 저장한 다음 브라우저가 다시 온라인 상태가 되면 백엔드와 동기화한다. 물론 앱의 분산된 부분을 동기화하는 것은 본질적으로 복잡하다. 구글 문서 도구는 이를 정교한 아키텍처로 구현한다. 하지만 기본적인 애플리케이션은 훨씬 더 간단하다. 결과적으로 애플리케이션의 요구 사항에 따라 프로그레시브 웹 앱 구현 작업의 복잡성이 결정된다.

이제 프로그레시브 웹 앱이 어떤 기능을 제공하는지 이해했으니, 작동 방식을 살펴보자.
 

프로그레시브 웹 앱 설치하기

프로그레시브 웹 앱의 특징은 브라우저에서 실행하더라도 '설치'가 가능하다는 점이다. 프런트엔드에서 기기 홈페이지에 링크를 넣으면 브라우저에서 웹 사이트가 실행된다. 이런 설치 작업은 브라우저에 앱의 기능과 홈페이지 아이콘을 알려주는 manifest.json 파일을 통해 이뤄진다. 다음은 간단한 매니페스트의 예다.
 
{
  "name": "My PWA App",
  "short_name": "My PWA",
  "icons": [
       {
      "src": "icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    }
  ],
  "start_url": "/",
  "display": "standalone",
  "theme_color": "#ffffff"
}

브라우저가 루트 디렉터리에서 해당 파일을 발견하고 파일이 유효하면 홈페이지 링크를 추가하는 방식으로 작동한다.
 

서비스 워커

서비스 워커는 프로그레시브 웹 앱 기능을 제공하는 주요 수단이다. navigator.serviceWorker 객체를 사용하면 서비스 워커 API에 액세스할 수 있다. 보안(HTTPS) 컨텍스트에서만 사용할 수 있다. 서비스 워커는 워커 스레드(worker thread)와 비슷하지만 수명이 더 길고 DOM, 브라우저 API에 대한 액세스 권한이 더 적다.

서비스 워커는 메시지와 이벤트를 통해 메인 스레드나 다른 워커와 상호 작용할 수 있는 격리된 컨텍스트라고 생각하면 된다. 이런 이벤트에 응답하고, 네트워크 요청을 실행하고, 푸시 호출에 응답하고, 캐시 API 또는 IndexedDB에 저장한다. 서비스 워커는 메인 스레드로 돌아오는 메시지를 통해서만 UI에서 작업할 수 있다. 어떤 의미에서 서비스 워커는 브라우저에서 실행되는 프록시 미들웨어다.

서비스 워커에는 고유한 수명 주기가 있다. 특정 조건에 따라 종료 시기가 결정된다. 서비스 워커는 이를 생성한 페이지가 닫힌 후에도 푸시 업데이트를 기반으로 사용자에게 알림을 실행하고 표시한다. 더 자세한 내용은 서비스 워커 수명 주기를 참고하면 된다. 크롬 같은 브라우저별 서비스 워커 종료 정보도 찾을 수 있다.
 

서비스 워커 이벤트

기본적으로 서비스 워커는 비동기 이벤트 처리기다. UI 또는 백엔드에서 이벤트에 응답한다. 이벤트 간에 공유할 로컬 변수에 상태를 저장할 수 없고 캐시나 데이터베이스를 사용하므로 이벤트 간에 컨텍스트가 지워질 수 있도록 설계해야 한다. 다음은 서비스 워커가 응답할 수 있는 모든 이벤트다.
 
  • install : 이 이벤트는 서비스 워커가 처음 설치될 때 한 번 발생한다. 오프라인 기능을 활성화하기 위해 HTML, CSS, 자바스크립트 파일과 같은 자산을 미리 캐싱하는 데 자주 사용된다.
  • activate : 이 이벤트는 서비스 워커가 활성화된 후에 발생한다. 이전 버전의 서비스 워커에서 캐시를 정리하거나 서비스 워커가 클라이언트(웹 페이지)를 제어할 때 사용한다.
  • fetch : 이 이벤트는 제어되는 페이지가 네트워크에 요청할 때마다 발생한다. 이를 통해 서비스 워커는 메인 페이지에 대한 보이지 않는 중개자 역할을 수행해 요청을 가로채고 캐시된 버전을 제공하거나 완전히 처리하도록 요청을 수정할 수 있다.
  • sync : 이 이벤트는 네트워크 연결이 안정적일 때 브라우저에서 정의한 간격으로 발생한다. 오프라인에서 변경된 데이터를 서버와 동기화하는 데 자주 사용한다. 이 API는 네트워크가 사용 가능할 때 처리하도록 자동화하는 데도 유용하다.
  • push : 이 이벤트는 서비스 워커가 서버로부터 푸시 알림을 받을 때 발생한다. 이를 통해 서비스 작업자는 웹페이지가 닫혀 있어도 알림을 처리해 사용자에게 표시한다.
  • notificationclick : 이 이벤트는 사용자가 표시된 푸시 알림을 클릭할 때 발생한다. 이 이벤트를 사용해 알림 상호 작용을 처리하고 사용자를 프로그레시브 웹 앱 내의 특정 페이지로 이동할 수 있다.
  • error : 이 이벤트는 서비스 워커가 작동하는 동안 오류가 나타날 때 발생한다. 로깅 또는 디버깅 목적으로 사용할 수도 있다.
  • broadcast와 post messages : 메인 스레드에서 자바스크립트 코드에 의해 특별히 발생하는 이벤트다. 서비스 워커에 데이터를 전달하는 데 사용한다.

서비스 워커는 이런 이벤트와 함께 다음과 같은 다양한 API에 액세스할 수 있다.
 
  • IndexedDB : 쿼리를 지원하는 강력한 객체 저장소 데이터베이스다. 서비스 워커 인스턴스 사이에 존재하며 메인 스레드와 공유된다.
  • Cache API : 이를 사용하면 요청 개체를 쉽게 가져와 응답을 저장할 수 있다. fetch 이벤트와 결합해 오프라인 모드에 대한 응답을 메인 스레드의 관점에서 캐시할 수도 있다. 캐시 전략에 대한 더 자세한 설명은 여기를 참고하면 된다.
  • Fetch와 WebSocket API : 서비스 워커는 DOM에 대한 액세스 권한은 없지만 Fetch와 WebSocket API를 통해 네트워크에 대한 전체 액세스 권한을 가질 수 확보할 수 있다.
  • Geolocation : 서비스 워커를 지리적 위치에 노출하고 서비스 워커에서 지리적 위치를 지원하는 방법에 대한 논의가 계속 진행 중이다.
 

서비스 워커 예제

서비스 워커는 항상 다음과 같이 navigator.serviceWorker 객체가 있는 자바스크립트 파일을 로드하는 것으로 시작한다.
 
const subscription = await navigator.serviceWorker.register('service-worker.js');

그런 다음 service-worker.js에서 이벤트 구독이 이루어진다. 예를 들어 fetch 이벤트를 감시하고 캐시 API를 사용하려면 다음과 같이 하면 된다.
 
self.addEventListener('fetch', (event) => {
  const request = event.request;
  const url = new URL(request.url);

  // Try serving assets from cache first
  event.respondWith(
    caches.match(request)
      .then((cachedResponse) => {
        // If found in cache, return the cached response
        if (cachedResponse) {
          return cachedResponse;
        }

        // If not in cache, fetch from network
        return fetch(request)
          .then((response) => {
            // Clone the response for potential caching
            const responseClone = response.clone();

            // Cache the new response for future requests
            caches.open('my-cache')
              .then((cache) => {
                cache.put(request, responseClone);
              });

            return response;
          });
      })
  );
});

서비스 워커가 로드되면 self가 이를 참조한다. addEventListener 메서드를 사용하면 다양한 이벤트를 감시할 수 있다. fetch 이벤트 내에서 캐시 API를 사용해 주어진 요청 URL이 이미 캐시돼 있는지 확인하고, 캐시돼 있다면 다시 전송하는 것도 가능하다. URL이 새 URL인 경우 서버에 요청을 한 후 응답을 캐시한다. 캐시 API는 같은 요청인지 판단하는 데 있어 많은 복잡성을 제거한다. 서비스 워커를 사용하면 이 모든 것이 메인 스레드에 투명하게 표시된다.
 

브라우저 보편성과 네이티브 기능성의 결합

프로그레시브 웹 앱을 사용하면 브라우저에서 앱을 제공하고 일반적인 브라우저 기반 애플리케이션에서는 불가능한 기능을 제공할 수 있다. 반면 프로그레시브 웹 앱을 개발할 때는 더 복잡한 문제를 처리해야 한다. 프로그레시브 웹 앱으로 할 수 있는 네이티브와 유사한 작업 대부분에는 서비스 워커가 필요하며, 이는 안드로이드나 맥OS와 같은 운영체제를 사용하는 네이티브 앱에서 하는 것보다 더 많은 작업이 필요하다.

반면 프로그레시브 웹 앱 기술을 사용해 브라우저를 통해 여러 플랫폼에서 같은 기능을 구현하는 것은, 여러 플랫폼에 맞춰 기능을 다시 구현하는 것보다 작업량이 적다. 프로그레시브 웹 앱을 사용하면 하나의 코드베이스만 빌드하고 유지 관리하면 되며 익숙한 브라우저 표준으로 작업할 수 있다.
editor@itworld.co.kr
Sponsored

회사명 : 한국IDG | 제호: ITWorld | 주소 : 서울시 중구 세종대로 23, 4층 우)04512
| 등록번호 : 서울 아00743 등록발행일자 : 2009년 01월 19일

발행인 : 박형미 | 편집인 : 박재곤 | 청소년보호책임자 : 한정규
| 사업자 등록번호 : 214-87-22467 Tel : 02-558-6950

Copyright © 2024 International Data Group. All rights reserved.