[ 공지 ]
프로그레시브 웹 앱의 전반적인 내용에 대해 더 자세하게 배울 수 있는 도서가 출간되었습니다!
현재 블로그에 업로드된 PWA 강좌보다 더 다양하고 자세한 내용으로 구성되어있으며,
하나의 SNS 웹 앱을 개발해나가는 방식으로 실습을 진행합니다.
(도서 소개 및 실습 미리보기)
SNS 앱 예제로 배우는 프로그레시브 웹 앱
(구매 링크 / 2020.07.31 출간)
[YES24]
[교보문고]
[알라딘]
[인터파크도서]
프로그레시브 웹 앱의 기본적인 내용부터 시작하여, 서비스 워커, 오프라인 캐싱, 백그라운드 동기화 및 푸시 알림까지
상세한 설명을 통해 프로그레시브 웹 앱에 대해 더 다가갈 수 있을 것입니다.
안녕하세요~
이번 강좌에서는 서비스워커를 등록하고 서비스워커의 생명주기(Life Cycle)에 대해 알아보도록 하겠습니다.
실습 코드는 이전 강좌 그대로 진행되구요 추가적으로 필요한 준비사항도 없습니다!
그럼 두 번째 강좌를 시작하도록 하겠습니다!
[1. 서비스워커 등록하기]
ch2의 예제 코드 scripts/app.js 파일을 열어줍니다.
(소스코드는 첫 번째 강좌 참고)
61행을 확인해보시면
// TODO: 아래에 서비스워커 등록
주석이 있습니다.
주석 아래에 코드를 추가해줍니다.
if문 조건은 브라우저의 navigator 객체에 serviceWorker라는 프로퍼티가 있는지 확인하는 조건입니다.
만약 지원하지 않는 브라우저라면 serviceWorker라는 프로퍼티가 있을 경우에만 서비스워커를 등록합니다.
serviceWorker의 register 메소드를 통해 서비스워커 스크립트를 등록할 수 있습니다.
기본 코드에 service-worker.js 라는 빈 스크립트 파일이 있습니다.
해당 서비스워커 스크립트 경로를 작성해주면 되구요, 성공적으로 resolve되면 console.log의 메시지가 출력됩니다.
한 번 웹 페이지를 새로고침 해줍니다.
F12를 눌러 개발자 도구를 열어보시면 콘솔에 서비스워커가 등록되었다는 로그가 찍혀있습니다.
개발자도구 상단의 Application 탭을 선택한 후 좌측에 있는 Service Workers 메뉴를 선택해줍니다.
등록된 서비스 워커들은 여기서 확인이 가능합니다.
localhost에 대한 서비스 워커가 잘 등록되었네요
(날짜와 시간은 로컬호스트에서 테스트중이기 때문에 위와같이 나옵니다.)
(2019-08-06 수정: 현재는 날짜가 정상적으로 표시 됨, Chrome 오류였던것으로 판단)
상태 Status를 확인해보시면 Running 이라고 표시되어있네요
위쪽에 Update, Unregister는 서비스워커 업데이트, 등록 해제하는 부분이므로 한 번씩 눌러보셔도 됩니다.
빈 서비스워커를 등록했기 때문에 아무런 기능을 사용하지 못합니다.
Network에서 Offline 체크 후 새로고침을 해보면 여전히 오프라인이기 때문에 웹 페이지에 접근할 수 없습니다.
서비스워커에서 캐싱한 데이터를 오프라인 상태일때 응답해줘야 하는데
우리의 서비스워커는 아무런 기능이 없는 빈 파일이기 때문에 당연한 일입니다.
등록하는 방법은 매우 간단하기 때문에 여기까지 하도록 하고 서비스워커의 생명주기에 대해 알아봅시다.
[2. 서비스워커 생명주기]
서비스워커는 등록 이후에 순서대로 거치는 생명주기(Life Cycle)가 있습니다.
우리가 서비스워커를 등록하면
서비스워커는 설치를 진행하고 활성화 상태가 됩니다.
활성화 상태에만 푸시알림, Fetch 등 기능적인 부분에 대해 제어를 할 수 있습니다.
또한 서비스워커는 도메인당 한 개만 등록할 수 있고 활성화 됩니다.
이미 등록되어있는 서비스워커가 존재한다면, 업데이트 작업을 거치게 되면 기존 서비스워커는
제거되고 새로운 서비스워커로 대체됩니다.
서비스워커는 아래와 같은 생명주기를 가집니다.
이미지 원본
https://developer.mozilla.org/ko/docs/Web/API/Service_Worker_API/Using_Service_Workers
(원본 이미지의 내용을 번역하여 새로 작성했습니다.)
1. 등록
서비스워커를 등록합니다.
(위 사진엔 없음)
2. 설치
먼저 우리가 서비스워커를 등록하면 서비스워커는 설치를 진행합니다. (INSTALLING)
동일한 서비스워커인 경우 (업데이트 되지 않은 경우) 설치 과정을 건너뛰고 기존서비스워커를 그대로 둡니다.
설치 과정에서 오프라인에서 표시할 리소스를 캐싱할 수 있습니다.
설치 과정은 install 이벤트를 핸들링하여 조작할 수 있습니다.
(선택 1)
리소스를 캐싱하고 작업하는 경우 시간이 더 소요되는데 event.waitUntil()을 통해
작업이 설치 작업이 마무리 될 때 까지 설치 단계를 연장시길 수 있습니다.
(선택 2)
self.skipWaiting()을 통해 기존 서비스워커가 종료되기까지 대기하지 않고 새 서비스워커를 활성화합니다.
3. 설치 완료
리소스를 캐싱하는 등 설치 작업이 마무리되면 기존 서비스워커를 사용하는 클라이언트가 완전히 닫힐때 까지 대기합니다.
만약 처음 서비스워커를 등록하는 과정이라면 대기 없이 다음 과정으로 전환됩니다.
4. 활성화
활성화는 activate 이벤트를 핸들링하여 조작할 수 있습니다.
(선택 1)
이전 서비스워커가 캐싱한 데이터를 지우는 등 주로 정리작업을 수행합니다. (프로그래머가 작성)
이 과정 역시 시간이 소요되는데 event.waitUntil()을 통해 연장시킬 수 있습니다.
(선택 2)
self.clients.claim()을 통해 열려있는 클라이언트를 다시 로드하지 않고 본 서비스워커가 바로 제어할 수 있습니다.
5. 활성화 후
이제 본 서비스워커는 기능적인 이벤트를 처리할 수 있습니다.
push, fetch 등
6. 서비스워커 변경
작동중이던 서비스워커는 여전히 동작합니다.
서비스워커를 사용하는 클라이언트가 모두 종료되면 새로운 서비스워커로 대체됩니다.
만약 새 서비스워커 설치 과정에서 self.skipWaiting()이 사용되었다면 바로 대체됩니다.
서비스워커는 처음 접할 때 꽤 어려울 수 있습니다.
일단 DOM 조작을 위한 스크립트가 아니며 새로운 웹 기능을 조작하는 스크립트를 작성해야 하기 때문에 생소할 수 있습니다.
기본적으로는 아래와 같은 순서로 진행됩니다.
등록 -> 설치 -> 활성화 -> 기능 제어
활성화 이후에는 Functional events에 해당하는 이벤트를 핸들링하여 조작할 수 있습니다.
한 번 생명주기 이벤트를 코드 작성을 통해 알아봅시다!
[3. 서비스워커 생명주기 이벤트]
기본 소스코드의 service-worker.js 파일을 열어 아래 코드를 추가합니다.
파일을 저장하고 웹 페이지를 새로고침(F5) 해주세요
개발자도구를 확인해보면 INSTALL 로그가 찍혀있습니다.
그런데 활성화(activate 이벤트) 로그가 찍히지 않았네요?
그 이유는 위에서 처음 등록한 서비스 워커가 존재하기 때문입니다.
만약 처음 서비스 워커를 등록하는 과정이라면, 활성화 되었다는 로그도 함께 찍혔을겁니다.
지금은 기존에 활성화되어있던 서비스워커가 존재하기 때문에
해당 서비스워커를 사용중인 클라이언트(현재 우리가 보고있는 웹 페이지)가 완전히 종료되고
서비스워커 사용이 중단될 때 까지 대기하고 있기 때문입니다.
개발자도구 상단의 Application 탭을 선택한 후 좌축에 Service Workers 메뉴를 선택해보시면
우리의 서비스워커가 waiting to activate 상태인것을 확인하실 수 있습니다.
옆쪽에 skipWaiting을 눌러서 대기중인 상태를 벗어나봅시다.
눌러보시면 기존에 작동하던 서비스워커는 소멸되고 우리의 서비스워커가 새로 활성화됩니다.
또한 조용하던 로그창에도 활성화되었다는 로그가 찍히게 됩니다.
이제 페이지 새로고침을 다시 해봅시다.
콘솔에 남는 내용을 확인해보면 Fetch 이벤트에서 로그가 남는것을 확인하실 수 있습니다.
설치와 활성화는 이미 이전에 마무리되었기 때문에 로그에 남지 않습니다. 이젠 활성화 이후에
제어할 수 있는 fetch, push, sync 에 대한 이벤트만 담당합니다.
fetch는 가져오다 라는 의미가 있는데 서비스워커에서는 웹 브라우저의 기본 fetch 활동을 가로챌 수 있습니다.
무슨 의미인지 헷갈릴텐데 다음 내용의 예제를 작성하여 확인해봅시다.
[4. 서비스워커를 통해 브라우저 Fetch 가로채기]
service-worker.js의 맨 위에 있는 _version = 'v1' 의 값을 'v2' 로 변경해줍니다.
이 부분은 안해줘도 되지만 로그를 통해 서비스워커 업데이트 과정을 확실히 확인하기 위해 다른 값으로 변경하였습니다.
service-worker.js의 fetch 이벤트 콜백 함수 내용을 아래와 같이 작성합니다.
log(...) 부분까지는 진행했던 부분이므로 아래 if 문 블럭만 추가해주시면 됩니다.
if 문 조건은 요청URL에 .jpg 문자열이 있을 경우 true가 되는 조건입니다.
즉, jpg 파일을 요청할 경우 참이 되는 조건이죠
조건이 참일 경우 respondWith를 통해 브라우저의 기본 fetch를 서비스워커가 대신 수행하겠다는 코드입니다.
가로채기가 바로 이 부분이 됩니다.
respondWith의 인자로 Response 객체를 포함하는 프라미스를 전달해주면 됩니다.
fetch()는 말 그대로 지정한 리소스를 가져오는 메소드입니다. (ajax, axios를 생각하시면 쉽습니다)
fetch()는 응답 객체를 포함하는 Promise를 반환하기 때문에 respondWith에 사용할 수 있습니다.
[ RespondWith 더 알아보기 ]
https://developer.mozilla.org/ko/docs/Web/API/FetchEvent/respondWith
[ Fetch 더 알아보기 ]
https://developer.mozilla.org/ko/docs/Web/API/Fetch_API/Fetch%EC%9D%98_%EC%82%AC%EC%9A%A9%EB%B2%95
위 코드를 전체적으로 정리해보자면
jpg 파일을 요청할 경우 서비스워커에서 /images/2.jpg 파일로 대체하여 응답한다.
라고 할 수 있습니다.
브라우저에서 3.jpg, 4.jpg, test.jpg 등 모든 .jpg 요청은 서비스워커로 인해 2.jpg로 대체됩니다.
한 번 웹 페이지를 새로고침 해 봅시다.
결과는 동일하고 변한게 없습니다.
로그를 확인해보면 새로 수정한 v2 서비스워커 설치가 진행되었습니다.
아까전과 동일한 현상이네요.
이미 등록되고 활성화되었던 서비스워커가 있기 때문에 설치 이후에 대기 상태로 진입한 듯 합니다.
다시 한번 확인해보니 역시 대기 상태였네요
skipWaiting을 눌러 이전 서비스워커를 대체해봅시다.
로그에는 [ServiceWorker v2] Activate 라는 로그가 추가로 남겨져있고 새로운 서비스워커로 업데이트되었습니다.
그런데 이미지는 아직 동일하네요?
서비스워커에서 fetch 이벤트를 제어하려면 서비스워커가 활성화 되어있어야 합니다.
우리의 v2 서비스워커는 이미지 로드 후 방금 활성회되었기 때문에 이미지는 1.jpg로 표시됩니다.
다시 새로고침을 해 보시면 우리가 원하던 2.jpg가 나올 것 입니다.
PWA는 점진적으로 개선된다는 특징이 있는데 바로 이런 부분입니다.
첫 방문때는 서비스워커 등록, 캐싱 등 작업을 진행하지만 이후에 사용자가 재방문 할 경우 개선된 PWA를 경험할 수 있습니다.
다시 새로고침을 해 봅시다.
정말 우리가 의도한대로 1.jpg가 2.jpg로 바뀌었습니다.
+ 버튼을 눌러 새로운 이미지를 추가해봅시다.
.jpg로 이루어진 모든 파일이 2.jpg로 대체된 것을 확인하실 수 있습니다!
개발자 도구에서 실제 HTML 요소를 확인해봅시다
1, 2, 3, 4, 5 jpg 파일인데 모두 2.jpg (토끼 + 오리)사진으로 변경되어있네요
개발자도구 상단의 Network 탭을 선택해보면 어떻게 된 일인지 확인할 수 있습니다.
요청 리스트 중 1, 2, 3, 4, 5 사진들의 Size가 나와있지 않고 (ServiceWorker)라는 문구가 자리를 채우고 있습니다.
서비스워커를 통해 모든 .jpg 요청이 2.jpg로 대체된 결과가 보이고 있습니다.
이처럼 서비스워커를 통해 브라우저의 기본 fetch를 무시하고 원하는 응답을 전달할 수 있습니다.
다음 강좌에서는 오프라인 상태에도 페이지를 확인할 수 있도록 리소스를 캐싱하고 로드하는 기능을 구현해보도록 하겠습니다.
감사합니다~!