본문 바로가기
프로그래밍/PWA

PWA(Progressive Web App) on github pages

by IJIJIJ 2020. 6. 30.

아주 간단한 PWA 만들기

PWA를 Github page에 올리고 스마트폰에 다운로드하기까

가족이 이틀씩 오전 근무와 야간 근무를 돌아가면서 하기 때문에 폰에서 쉽게 확인하기 위한 방법을 찾다가

PWA로 한 번 해보고자 정리합니다.

[소스코드]

- 아래의 저장소에서 확인하실 수 있습니다.

https://github.com/in3166/sunnyNight

[참고 사이트]

https://developers.google.com/web/fundamentals/codelabs/your-first-pwapp/?hl=ko

- 공부하기 위해서는 위 사이트를 차근차근 따라 해 보는 게 가장 좋을 것 같습니다.

본 게시글은 제가 구현했던 과정들만 나열하기 때문에 자세한 설명은 없습니다. (설명할 주제도 못되고.. ㅜ)

정말 대충 짠 코드들로 구성되므로 참고(?)만 하시고 참고 사이트를 확인해 주세요..

가능한 한 빨리 간단한 PWA를 만들고자 하시면 app.js와 index.html, inline.css 정도만 수정하시고 사용하시면 될 것 같습니다.

[파일 구성]

- images 폴더

└ icons 폴더

└ 아이콘.ico

- scripts 폴더

└ app.js

└ install.js

- styles 폴더

└ inline.css

-index.html

-manifest.json

-service-worker.js

1. manifest.json 만들기

- 사용하고자 하는 아이콘을 준비하시고 아래의 사이트에 들어갑니다.

https://app-manifest.firebaseapp.com/

"name": "SUNandNight", "short_name": "SN", "theme_color": "#e8f321", "background_color": "#888888", "display": "standalone", "start_url": "https://in3166.github.io/sunnyNight/" (추후 본인의 github 주소를 입력하세요.)

- 빈칸을 작성 후 자신이 원하는 아이콘을 올리고 GENERATE.ZIP 클릭합니다.

- 생성된 manifest.json와 이미지들을 작업 중인 프로젝트에 넣습니다.

2. 화면 만들기 (index.html, inline.css)

- 저는 표 형식으로 텍스트만 띄울 것이기 때문에 정말 간단하게 만들었습니다.

- 아래 서비스 워커를 위한 스크립트를 추가해 줍니다.

 

<index.html>

<body>
    <button id="butInstall" aria-label="Install" hidden>설치</button> 
    // hidden을 없애면 설치 버튼을 화면에 표시하여 버튼 클릭으로 설치할 수 있습니다. 
    // 하지만 버튼이 없어도 설치는 가능합니다.
    
    <div class="layer">
        <label id="la1" name="la1" class="la1">오늘</label><br><br>
        <strong> <label id="la2" name="la2" class="la2"></strong></label><br><br>
        <hr>
        <label id="la3" name="la3" class="la3">내일</label><br><br>
        <strong><label id="la4" name="la4" class="la4"></strong></label>
    </div>

    <div class="table1">
        <table>
            <tr>
                <th><label id="d0" name="d0" class="d0"></label></th>
                <th><label id="d1" name="d1" class="d1"></label></th>
                <th><label id="d2" name="d2" class="d2"></label></th>
                <th><label id="d3" name="d3" class="d3"></label></th>
                <th><label id="d4" name="d4" class="d4"></label></th>
                <th><label id="d5" name="d5" class="d5"></label></th>
                <th><label id="d6" name="d6" class="d6"></label></th>
            </tr>
            <tr>
                <td><strong><label id="l0" name="l0" class="l0"></label></strong></td>
                <td><strong><label id="l1" name="l1" class="l1"></label></strong></td>
                <td><strong><label id="l2" name="l2" class="l2"></label></strong></td>
                <td><strong><label id="l3" name="l3" class="l3"></label></strong></td>
                <td><strong><label id="l4" name="l4" class="l4"></label></strong></td>
                <td><strong><label id="l5" name="l5" class="l5"></label></strong></td>
                <td><strong><label id="l6" name="l6" class="l6"></label></strong></td>
            </tr>
        </table>
    </div>

    <script src="https://in3166.github.io/sunnyNight/scripts/app.js"></script>
    <!-- CODELAB: Add the install script here -->
    <script src="https://in3166.github.io/sunnyNight/scripts/install.js"></script>
    <script>
        // navigator 객체에 serviceWorker라는 프로퍼티가 있는지 확인하는 조건입니다. //만약 지원하지 않는 브라우저라면 serviceWorker라는 프로퍼티가 있을 경우에만 서비스워커를 등록 // CODELAB: Register service worker. if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('https://in3166.github.io/sunnyNight/service-worker.js') .then((reg) => { console.log('Service worker registered.', reg); }); }); }
    </script>
