기본 콘텐츠로 건너뛰기

pwa (Progressive Web App) 기초


작년에 w3c 세미나에 운좋게 참여해다가
세상에 웹이 이렇게나 바뀌고 있었나 싶었던 내용을
이제서야 조금 정리해봤다.

PWA(Progressive Web App)

Progressive Web App은 웹의 장점과 앱의 장점을 결합한 환경입니다. 이것은 사용자가브라우저 탭을 맨 처음 방문할 때부터 유용하며, 설치가 필요 없습니다. 시간의 흐름에 따라 사용자가 앱과의 관계를 점진적으로 쌓아갈수록 성능이 더욱 강력해질 것입니다. 이 웹 앱은 느린 네트워크에서도 빠르게 로드되고, 관련된 푸시 알림을 전송하며, 홈 화면에 아이콘이 있고, 최상위의 전체 화면 환경으로 로드됩니다.

모바일웹과 네이티브앱을 비교해보자

웹이 나은점
 - ios, android 두벌로 개발할 필요가 없다.
 - 앱스토어가 필요 없으며 항상 최신버전을 유지할 수 있다.

앱이 나은점
 - 디바이스의 자원을 활용할 수 있다. (카메라, gps 등)
 - push
 - 인터넷이 끊긴 환경에서도 사용이 가능하다.

웹과 앱의 장단점은 명확하지만,
지금의 단말환경에서는
디바이스 자원을 활용할 수 있는 앱이 유리하다.

단, pwa에서는 지금의 웹이 못하는 앱의 장점을 2가지정도는 해결해줄수 있다.
- push, offline

서비스워커(Service-Worker)

pwa에서 가장 핵심요소이다.
현재 크롬,파이어폭스에서는 지원이 가능하며, 사파리와 IE는 개발중이다.
즉, 곧 머지않아 모든 브라우저에서 사용이 가능하다는 뜻이다.
왜?
좋으니까.

구글 개발자사이트에 나온 서비스 워커의 정의는 이렇다

서비스 워커는 브라우저가 백그라운드에서 실행하는 스크립트로, 웹페이지와는 별개로 작동하며, 웹페이지 또는 사용자 상호작용이 필요하지 않은 기능에 대해 문호를 개방합니다. 현재 푸시 알림 및 백그라운드 동기화와 같은 기능은 이미 제공되고 있습니다. 향후 서비스 워커는 주기적 동기화 또는 지오펜싱과 같은 다른 기능을 지원할 것입니다.

하나더 들어가서, 그럼 워커에 대해 간단하게 정리하면

자바스크립트는 기본적으로 단일 스레드로 동작을 한다.
브라우저에서 dom을 렌더링 하는것부터 모든것이 단일스레로 동작을 한다는 뜻인데,
워커는 이와 별개로 백그라운드에서 별도의 실행흐름을 만들어 브라우저의 성능을 더 뽑아먹을 수 있게 해주겠다는 얘기다.
서비스워커는 dom렌더링에 관여하지는 않지만, 네트워크 핸들링을 할 수 있으며 이를기반으로 푸시 이벤트를 지원할 수 있게된다.

서비스워커 예제

- 아래코드를 html페이지에 추가하면 service-worker를 바로 시작할 수 있다.
- service-worker.js 라는 파일을 새로 생성하여 service-worker가 할일을 따로 구현해야한다.
 if ('serviceWorker' in navigator) {
    navigator.serviceWorker
             .register('./service-worker.js')
             .then(function() { console.log('Service Worker Registered'); });
  }

service-worker.js
var cacheName = 'weatherPWA-step-6-1';
var filesToCache = [];
self.addEventListener('install', function(e) {
  console.log('[ServiceWorker] Install');
  e.waitUntil(
    caches.open(cacheName).then(function(cache) {
      console.log('[ServiceWorker] Caching app shell');
      return cache.addAll(filesToCache);
    })
  );
});

