1. PWA란?
PWA란 Progressive Web Application의 약자로 웹 기술의 유연성과 접근성을 활용하면서 모바일 앱과 유사한 사용자 경험을 제공하는 웹 어플리케이션 유형이다.
PWA는 최신 웹 브라우저가 있는 모든 장치에서 작동하도록 설계되어 플랫폼 간 호환성을 제공하고 플랫폼별 개발의 필요성을 줄인다.
2. PWA의 특징
(1) 반응형 디자인
PWA는 기본적으로 반응형 디자인을 염두하여 개발해야 한다.
반응형 디자인은 다양한 화면 크기와 방향에 맞게 레이아웃과 콘텐츠를 자동으로 조정한다.
이를 통해 스마트폰, 태블릿 및 데스크탑과 같은 다양한 장치에서 일관된 사용자 경험을 보장한다.
(2) 연결 독립성
PWA는 기본 브라우저 스레드와 별도로 백그라운드에서 실행되는 서비스워커를 사용한다.
서비스 워커는 네트워크 요청을 인터셉트하고 콘텐츠를 캐싱하여 인터넷 연결이 안된 상태에서도 콘텐츠를 제공할 수 있다.
(3) 앱처럼 사용
PWA는 사용자의 장치에 설치되어 일반 앱처럼 아이콘을 홈 화면에 배치할 수 있다.
또한, 기존 url을 보여주는 공간 없이 실행할 수 있어서 앱처럼 사용할 수 있다.
(4) 푸시 알림
PWA는 앱이 활성화 되지 않은 경우에도 사용자에게 푸시 알림을 보낼 수 있다.
(5) 백그라운드 동기화
PWA는 백그라운드에서 앱을 사용하지 않을 때도 최신 상태로 유지할 수 있다.
(6) 보안 컨텍스트
PWA는 HTTPS를 통해 제공되어 사용자와 앱간의 보안 연결을 보장해야 한다.
이를 통해 데이터를 보호하고 무단 액세스나 조작을 방지할 수 있다.
(7) 검색
PWA는 웹이기 때문에 앱스토에서 앱을 설치할 필요 없이 검색엔진을 통해 검색하고 URL을 통해 공유할 수 있다.
3. 서비스 워커
서비스 워커는 캐싱, 백그라운드 동기화 및 푸시 알림과 같은 다양한 기능을 지원하는 PWA의 핵심 기술이다.
기본적으로 기본 브라우저 스레드와 별도로 백그라운드에서 실행되는 JavaScript 스크립트로
웹 앱과 네트워크 간의 프록시 역할을 한다.
(1) 이벤트 중심
서비스 워커는 이벤트 중심이다.
즉, 필요한 경우에만 실행되어 푸시 또는 동기화와 같은 특정 이벤트에 응답한다.
이를 통해 필요한 리소스를 최소화하고 불필요한 처리 능력이나 메모리를 소비하지 않도록 한다.
(2) 캐싱
서비스 워커는 네트워크 요청을 가로채고 응답을 캐시하여 웹 앱이 오프라인이거나
신뢰할 수 없는 네트워크에 있는 경우에도 계속 작동할 수 있다.
네트워크를 사용할 수 없거나 너무 느릴때 캐시된 콘텐츠를 제공하여 원활한 사용자 경험을 제공할 수 있다.
(3) 백그라운드 동기화
서비스 워커는 백그라운드 동기화 작업을 수행할 수 있으므로 앱이 활성화되지 않은 경우에도
웹 앱에서 데이터를 보내거나 검색할 수 있다.
이렇게 하면 기기가 오프라인 상태가 되더라도 앱이 최신 상태로 유지할 수 있다.
(4) 푸시 알림
서비스 워커는 앱이 활성화되지 않았거나 브라우저가 닫힌 경우에도 PWA가 서버에서 푸시 알림을 받을수 있도록 한다.
(5) 보안 컨텍스트
서비스 워커는 HTTPS를 통해 제공되는 페이지에만 등록할 수 있다.
이를 통해 사용자 데이터를 보호하고 무단 액세스 또는 조작을 방지할 수 있다.
4. 서비스워커의 생명주기
등록 -> 설치 -> 활성화 -> 실행 -> 업데이트 -> 종료
서비스 워커는 생명 주기 동안 이벤트에 응답하여 PWA의 주요기능을 활성화 한다.
(1) 등록
기본 JavaScript 파일 (index.js 나 main.js) 에서 브라우저가 서비스 워커를 지원하는지 확인한 다음 서비스워커 파일을 등록한다.
이렇게 하면 PWA용 서비스 워커를 설치하고 활성화하도록 브라우저에게 알린다.
// main.js
// 브라우저가 서비스워커를 지원하는지 체크
if ('serviceWorker' in navigator) {
// 서비스워커 등록
navigator.serviceWorker
.register('/service-worker.js')
.then((registration) => {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch((error) => {
console.error('Service Worker registration failed:', error);
});
}
(2) 설치
서비스 워커가 등록되면 브라우저에서 install 이벤트를 트리거 한다.
서비스 워커 파일에서 이 이벤트를 수신하고 설치 후 여러 기능들을 수행할 수 있다.
설치 프로세스가 완료되면 서비스워커는 "설치됨" 상태로 들어가고 브라우저는 활성화 단계를 진행한다.
// service-worker.js
const cacheName = 'my-cache-v1';
const assetsToCache = [
'/',
'/index.html',
'/styles.css',
'/main.js',
'/icon.png',
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(cacheName).then((cache) => {
console.log('Opened cache:', cacheName);
return cache.addAll(assetsToCache);
})
);
});
install 이벤트 리스너는 지정된 'cacheName'으로 캐시를 열고 'assetsToCache' 배열에서 자산을 추가한다.
서비스워커는 브라우저와 별도의 스레드에서 실행된다고 했다.
그래서 기본 스레드를 차단하지 않고 백그라운드에서 작업을 수행하기 위해 window 객체가 아닌 self 키워드를 사용하여
'WorkerGlobalScope' 라는 웹 워커와 'ServiceWorkerGlobalScope' 라는 서비스 워커의 전역 객체를 참조한다.
여기서 서비스워커는 self 키워드를 사용하여 'ServiceWorkerGlobalScope' 객체를 참조한다.
self 키워드는 이 컨텍스트 내에서 사용할 수 있는 속성 및 메서드에 대한 액세스를 제공한다.
즉, self를 사용하여 기본 JavaScript 실행 컨텍스트가 아닌 서비스 워커 자체에 이벤트 리스너를 연결할 수 있다.
(3) 활성화
브라우저가 "활성화" 이벤트를 트리거하면 활성화 단계가 시작된다.
이 단계에서 오래된 캐시를 정리하거나 기타 유지 관리 작업을 수행할 수 있다.
서비스 워커가 활성화 되면 PWA에서 이벤트를 처리할 준비가 된다.
기존 서비스 워커가 있는 경우 새 서비스 워커는 활성화하기 전에 이전 서비스 워커가 더 이상 클라이언트를 제어하지 않을때까지 기다린다.
// service-worker.js
// Activate event
self.addEventListener('activate', (event) => {
const cacheWhitelist = [cacheName];
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (!cacheWhitelist.includes(cacheName)) {
// whitelist에 없는 캐시를 제거한다(캐시 최신화)
return caches.delete(cacheName);
}
})
);
})
);
});
activate 이벤트는 서비스 워커가 설치 후 활성화 될때 트리거되는 이벤트이다.
이 이벤트는 이전 캐시 정리, 캐시 버전 업데이트 및 기타 리소스 관리와 관련된 작업을 수행할 수 있다.
일반적으로 install 이벤트 이후에 발생하며 사용할 서비스 워커를 설정하는 마지막 단계이다.
이벤트가 트리거되면 서비스 워커는 caches.keys()를 사용하여 이름 목록을 검색한 다음 캐시 이름이 cacheWhitelist에 포함되지 않은 경우 caches.delete(cacheName)을 사용하여 해당 캐시를 삭제한다.
이 예제는 서비스 워커가 activate이벤트를 사용하여 오래된 캐시를 정리하고 캐시된 자산의 최신 버전만 사용되도록 하는 방법이다.
(4) 실행
서비스 워커가 활성화 되면 fetch, push 및 sync와 같은 이벤트를 수신할 수 있다.
서비스 워커는 네트워크 요청을 가로채고, 캐시된 콘텐츠를 제공하고, 백그라운드 동기화를 활성화하고, 푸시 알림을 처리할 수 있다.
이 단계는 서비스 워커가 PWA의 핵심 기능을 제공하는 단계이다.
// service-worker.js
// Fetch event
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
// 캐시가 존재하면 캐시 return
return cachedResponse;
}
// 캐시가 없다면 network에 요청
return fetch(event.request).then((response) => {
// 캐시에 넣기
return caches.open(cacheName).then((cache) => {
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
fetch 이벤트를 통해 기존 캐시가 있다면 가져오고 없다면 캐시에 집어넣는 작업을 수행한다.
네트워크 요청이 이루어지면 caches.match(event.request) 를 사용하여 일치하는 응답이 있는지 캐시를 확인한다.
캐시된 응답이 발견되면 즉시 반환하고 없다면 fetch(event.request)를 사용하여 네트워크에서 리소스를 가져온다.
fetch 이벤트를 통해 서비스 워커는 캐시된 콘텐츠를 제공하거나 사용자 지정 캐싱 전략을 구현하는 등 네트워크 요청을 가로채고 응답을 조작할 수 있다.
(5) 업데이트
서비스 워커의 새 버전이 출시되면 브라우저에서 등록 및 설치를 시도한다.
새 서비스 워커가 현재 서비스 워커와 다른 경우 브라우저는 새 워커에 대한 install 이벤트를 트리거하여 업데이트된 자산을 캐시할 수 있다.
새 서비스 워커는 현재 서비스 워커가 더 이상 클라이언트를 제어하지 않을 때까지 "대기" 상태에 들어간다.
현재 서비스 워커가 해제되면 새 워커가 활성화 단계로 진행된다.
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(cacheName).then((cache) => {
console.log('Opened cache:', cacheName);
return cache.addAll(assetsToCache);
}).then(() => self.skipWaiting()) // 새 서비스 워커 즉시 실행
);
});
새 서비스 워커가 적용되도록 하려면 사용자에게 페이지를 새로고침 하도록 알리고나 install 이벤트에서 skipWaiting() 메서드를 사용하여 새 서비스 워커를 즉시 활성화하도록 할 수 있다.
이전 서비스 워커를 사용하는 활성 클라이언트가 있는 경우 skipWaiting()을 사용하면 문제가 발생할 수 있다.
일반적으로 이전 서비스 워커가 더 이상 사용되지 않을때 새 서비스워커가 자연스럽게 활성화되도록 하는것이 좋다.
(6) 종료
서비스 워커는 사용되지 않을 때 리소스를 절약하기 위해 브라우저에 의해 종료될 수 있다.
브라우저는 새 이벤트가 발생할 떄와 같이 필요할 때 서비스 워커를 다시 시작한다.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then((registrations) => {
for (const registration of registrations) {
registration.unregister();
}
});
}
브라우저는 서비스워커의 생명주기를 자동으로 관리하므로 서비스 워커를 종료하는 것은 일반적이지 않다.
그러나 서비스 워커를 명시적으로 등록 취소해야 하는 경우 서비스 워커 자체 내부가 아니라 기본 JavaScript 실행 컨텍스트에서 등록을 취소할 수 있다.
먼저 서비스워커를 지원하는 브라우저인지 확인 후 navigator.serviceWorker.getRegistrations() 를 사용하여 현재 원본에 대한 모든 서비스 워커 등록 목록을 가져온 후 for문을 통해 모든 서비스 워커를 등록 해제한다.
5. PWA의 장단점
장점
(1) 플랫폼 간 호환성
PWA는 최신 웹 브라우저가 있는 모든 플랫폼에서 실행할 수 있으므로 Andriod 및 iOS와 같은 다양한 플랫폼에 대해 별도의 기본 앱을 개발할 필요가 없다.
(2) 개발 및 유지 관리 비용 절감
PWA는 웹 기술을 사용하므로 개발자는 기존 웹기술을 호라용하고 여러 플랫폼에 대한 단일 코드베이스를 유지 관리하여 개발 및 유지 관리 비용을 줄일 수 있다.
(3) 빠른 업데이트
PWA는 서버에서 직접 업데이트할 수 있으므로 기본 앱과 달리 사용자가 앱을 수동으로 업데이트 하지 않고도 최신 버전에 액세스 할 수 있다.
(4) 향상된 성능
PWA는 캐싱 및 기타 최적화 기술을 사용하여 로드 시간과 전반적인 성능을 개선할 수 있다.
(5) 오프라인 액세스
서비스 워커는 PWA가 오프라이 또는 불안정한 네트워크에서 작업할 수 있도록 허용한다.
이는 인터넷 연결이 좋지 않은 지역에서 특히 유용할 수 있다.
(6) 앱과 같은 경험
PWA는 장치의 홈 화면에 설치할 수 있으며 푸시 알림과 같은 기능을 지원하여 기본 앱과 유사한 사용자 경험을 제공한다.
단점
(1) 제한된 기능
PWA는 고급 카메라 제어 또는 특정 센서와 같은 모든 기본 장치 및 API에 액세스하지 못할 수 있다.
(2) 일관되지 않은 지원
최신 브라우저는 대부분의 PWA 기능을 지원하지만 다양한 브라우저 및 플랫폼 간에 구현이 다를 수 있다.
(3) 가시성 감소
PWA는 기본 앱과 같이 앱 스토어에 나열되지 않으므로 사용자가 PWA를 발견하기 어려울 수 있다.
(4) 잠재적으로 낮은 성능
경우에 따라 PWA는 게임이나 멀티미디어 처리와 같은 리소스 집약적인 작업에서 기본 앱만큼 성능이 좋지 않을 수 있다.
'개발 > 정리' 카테고리의 다른 글
[정리] 브라우저의 작동 방식 (0) | 2023.04.11 |
---|---|
[정리] 개발 방법론 (Water Fall vs Agile) (0) | 2023.04.07 |
[정리] MSA란 무엇일까? (Node.js 예시) (0) | 2023.03.08 |
[패턴] 자주 사용되는 아키텍처패턴 4가지 (0) | 2022.12.19 |
[정리] 해싱과 암호화 (0) | 2022.12.12 |