</body>

 


<inline.css>

.layer {
    padding: 20px;
    position: absolute;
    top: 30%;
    left: 35%;
    width: 200px;
    height: auto;
    justify-content: center;
    align-items: center;
    color: darkgoldenrod;
    border: solid darkgoldenrod 1px;
    margin: -50px 0 0 -50px;
    text-align: center;
}

body {
    background: rgb(48, 48, 48);
}

#la2,
#la4 {
    font-size: 35px;
}

#la1,
#la3 {
    font-size: 30px;
}

@media (max-width: 480px) {
    header h1 {
        font-size: 35px;
    }
    header p {
        font-size: 16px;
    }
    .pads {
        margin-bottom: 100px;
    }
}

 

2. 기능 구현 (app.js)

- 오늘 날짜와 특정 날짜를 비교해서 오늘과 내일의 근무를 출력, 오늘부터 7일간의 근무도 출력

window.addEventListener('load', () => {
    //serviceWorker.register();

// 오늘과 내일의 근무 형태를 출력할 엘리먼트
    var label2 = document.getElementById('la2');
    var label4 = document.getElementById('la4');
// 요일을 출력할 엘리먼트
    var l0 = document.getElementById('l0');
    var l1 = document.getElementById('l1');
    var l2 = document.getElementById('l2');
    var l3 = document.getElementById('l3');
    var l4 = document.getElementById('l4');
    var l5 = document.getElementById('l5');
    var l6 = document.getElementById('l6');
// 근무 형태를 출력할 엘리먼트
    var d0 = document.getElementById('d0');
    var d1 = document.getElementById('d1');
    var d2 = document.getElementById('d2');
    var d3 = document.getElementById('d3');
    var d4 = document.getElementById('d4');
    var d5 = document.getElementById('d5');
    var d6 = document.getElementById('d6');

// 특정 요일을 기준으로 오늘의 근무형태를 알아냅니다.
    let day = new Date(2020, 5, 13);
    let today = new Date();

    console.log("to: " + moment(today).format('YYYY MM DD HH:mm:ss'));
    console.log("day: " + moment(day).format('YYYY MM DD HH:mm:ss'));

    var cDay = 24 * 60 * 60 * 1000; // 시 * 분 * 초 * 밀리세컨
    var cMonth = cDay * 30; // 월 만듬
    var cYear = cMonth * 12; // 년 만듬

// 특정 날짜와 오늘의 일수 차이를 계산
    var dif1 = today - day;
    var dif = parseInt(dif1 / cDay)

// 나머지를 저장할 변수
    var namu = dif;
    var text, text2;
    console.log("일차이: " + parseInt(dif1 / cDay));

    namu = dif % 6;

// 텍스트 뿌려줌
    label2.innerText = swit(namu);
    label4.innerText = swit((namu + 1) % 6);
    l0.innerText = swit(namu);
    l1.innerText = swit((namu + 1) % 6);
    l2.innerText = swit((namu + 2) % 6);
    l3.innerText = swit((namu + 3) % 6);
    l4.innerText = swit((namu + 4) % 6);
    l5.innerText = swit((namu + 5) % 6);
    l6.innerText = swit((namu + 6) % 6);
    
    d0.innerText = getTodayLabel(today.getDay());
    d1.innerText = getTodayLabel(today.getDay() + 1);
    d2.innerText = getTodayLabel(today.getDay() + 2);
    d3.innerText = getTodayLabel(today.getDay() + 3);
    d4.innerText = getTodayLabel(today.getDay() + 4);
    d5.innerText = getTodayLabel(today.getDay() + 5);
    d6.innerText = getTodayLabel(today.getDay() + 6);
});

// 나머지에 따라 오늘의 근무 형태 출력하는 함수
function swit(namu) {
    switch (namu) {
        case 0:
        case 1:
            text = "오전";
            break;
        case 2:
        case 3:
            text = "야간";
            break;
        case 5:
        case 4:
            text = "휴일";
            break;
        default:
            text = "??";
            break;
    }
    return text;
}

//요일 구하기
function getTodayLabel(today) {

    var week = new Array('일', '월', '화', '수', '목', '금', '토');
    today = today % 7;
    var todayLabel = week[today];

    return todayLabel;
}

3. install.js

- install.js는 참고 사이트의 예제 파일을 긁어 왔습니다.

'use strict';

let deferredInstallPrompt = null;
const installButton = document.getElementById('butInstall');
installButton.addEventListener('click', installPWA);

