아주 간단한 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
- 우선 Github에 로그인합니다.
- Repository 생성

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

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

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

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

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

상단에 본인의 주소가 뜨는 것을 확인할 수 있습니다. 클릭하시면 브라우저에 화면이 표시됩니다. 브라우저에서도 다운로드할 수 있습니다.
6. 스마트폰에 다운로드하기
- 위의 주소를 스마트폰에서 열어줍니다.
- 그럼 아래와 같이 화면이 뜨고 다운로드 버튼을 클릭하면 다운로드하실 수 있습니다.

댓글