웹 프로그래밍 고급 주제들

4. 익스텐션 그 너머 - PWA: Progressive Web Application 2/2

영바이트 2024. 3. 27. 08:15

 

지난 포스팅에서 PWA의 구조에 대해 살펴보았고 PWA의 기반이 되는 전통적인 웹 애플리케이션을 작성해보았다.

<전통적인 웹 애플리케이션 구조>

 

전통적인 웹 애플리케이션 구조에서는 웹 서버가 멈추면 더 이상 웹 애플리케이션을 이용할 수 없게 된다. 하지만 PWA 구조에서는 웹 서버가 멈추더라도 전체 기능까지는 아니더라도 사전에 정의한 수준까지 웹 서비스를 제공하게 할 수 있다. 이는 PWA에 포함되어 있는 서비스 워커가 웹 서버의 대리인(proxy) 역할을 함으로서 가능하다.

<PWA 구조의 웹 애플리케이션>

 

이전 포스팅 '익스텐션 그 너머 - PWA 1/2'에서 전통적인 웹 애플리케이션에 해당하는 아래 내용까지 작성하였다. 여기서 sw.js 파일이 서비스 워커 스크립트지만 그 내용을 작성하지는 않았었다. 이 sw.js 파일을 작성해보자.

<온도 변환 웹 서비스 스크립트들>

 

 

const CACHE_NAME = `temperature-converter-v1`;

// 앱이 설치될 때 서버의 응답들을 저장해둔다.
self.addEventListener('install', event => {
    event.waitUntil((async () => {
        // cache API는 웹 브라우저에 정보를 저장할 수 있도록 storage 기능을 제공한다.
        const cache = await caches.open(CACHE_NAME);
        // 서버 도메인에 속해 있는 아래의 자원들을 요청하고 그 결과를 캐시에 저장한다.
        cache.addAll([
            '/'
            , '/converter.js'
            , '/converter.css'
            , '/manifest.json'
            , '/icon512.png'
        ]);
    })());
});

//  클라이언트의 요청(request)을 서비스 워커가 먼저 받아서 처리한다.
self.addEventListener('fetch', event => {
    event.respondWith((async () => {
        const cache = await caches.open(CACHE_NAME);

        // 요청의 응답을 캐시에서 찾는다.
        const cachedResponse = await cache.match(event.request);

        // 요청의 응답이 캐시에 저장되어있다면 이를 클라이언트에 전달한다.
        if (cachedResponse) {
            return cachedResponse;
        }
        // 요청이 캐시에서 발견되지 않은 경우
        else {
            try {
                // 요청의 응답이 캐시에서 발견되지 않았다면 서버로 요청을 보낸다.
                const fetchResponse = await fetch(event.request);

                // 요청에 대한 응답을 받으면 이를 복사한 후 캐시에 저장한다.
                cache.put(event.request, fetchResponse.clone());
                return fetchResponse;
            }
            catch (e) {
                // Network error
                console.log('[Error]', e);
            }
        }
    })());
});

<MySamplePWA/sw.js>

 

서버를 본사(headquater)에 비유한다면 서비스 워커는 지사(local office)로 비유할 수 있다. 웹 서비스, 즉 웹 어플리케이션이 서비스를 제공하기 위해 필요한 자원이 있다면 이를 본사에 요청하고 가져다 쓰는 것이 가장 직관적이다. 하지만 네트워크 환경에는 늘 불확실성이 존재한다. 서비스 워커는 이런 불확실성을 완충시켜주는 완충장치 역할을 해준다. 서비스 제공을 위해 필요한 자원을 매 번 본사에서 가져다 쓰는 것이 아니라, 지사(local)에 보관해 두었다가 사용한다. 이 역할이 sw.js 스크립트에 구현되어 있다.

 

sw.js 파일의 내용은 두 개의 이벤트 리스너로 이루어져 있다. 첫 번째 리스너는 install 이벤트 즉, PWA가 PC에 설치될 때( PWA는 PC에 설치(install)가 가능하다) 실행되는 내용이다. 애플리케이션에 필요한 자원을 서버에 요청하고 cache라 불리는 저장 장소에 보관한다. cache storage API는 클라이언트 요청에 대한 서버 응답을 통째로 저장하기 때문에 나중에 응답 자체에 접근할 필요가 있을 때 사용하면 편리하다.

sw.js 스크립트의 두 번째 이벤트 리스너는 웹 애플리케이션이 서버에 요청(request)을 보내기 위해 fetch 동작을 할 때 마다 발생한다. 즉, 웹 애플리케이션의 fetch 동작을 서비스 워커가 먼저 살펴본다. 만약 서비스 워커가 저장해 놓은 응답이 있다면 이를 즉시 제공하고, 요청에 상응하는 응답이 저장되어 있지 않다면 서버에 전달한다.

 

PWA를 구성하기 위한 또 다른 추가 요소는 manifest 파일이다. manifest 파일은 웹 브라우저에 PWA가 어떻게 구성되어 있는지 알려주는 역할을 한다. 아래와 같이 manifest.json 파일을 작성한다.

{
    "lang": "en-us"
    , "name": "Temperature converter app"
    , "short_name": "Temperature converter"
    , "description": "A basic temperature converter that can convert to and from Celsius, Kelvin, and Fahrenheit."
    , "start_url": "/"
    , "background_color": "#2f3d58"
    , "theme_color": "#2f3d58"
    , "orientation": "any"
    , "display": "standalone"
    , "icons": [
        {
            "src": "/icon512.png"
            , "sizes": "512x512"
        }
    ]
}

<MySamplePWA/manifest.json>

 

각 항목의 내용을 아래와 같이 정리할 수 있다.