// CODELAB: Add event listener for beforeinstallprompt event
window.addEventListener('beforeinstallprompt', saveBeforeInstallPromptEvent);

/**
 * Event handler for beforeinstallprompt event.
 *   Saves the event & shows install button.
 *
 * @param {Event} evt
 */
function saveBeforeInstallPromptEvent(evt) {
    // CODELAB: Add code to save event & show the install button.
    deferredInstallPrompt = evt;
    installButton.removeAttribute('hidden');
}


/**
 * Event handler for butInstall - Does the PWA installation.
 *
 * @param {Event} evt
 */
function installPWA(evt) {
    // CODELAB: Add code show install prompt & hide the install button.
    deferredInstallPrompt.prompt();
    // Hide the install button, it can't be called twice.
    evt.srcElement.setAttribute('hidden', true);
    // CODELAB: Log user response to prompt.
    deferredInstallPrompt.userChoice
        .then((choice) => {
            if (choice.outcome === 'accepted') {
                console.log('User accepted the A2HS prompt', choice);
            } else {
                console.log('User dismissed the A2HS prompt', choice);
            }
            deferredInstallPrompt = null;
        });
}

// CODELAB: Add event listener for appinstalled event
window.addEventListener('appinstalled', logAppInstalled);
/**
 * Event handler for appinstalled event.
 *   Log the installation to analytics or save the event somehow.
 *
 * @param {Event} evt
 */
function logAppInstalled(evt) {
    // CODELAB: Add code to log the event
    console.log('Weather App was installed.', evt);
}

4. service-worker.js

- service worker 또한 예제 파일을 긁어와 필요 없는 부분만 삭제했습니다.

- 서비스 워커의 역할을 간단히 말하자면 오프라인 상태에서도 해당 어플을 작동하게 해줍니다.

'use strict';
// CODELAB: Update cache names any time any of the cached files change.
const CACHE_NAME = 'static-cache-v6';
const DATA_CACHE_NAME = 'data-cache-v5';

// CODELAB: Add list of files to cache here.
const FILES_TO_CACHE = [
    '.',
    './index.html',
    './scripts/app.js',
    './scripts/install.js',
    './styles/inline.css'
];

self.addEventListener('install', (evt) => {
    console.log('[ServiceWorker] Install');
    // CODELAB: Precache static resources here.
    evt.waitUntil(
        caches.open(CACHE_NAME).then((cache) => {
            console.log('[ServiceWorker] Pre-caching offline page');
            return cache.addAll(FILES_TO_CACHE);
        })
    );
    self.skipWaiting();
});

self.addEventListener('activate', (evt) => {
    console.log('[ServiceWorker] Activate');
    // CODELAB: Remove previous cached data from disk.
    evt.waitUntil(
        caches.keys().then((keyList) => {
            return Promise.all(keyList.map((key) => {
                if (key !== CACHE_NAME) {
                    console.log('[ServiceWorker] Removing old cache', key);
                    return caches.delete(key);
                }
            }));
        })
    );

    self.clients.claim();
});

self.addEventListener('fetch', (evt) => {
    console.log('[ServiceWorker] Fetch', evt.request.url);

    evt.respondWith(
        caches.open(CACHE_NAME).then((cache) => {
            return cache.match(evt.request)
                .then((response) => {
                    return response || fetch(evt.request);
                });
        })
    );

    if (evt.request.mode !== 'navigate') {
        // Not a page navigation, bail.
        return;
    }

    evt.respondWith(
        fetch(evt.request)
        .catch(() => {
            return caches.open(CACHE_NAME)
                .then((cache) => {
                    return cache.match('offline.html');
                });
        })
    );
});

5. Github pages

https://github.com/

- 우선 Github에 로그인합니다.

- Repository 생성

New 버튼을 클릭하여 새로운 Repositorie를 만들어 줍니다.


Repository 이름을 입력하고 Public으로 설정 후 Create repository를 클릭합니다.


Quick setup 하단의 파란색 글씨, uploading an exsting file 클릭 후 본인이 작업한 폴더를 업로드합니다.


파일이 Repository에 모두 올라갔으면 Setting을 클릭합니다.


하단으로 조금 내리면 Github Pages가 보입니다. Source 아래의 None을 master branch로 바꿔줍니다.


상단에 본인의 주소가 뜨는 것을 확인할 수 있습니다. 클릭하시면 브라우저에 화면이 표시됩니다. 브라우저에서도 다운로드할 수 있습니다.

6. 스마트폰에 다운로드하기

- 위의 주소를 스마트폰에서 열어줍니다.

- 그럼 아래와 같이 화면이 뜨고 다운로드 버튼을 클릭하면 다운로드하실 수 있습니다.

댓글