install 이벤트는 서비스워커가 최초로 설치됄때 1번 실행된다.
위의 예제에서는 fileToCache라는 배열에 나열된 resource (js,css, image 파일등)들을 저장할 예정이다.
이런식으로 서비스워커에서는 cache기능을 사용해서 인터넷 신호가 약하거나 없는 환경에서도 서비스를 진행할 수 있도록 지원이 가능하다.

서비스워커 생명주기

 - 서비스워커는 dom의 실행과는 별개로 생명주기를 가지고 있다.
 - 설치,설치완료 - 실행 ,실행완료 - 종료
 - 캐시를 사용할때 서비스워커의 특성들을 고려해서 적절한 방법을 모색하여야 한다. 
    (캐시 전략 : https://jakearchibald.com/2014/offline-cookbook/#cache-network-race)



push

서비스워커를 사용해 push를 받을 수 있다.
구글개발자사이트에 있는 예제를 실행해봤다.




왼쪽이 html화면이다 push이벤트를 수신동의/불가 하는 버튼이 있으며
버튼 클릭시 하단에  json형식의 데이터는 구글 테스트 웹 푸시(https://web-push-codelab.glitch.me)
를 보내기위한 파라미터 정보가 표시된다.

오른쪽에는 실행중인 서비스워커를 확인할 수 있다.
if ('serviceWorker' in navigator && 'PushManager' in window) {
  console.log('Service Worker and Push is supported');

  navigator.serviceWorker.register('sw.js')
  .then(function(swReg) {
    console.log('Service Worker is registered', swReg);

    swRegistration = swReg;
    initialiseUI();
  })
  .catch(function(error) {
    console.error('Service Worker Error', error);
  });
} else {
  console.warn('Push messaging is not supported');
  pushButton.textContent = 'Push Not Supported';
}
첫째줄에 serviceWorker외에 pushManager가 추가됨을 확인할 수 있다.
push도 이미 api화 되어있기 때문에 사용하기 어렵지 않아 보인다.

self.addEventListener('push', function(event) {
  console.log('[Service Worker] Push Received.');
  console.log(`[Service Worker] Push had this data: "${event.data.text()}"`);

  const title = 'Push Codelab';
  const options = {
    body: event.data.text(),
    icon: 'images/icon.png',
    badge: 'images/badge.png'
  };

  event.waitUntil(self.registration.showNotification(title, options));
});
서비스워커가 푸쉬이벤트를 감지하게 되면 아래의 코드로 인해 메세지를 노출하게 된다.
생각보다  간단하다.


실제로 메세지를 보내봤다. (https://web-push-codelab.glitch.me)
오른쪽에 알람이 오는것을 확인할 수 있다.




모바일에서도 진행을 해봤는데,
ios의 크롬앱에서는 제대로 동작하지 않았으며,
android의 크롬앱에서는 제대로 동작하는것을 확인할 수 있었다.
ios에서는 아직 지원이 제대로 되고 있지 않은듯 하다.





(글자가 검은색;;)




소감

우선 서비스워커에 대해 학습하는데 어느정도 시간이 걸릴듯하다. 주어진 예제코드를 실행하는데도 쉽게 눈에 들어오지는 않았다. javascript 기초를 조금더 단단하게 할 필요가 있다. 
pwa와 마찬가지로 구글에서 진행하는 amp에서도 service-worker를 지원한다.  (https://ampbyexample.com/components/amp-install-serviceworker/) 조금 더 빠른 모바일 페이지 렌더링을 위해 amp는 이미 어느정도 상용서비스에서 많이 사용하고 있다. pwa와 amp를 적절히 섞어서 사용한다면, 분명 좋은 성능을 낼 수 있을것 같다. 둘다 아직은 기능상 제약이 많지만 어찌됐든 관심을 가져야 할 부분이지 싶다. 

















댓글

이 블로그의 인기 게시물

메일서버가 스팸으로 취급받을때

설치한 메일서버를 통해 발송되는 메일이 스팸으로 들어가는 경우가 더러 있다. 이게 한번 들어가기는 쉬운데, 빠져나오기는 드럽게 힘든것 같다... 본인의 경우에는 우선 국내서비스에는 별 무리 없이 들어간다. (naver,daum 등) 그런데 해외메일 그중 Gmail, Hotmail 에는 에누리없이 스팸으로 간주되고 있었다. Gmail같은 경우에는 그래도 스팸함으로 발송은 제대로 되는반면에 Hotmail같은경우에는 아예 수신자체가 안되는 경우도 더러있다.. ㅡ,.ㅡ; 제일 좋은 방법은 Gmail,Hotmail에 전화걸어서 우리 메일서버 IP white Ip로 등록해달라!!! 하면 좋지만, 얘네들은 걸어봤자 자동응답기고, 문의채널은 구글 그룹스 게시판이 전부다.. 본론으로 들어가서. 해외 메일이 차단될 경우 내 매일서버ip가 스팸ip로 등록되 버린 경우일 수 있다. (본인처럼. ㅎ) 이것부터 조회 해보고 싶으면 RBL(real-time blocking List) 체크를 해야 하는데, RBL체크 해주는 사이트는 꽤 많이 있고, 그중 좀 깔끔해 보이는곳 하나 소개. http://www.anti-abuse.org/ 메일서버ip 입력하고 조회해보면 쭈루룩 리스트가 나온다. 그 중 빨간불이 들어온 부분이 메일 서버가 스팸서버가 된 각종 이유들이다.ㅋ 본인의 경우 CBL 때문에 걸렸는데, 내용은 아래와 같다. This IP address is HELO'ing as  "localhost.localdomain"  which violates the relevant standards (specifically: RFC5321). 메일서버 도메인에 별다른 작업을 안해놓아서 "localhost.localdomain" 으로 설정되어있었다. 만약 CBL만 바로 테스트 해보고 싶으면 http://cbl.abusea

[javascript] 특정시간에만 함수 실행

특정시간에만 팝업을 띄우려면?? 특정시간에만 로그인을 막으려면?? 특정시간에만 할일은 의외로 참 많다. 방법? 딱히 없다. 현재시간 구해서 시작시간, 종료시간 사이에 있을때 시작하는 수밖엔. if ((현재시간 > 시작시간) && (현재시간 < 종료시간)){ .. 팝업노출(); 공사페이지 리다이렉트(); 기타등등(); .. } 자바스크립트로 작성하면 다음과 같다. var startdate = "2014012008" ; var enddate = "2014012418" ; var now = new Date (); //현재시간 year = now. getFullYear (); //현재시간 중 4자리 연도 month = now. getMonth () + 1 ; //현재시간 중 달. 달은 0부터 시작하기 때문에 +1 if ((month + "" ). length < 2 ){ month = "0" + month; //달의 숫자가 1자리면 앞에 0을 붙임. } date = now. getDate (); //현재 시간 중 날짜. if ((date + "" ). length < 2 ){ date = "0" + date; } hour = now. getHours (); //현재 시간 중 시간. if ((hour + "" ). length < 2 ){ hour = "0" + hour; } today = year + "" + month + "" + date + "" + hour; //오늘 날짜 완성. / / 시간비교 i

스레드 동기화1 - syncronized

구현 스레드를 구현하는 방법은 2가지다. 1. Thread 클래스를 extends 한다. 2. Runnable 인터페이스를 implements 한다. 뭐 사실 Thread는 생성자의 변수로 Runnable을 취한다. public Thread (Runnable target) { init( null, target , "Thread-" + nextThreadNum () , 0 ) ; } 그리고 Runnable 인터페이스는 run() 이라는 단일함수를 갖는 인터페이스이다. @FunctionalInterface public interface Runnable { /** * When an object implementing interface <code> Runnable </code> is used * to create a thread, starting the thread causes the object's * <code> run </code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code> run </code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run () ; } 그렇기 때문에 람다식으로 표현이 가능한 것이다. 동기화 동기화는 동시에 같은자원의 접근을 제한하고자 할때 사용한다. 예를들어, 한 우물에서 물을 15번 길어야 하는 일을 해야할때 5명이서 3번만 하면 수고를 5배로 줄일수 있다. 그런데 우물은 하난데 동시에