항목 내용
lang 웹 앱(PWA)의 기본 언어
name 웹 앱의 이름
short_name 웹 앱의 짧은 이름
description 웹 앱을 소개하는 글
start_url 웹 앱이 서비스에 필요한 자원(파일)들을 찾기위한 기본 위치.
보통 manifest 파일이 위치하는 경로를 root 위치인 /로 인식한다.
background_color 웹 앱의 디자인 파일(CSS)이 로딩되기 전에 보여지는 바탕색
theme_color 웹 앱의 기본 바탕색
orientation 화면의 방향 가로, 세로 등
display 웹 앱이 어떻게 표시될지를 지정한다. 예를 들어 display를 'browser'로 지정하면 브라우저 안에서 실행되는 형태로 보여진다.
icons 웹 앱의 아이콘

 

manifest 파일에 대한 자세한 내용을 아래 페이지에서 확인할 수 있다.

https://developer.mozilla.org/en-US/docs/Web/Manifest 

 

Web app manifests | MDN

A web application manifest, defined in the Web Application Manifest specification, is a JSON text file that provides information about a web application.

developer.mozilla.org

 

이제 마지막으로 웹 앱의 아이콘을 다운로드하고 프로젝트 폴더에 추가하자. 여기서는 마이크로소프트에서 제공하는 아이콘을 사용하였다. 아래 URL에서 구할 수 있다.

https://learn.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium/how-to/index-images/icon512.png

 

sw.js 스크립트, manifest 파일, 그리고 아이콘을 추가하여 PWA 웹 앱을 구성하였다. 아래와 같이 프로젝트 파일들이 구성되었을 것이다.

<MySamplePWA 웹앱 구성 파일들>

 

 


 

 

이제 파이썬 실행 환경을 활성화하고(이전 포스팅 참조) local 웹 서버인 run.py 스크립트를 구동시킨다.

MySamplePWA$> .\venv\Scripts\activate.ps1
또는
MySamplePWA$> .\venv\Scripts\activate.bat

(venv)MySamplePWA$> python run.py

 

<VS Code 터미널에서 웹 서버 스크립트 run.py 실행>

 

웹 브라우저를 띄우고 서버가 응답 대기 중인 127.0.0.1.8000 또는 192.168.0.12:8000으로 접속하면 이전과 같이 웹 서비스를 제공받을 수 있다.

<웹 애플리케이션 서비스 화면>

 

이제 웹 서버를 중지하자. 웹 서버 스크립트가 실행 중인 터미널에서 Ctrl + C 키를 입력하면 실행 중인 서버가 중단될 것이다.

<웹 서버 중지>

 

웹 서버 실행이 중지된 상태에서 웹 서비스 화면에서 페이지를 새로고침하거나, 새로 웹 브라우저를 띄우고 웹 서비스 주소인 127.0.0.1.8000 또는 192.168.0.12:8000으로 접속하면 서비스 페이지를 찾을 수 없다는 메시지가 나와야 한다. 하지만웹 애플리케이션이 제공하는 서비스가 정상적으로 이어지는 것을 볼 수 있다.

<웹 서버 중단 후 PWA 웹 앱 실행>

 

이는 sw.js 스크립트에 작성했던 서비스 워커가 동작하고 웹 애플리케이션 동작에 필요한 리소스들을 대신 제공하기 때문에 가능한 일이다.

 

앞 내용에서 PWA 웹 앱은 PC에 설치가 가능하다고 하였다. 브라우저의 주소창 오른쪽을 보면 설치 아이콘이 보일 것이다.

<PWA 웹 앱 설치 아이콘>

 

이 아이콘을 클릭하면 웹 앱이 설치된다. 그리고 작업 표시줄에 아이콘을 고정할 것인지도 물어본다.

<웹 앱 설치 후 작업 표시줄에 고정>

 

작업 표시줄에 아이콘을 고정하지 않아도 된다. 모든 앱(윈도우 OS 기준 윈도우 버튼) 메뉴에 설치된 웹 앱이 표시된다.

<설치된 앱 - 윈도우 버튼 팝업 메뉴 내>

 

설치된 웹 앱은 일반적인 앱처럼 다루어진다. 제거하기를 원한다면 윈도우 OS 기준으로 프로그램 설치/제거 기능을 통해 관리할 수 있다.

<설치된 웹 앱의 관리>

 

 

참고로 맥OS 에서도 PWA를 지원한다. 웹 앱을 로컬 앱처럼 설치해서 사용할 수 있다.

<Mac OS PWA 앱>

 

 


 

 

이번 포스팅까지 총 9개의 포스팅에 걸쳐 웹 브라우저 익스텐션을 소개하고 간단한 예제들을 작성해보면서 그 구조와 동작에 대해서 살펴보았다. 브라우저 익스텐션은 익스텐션이 목표로 하는 타겟 웹 페이지들에 추가적인 기능을 제공해 줄 수 있고, 브라우저 자체의 기능도 확장시켜 줄 수 있다.

 

마지막 두 포스팅에서는 브라우저 익스텐션의 또 다른 발전된 형태라 할 수 있는 Progressive Web Application(PWA)에 대해서 살펴보았다. 브라우저 익스텐션이 타겟 웹 페이지들과 상호작용을 전제로 한 프로그램인데 반해 PWA는 그 자체로 동작하는(standalone), 발전된 브라우저 익스텐션이다. 웹 애플리케이션이 제공하는 서비스는 '연결'을 전제로 한 것이지만 그 기능의 일부 또는 전체가 연결없이도 유용하다면 PWA는 네트워크의 불확실성에 따른 서비스 이용 제한을 경감시켜주고, 네트워크 자원의 효율적인 사용(PC에 설치해서 사용할 때)을 가능하게 해 주는 독특한 장점을 제공한다.