[ 공지 ]
프로그레시브 웹 앱의 전반적인 내용에 대해 더 자세하게 배울 수 있는 도서가 출간되었습니다!
현재 블로그에 업로드된 PWA 강좌보다 더 다양하고 자세한 내용으로 구성되어있으며,
하나의 SNS 웹 앱을 개발해나가는 방식으로 실습을 진행합니다.
(도서 소개 및 실습 미리보기)
SNS 앱 예제로 배우는 프로그레시브 웹 앱
(구매 링크 / 2020.07.31 출간)
[YES24]
[교보문고]
[알라딘]
[인터파크도서]
프로그레시브 웹 앱의 기본적인 내용부터 시작하여, 서비스 워커, 오프라인 캐싱, 백그라운드 동기화 및 푸시 알림까지
상세한 설명을 통해 프로그레시브 웹 앱에 대해 더 다가갈 수 있을 것입니다.
안녕하세요~
오랜만에 진행하는 PWA 강좌인것 같네요
이번 강좌에서는 Push 기능을 구현하여 웹 앱에서 푸시 알림을 받을 수 있도록 진행할 예정입니다!
소스코드는 지난 강좌에 이어서 진행할 예정이지만,
추가된 코드가 있기 때문에 제공하는 코드에서부터 시작하시는것을 추천드립니다.
혹시 소스코드가 필요하시다면 아래 링크에서 다운로드 가능합니다.
https://github.com/leegeunhyeok/pwa-example
(다운로드 방법은 첫 번째 강좌 참조)
ch5 폴더의 소스코드로 시작하시면 됩니다!
[1. 푸시 & 풀]
먼저 푸시 알림에 대해 진행하기 전에 어떤 방식으로 푸시 알림을 주고 받는지 간단하게 짚고 진행하도록 하겠습니다!
먼저 Pull은 클라이언트가 서버로 데이터를 요청한 후 서버로부터 데이터를 직접 가져오는 방식입니다.
웹 페이지와 서버를 예로 들자면, 웹 페이지에서 XMLHTTPRequest(ajax, axios 등) 요청을 보내고
직접 데이터를 받아오는 상황을 생각해볼 수 있습니다.
풀 방식은 사용자(클라이언트)가 직접 데이터를 당겨오는 방식으로 이해하시면 될 것 같습니다.
이와 반대로 Push는 서버에서 클라이언트로 정보를 보내주는 방식입니다.
클라이언트가 데이터를 요청하지 않아도 전송 요청이 중앙 서버에서 시작되는 정보 전달방식입니다.
스마트폰의 푸시 알람 등 다양한 알람 서비스가 모두 서버에서 클라이언트로 정보를 보내는
푸시 방식으로 구현되었다고 볼 수 있습니다.
실제 애플리케이션 서버는 기존과 동일하게 주요 로직을 처리하고,
실제 푸시알림을 보내는 서버는 다른 중개서버를 통해 알림을 전송합니다.
주로 구글의 FCM (또는 GCM), 애플의 APNs 등의 클라우드 메시징 서비스를 사용하며
직접 푸시 서버를 구축할 수도 있지만 다양한 이슈가 있기 때문에
대부분 제공되고 있는 클라우드 메시징 서비스를 사용합니다.
애플리케이션 서버와 푸시 서버가 각각 분리되어있는 구조이며
애플리케이션 서버는 푸시 알림에 대한 리소스 자원에 대해 자유롭기 때문에 부담이 적으며 본 기능에 집중할 수 있다는 장점이 있습니다.
푸시 알림을 보내야 하는 경우 (예: 댓글 알림, 새로운 글 알림), 애플리케이션 서버에서 클라우드 메시징 서비스의 서버로 사용자 등록 정보(특정 사용자)와 푸시 알림 메시지 내용을 전달하기만 하면 그 이후의 푸시 알림 전송과정은 클라우드 메시징 서비스가 알아서 처리한 후 사용자에게 푸시 알림을 전달합니다.
간단한 상황을 가정하여 알아보도록 합시다.
(1)
먼저 A라는 서비스 사용자가 B유저를 팔로우 하기 위해 버튼을 눌러 액션을 진행했습니다.
버튼을 누르면 팔로우 데이터를 애플리케이션 서버로 전송하게 됩니다.
(2)
애플리케이션 서버에서는 팔로우 정보를 받고 내부적인 로직을 거쳐 데이터베이스 또는 기타 작업을 진행합니다.
(푸시 기능이 없는 서비스라면 여기서 과정이 마무리 됩니다)
A사용자가 B유저를 팔로우 했기 때문에 B유저에게 팔로우 사실을 푸시 알림으로 알리려고 합니다.
애플리케이션 서버에서는 클라우드 메시징 서비스의 서버로 푸시 알림을 요청합니다.
(3)
푸시 알림을 요청할 때 서비스 인증 키, 푸시 알림 메시지 내용과 어떤 유저에게 푸시알림을 요청할지에 대한 등록정보를 함께 클라우드 메시징 서비스로 전송합니다.
여기서 등록정보는 사용자가 알림 기능을 구독(사용)할 때
클라우드 메시징 서비스에게 등록을 요청하게 되는데 이때 받아오는 정보입니다.
등록되지 않은 사용자는 푸시 알림을 받을 수 없습니다.
(4)
클라우드 메시징 서비스에서 서비스 인증 키, 푸시 알림 대상의 등록정보를 확인하여
푸시 알림을 사용자에게 전달합니다.
클라이언트에서 푸시 정보를 받고 스마트폰, PC등 다양한 기기에서
푸시 알림을 사용자에게 전달합니다.
간단하게 설명하면 위와 같은 순서의 프로세스를 거치게 되며,
사용자 입장에서는 푸시 알림으로 인해 우리의 애플리케이션에 재참여할 수 있게 유도할 수 있습니다.
[2. 클라우드 메시징 서비스]
PWA 푸시 강좌는 구글의 FCM을 사용하여 진행할 예정입니다.
FCM은 Firebase Cloud Messaging의 약자로 다양한 플랫폼으로 푸시 알림을 전송할 수 있는 서비스입니다.
기존에는 GCM (Google Cloud Messaging)을 많이 사용했는데
FCM으로 기능이 통합되고, 다양한 기능이 추가되었기 때문에 FCM으로 진행하도록 하겠습니다.
클라우드 메시징 서비스는 아래와 같은 과정을 거쳐 사용자에게 푸시 알림 기능을 제공합니다.
(1)
사용자가 웹 앱에 접속하여 알림을 구독할 경우 클라우드 메시징 서비스로 등록을 요청합니다.
(2)
요청을 받은 클라우드 메시징 서비스는 해당 유저의 등록 정보를 제공합니다.
(3)
유저는 클라우드 메시징 서비스로부터 받은 등록 정보를 애플리케이션 서버로 전송하여
추후 애플리케이션 서버가 나에게 알림을 보낼 수 있도록 합니다.
(알림을 받을 유저의 등록 정보가 필요하기 때문)
(A)
특정 이벤트(팔로우, 새 댓글 등)로 인해 애플리케이션 서버가 특정 유저에게 푸시 알림을 보내야 하는 경우
알림을 보낼 대상 유저의 등록정보와 푸시 메시지 내용을 클라우드 메시징 서비스로 전달합니다.
(B)
애플리케이션 서버로부터 수신한 정보를 확인하며 등록된 유저인 경우 푸시 알림을 보내고, 등록되지 않은 정보인 경우 애플리케이션 서버에게 이를 알립니다.
PWA 푸시 강좌의 최종 목적지는 위 그림의 클라이언트와 애플리케이션 서버를 직접 구현하고 FCM와 연동하여 푸시 메시지 기능을 사용할 수 있도록 할 예정입니다!
먼저 FCM과 PWA를 연동하기 전에 기본적인 푸시 기능 구현 및 테스트를 진행하고
클라우드 메시징 서비스와 애플리케이션 서버를 구현할 예정입니다.
[3. 푸시 알림 구독]
푸시 알림을 사용자가 구독해야 푸시 알림을 받을 수 있기 때문에
먼저 구독에 대한 로직을 구현하도록 하겠습니다.
ch5 디렉토리를 Simple Server를 통해 실행시킨 후 브라우저에서 접속하면 아래와 같이 보여질겁니다.
(이미지 갯수는 localStorage 데이터에 따라 다를 수 있음)
상단에 Subscribe! 버튼이 추가되어있으며 해당 버튼은
알림 구독하기, 구독 취소하기와 같은 액션을 위해 사용되는 버튼입니다.
HTML 코드를 확인해보시면 subscribe ID가 부여되어있으니 참고하시길 바랍니다!
개발을 시작하기에 앞서 현재 상황으로는 우리의 애플리케이션 서버가 없기 때문에
구글의 애플리케이션 서버를 임시로 사용하여 진행하도록 하겠습니다.
https://web-push-codelab.glitch.me
위 링크에 접속하면 공개 키와 비공개 키가 생성되는데 여기서 공개 키를 복사하여 소스코드로 붙여넣기 합니다.
여기서 생성된 공개키/개인키 쌍은 웹 푸시 프로토콜에서 인증하기 위해 사용되는
VAPID(자발적 애플리케이션 서버 식별, Voluntary Application Server Identification)키 입니다.
자세한 설명은 생략하지만, 푸시 서비스와 사용자 그리고 푸시를 트리거하는 웹 서버의 유효성을 검사(인증)하고,
기타 문제가 발생한 경우 관리자에게 연락하는 수단을 제공하기 위해 사요합니다.
scripts/app.js의 4행에 붙여넣기합니다.
<YOUR_KEY> 부분을 지우고 복사한 공개키를 붙여넣어주세요
isSubcribed는 구독 상태를 저장하는 상태 변수입니다.
swRegist는 서비스 워커가 등록되었을 경우 반환되는 서비스워커 등록 객체입니다.
소스코드 아래쪽을 확인해보면 구현되지 않은 함수들이 있습니다.
주요 기능들은 강좌를 진행하며 직접 구현할 예정입니다.
먼저 initPush 함수를 구현해봅시다.
initPush함수는 푸시 기능에 대한 초기화 함수입니다.
유저의 푸시 구독 상태, 알림 기능 활성화 상태 등 기본적인 상태를 확인하는 함수입니다.
위와 같이 코드를 작성합니다.
먼저 #subcribe 버튼에 클릭 이벤트를 등록하는 모습을 확인하실 수 있습니다.
유저가 알림을 구독한 상태라면 구독 취소하고, 구독하지 않았다면
알림을 받아볼 수 있도록 구독하는 로직으로 구현되어있습니다.
subcribe 함수는 현재 비어있기 때문에 버튼을 눌러도 아무런 반응이 없습니다.
아래의 이 코드는 기존에 푸시 알림을 구독한 정보를 가져옵니다.
만약 유저가 이미 구독한 상태라면 구독 정보가 resolve되어 전달됩니다.
구독되지 않은 상태라면 null이 반환되기 때문에 구독 상태를 판단할 수 있게 됩니다.
구독 정보를 확인하여 isSubscribed 값을 변경하고 updateSubscription 함수로 구독 정보를 전달합니다.
updateSubscription 역시 비어있는 함수이며 추후 구독 정보를 애플리케이션 서버로 전달하기 위한 로직을 구현할 예정입니다.
updateButton 함수는 알림 권한 및 구독 상태에 따라 버튼의 텍스트와 활성화/비활성화 여부를 변경할 함수이며 곧 구현할 함수입니다!
초기화 함수에서 알림 구독 로직을 처리할 subcribe 함수를 구현해봅시다.
urlB64ToUint8Array 함수를 통해 공개키 문자열을 Uint8Array 타입으로 변환하는 코드를 확인하실 수 있습니다.
(urlB64ToUint8Array 함수는 app.js 코드 상단에 구현되어있음)
변환된 애플리케이션 서버 코드는 applicationServerKey 값으로 지정해주면 되며
userVisibleOnly 프로퍼티는 푸시가 전송될 때마다 알림을 표시할지 결정하는 값 입니다.
작성 시점에서는 이 값이 필수이고 반드시 true여야 합니다.
구독하지 않은 처음 사용자인 경우 새로 등록하여 구독정보를 받아옵니다.
구독정보를 받고 해당 정보는 initPush와 동일하게
updateSubscription를 통해 애플리케이션 서버로 전송하고 버튼 상태를 업데이트합니다.
구독 정보를 받아온 경우 구독을 정상적으로 한 상황이므로 isSubscribed의 값을 true로 변경합니다.
[4. 버튼 상태 갱신]
앞서 진행한 과정 중 pushManager.subscribe 및 pushManager.getSubscription을 통해
구독 상태를 판단할 수 있음을 확인했습니다.
구독 상태를 판단하고 isSubscribed 변수의 상태값을 변경할 수 있도록 구현했는데요,
이제 이 isSubscribed 값을 확인하여 버튼의 상태 메시지를 변경할 수 있도록 updateButton 함수를 구현해봅시다.
구독 액션을 위해 미리 구성해둔 #subscribe 버튼의 Element를 가져온 후 해당 버튼의 텍스트를 바꾸려고 합니다.
isSubscribed 상태에 따라 푸시 알림 활성화/비활성화 텍스트가 표현되도록 구현되었습니다.
[5. 구독 정보 전송]
구독 정보를 서버로 전송하는 함수인 updateSubscription을 구현해봅시다.
현재 애플리케이션 서버를 구현하지 않았으므로 서버로 전송하지 않고,
구독 정보를 웹 화면에 표현하도록 구현할 예정입니다.
간단하게 구성했는데요 인자로 받아온 구독 정보가 존재한다면
해당 구독 정보를 문자열로 반환하여 subscription_detail 영역에 텍스트로 보여주고,
구독 정보가 없다면 해당 subscription_detail영역을 숨기도록 구현되어 있습니다.
마지막으로 initPush 함수를 호출하도록 코드를 추가해줍니다.
// TODO: Push 기능 초기화 부분에 initPush 함수를 호출하도록 작성해주세요!
[6. 푸시 알림 받기]
푸시 알림을 받기 위해 서비스워커 소스코드를 추가해줍니다.
(service-worker.js)
event를 통해 푸시 데이터를 받아올 수 있습니다.
받아온 데이터를 통해 푸시 알림을 구성하고, showNotification을 통해 푸시 알림을 생성할 수 있습니다.
showNotification은 Promise가 반환되며 waitUntil을 통해 Push 이벤트를 연장시켜야 합니다.
서비스워커의 푸시 이벤트 핸들러는 데이터를 받고, 보여주기만 하면 되기 때문에 복잡하지 않습니다.
브라우저에서 캐시와 서비스워커를 제거하거나 서비스워커 소스코드 상단의 _version과 cacheName 값을 변경하여 새로 작성한 코드가 캐싱되고 등록될 수 있도록 수정해주세요!!
웹 페이지를 다시 로드하면 상단의 "Subscribe!" 버튼이
"Enable Push Messaging" 텍스트로 변경됨을 확인하실 수 있습니다.
updateButton으로 구현한 로직이 잘 동작하고 있네요.
푸시 메시지 활성화 버튼을 누르면 브라우저에서 알림 권한을 사용자에게 물어봅니다.
허용을 눌러줍니다.
권한을 허용하면 등록 정보를 받아오게 되고, subscription_detail 영역에 표현됩니다.
보여지는 구독 정보는 추후에 애플리케이션 서버로 전송하게 될 예정이고,
구독 정보를 통해 서버에서 지금 웹 앱을 실행하고 있는 기기에 푸시 알림을 보낼 수 있습니다.
푸시 알림을 확인하기 위해 구독정보를 복사하여 https://web-push-codelab.glitch.me 페이지 하단에 붙여넣습니다.
(아까 생성한 키 값이 변경되었다면 소스코드의 키 값도 다시 변경해주셔야 합니다.)
Text to Send 부분에는 푸시 메시지로 받을 데이터를 작성해주세요
그리고 SEND PUSH MESSAGE 버튼을 누르면 우리의 웹 앱으로 푸시 메시지를 보내줍니다!
드디어 웹에서 푸시 알림을 받았습니다!
사실 매우 간단한 푸시 테스트인 경우 개발자 도구를 통해서도 가능합니다.
(app.js에 구현했던 코드 전혀 없이)
Application > Service Workers > Push에 메시지 입력후 Push 버튼
개발자 도구를 통해 진행하는 테스트의 경우 서비스 워커의 Push 이벤트에 구현된 부분만
결과물로 확인할 수 있기 때문에
알림 구독, 구독 취소, 권한 상태 등에 대한 결과는 확인할 수 없습니다.
(간단히 푸시 알림만 확인할 때 사용하는 기능입니다)
지금은 애플리케이션 서버는 구글 코드랩의 테스트 서버로 빌려 사용하고 있지만
이후 강좌에서는 직접 애플리케이션 서버를 구축하여 진행할 예정입니다.
페이지를 새로 고침해도 여전히 구독 정보가 남아있기 때문에
구독을 취소하지 않는 이상 구독 정보는 그대로 유지됩니다.
웹 앱의 Disable Push Messaging 버튼을 눌러보면 구독이 취소되어야 하는데 아직 아무런 반응이 없습니다.
또한 브라우저에서 알림 권한을 거부했을 경우의 처리도 되어있지 않은 상황입니다.
알림 권한을 "차단" 으로 변경하고 페이지를 새로고침할 경우
Enable Push Messaging 버튼을 눌러도 아무 반응이 없습니다.
유저가 직접 알림을 차단(비활성화가 아닌 차단)한 경우 버튼을 비활성화 하고 유저에게 차단된 기능임을 알려야 합니다.
다음 강좌에서는
푸시 알림을 클릭했을 때의 이벤트 처리, 알림 구독 취소, 브라우저 알림 권한 확인에 대한 부분을 알아보고 구현하도록 하겠습니다.
감사합니다!