PWA에서 유용하게 사용할 수 있는 여러가지 기능의 사용법을 모아둔 것이다.
설치 버튼 추가
웹 앱 매니페스트 파일을 만든다. (manifest.json)
{
"short_name": "Demo App",
"name": "Demo App",
"icons": [
{
"src": "logo.png",
"type": "image/png",
"sizes": "192x192"
}
],
"start_url": ".",
"display": "standalone",
"orientation": "portrait-primary",
"theme_color": "#000000",
"background_color": "#ffffff",
"description": "Demo App 입니다.",
"dir": "ltr",
"lang": "ko-KR"
}
설치 버튼 추가
const [deferredPrompt, setDeferredPrompt] = useState(null);
const handleInstallBanner = () => {
if (deferredPrompt) {
deferredPrompt.prompt();
}
};
const handleBeforeInstallPrompt = (e) => {
e.preventDefault();
setDeferredPrompt(e);
};
useEffect(() => {
window.addEventListener("beforeinstallprompt", handleBeforeInstallPrompt);
return () => {
window.removeEventListener(
"beforeinstallprompt",
handleBeforeInstallPrompt,
);
};
}, []);
return (
<button onClick={handleInstallBanner}>
홈화면에 추가
</button>
)
Chrome에서 프로그레시브 웹 앱은 beforeinstallprompt
이벤트를 실행하고 브라우저 내 설치 프로모션을 표시하려면 먼저 다음 기준을 충족해야 합니다.
- 웹 앱이 아직 설치되지 않았습니다.
- 사용자 참여 휴리스틱을 충족합니다.
- 사용자가 페이지를 한 번 이상 클릭하거나 탭했어야 합니다 (이전 페이지가 로드 중일 때 포함).
- 사용자가 언제든지 페이지를 30초 이상 보는 데 머물러야 합니다.
- HTTPS를 통해 제공
- 다음을 포함하는 웹 앱 매니페스트 를 포함합니다.
short_name
또는name
icons
- 192픽셀 및 512픽셀 아이콘을 포함해야 함start_url
display
-fullscreen
,standalone
,minimal-ui
중 하나여야 합니다.prefer_related_applications
필드가 있거나false
이 아니어야 합니다.
참고: https://web.dev/articles/install-criteria?hl=ko
앱 타이틀 색상 변경
<meta name="theme-color" content="red" />
service worker 워커 변경 감지
서비스 워커가 변경되었을 경우 updatefound 이벤트를 통해 알아낼 수 있다.
self.registration.addEventListener("updatefound", (event) => {
const newSW = self.registration.installing;
newSW.addEventListener("statechange", (event) => {
if (newSW.state == "installed") {
}
});
});
display mode 가져오기
앱을 브라우저로 띄웠는지 standalone 모드(App)로 띄웠는지 체크하는 로직
const getLaunchDisplayMode = () => {
let displayMode = "browser";
if (window.matchMedia("(display-mode: standalone)").matches) {
displayMode = "standalone";
}
return displayMode;
}
displayMode = browser or standalone 이 나온다.
여기서 브라우저에서 standalone으로 전환을 한 경우 displayMode가 browser로 그대로 표시된다. 그래서 아래의 전송 감지 기능을 사용해야 한다.
전송 감지
브라우저와 앱 간의 전환을 감지하려면 미디어 쿼리를 사용한다.
window
.matchMedia("(display-mode: standalone)")
.addEventListener("change", (evt) => {
let displayMode = "browser";
if (evt.matches) {
displayMode = "standalone";
}
// Log display mode change to analytics
console.log("DISPLAY_MODE_CHANGED", displayMode);
});
앱 설치 이벤트
앱 설치가 되면 app installed 이벤트가 실행된다.
window.addEventListener('appinstalled', () => {
// If visible, hide the install promotion
hideInAppInstallPromotion();
// Log install to analytics
console.log('INSTALL: Success');
});
미디어 쿼리
/* It targets only the app used within the browser */
@media (display-mode: browser) {
}
/* It targets only the app used with a system icon in standalone mode */
@media (display-mode: standalone) {
}
/* It targets only the app used with a system icon in all mode */
@media (display-mode: standalone), (display-mode: fullscreen), (display-mode: minimal-ui) {
}
파일 시스템 읽기
파일을 찾아서 파일의 내용을 읽어들인다.
[app에서만 실행 가능, 브라우저에서 실행 불가]
const openFile = async () => {
const [handle] = await window.showOpenFilePicker();
// Get the File object from the handle.
const file = await handle.getFile();
// Get the file content.
// Also available, slice(), stream(), arrayBuffer()
const content = await file.text();
console.log("content", content);
};
파일 시스템 쓰기
파일의 내용을 수정한다. 위의 파일 시스템을 읽어서 내용 뒤에 modified라고 추가하자.
[app에서만 실행 가능, 브라우저에서 실행 불가]
const openFile = async () => {
const [handle] = await window.showOpenFilePicker();
// Get the File object from the handle.
const file = await handle.getFile();
// Get the file content.
// Also available, slice(), stream(), arrayBuffer()
const content = await file.text();
console.log("content", content);
const writable = await handle.createWritable();
// Write the contents of the file to the stream.
let contents = content + " modified";
await writable.write(contents);
// Close the file and write the contents to disk.
await writable.close();
};
연락처 정보 가져오기
const getContacts = async () => {
const properties = ["name", "email", "tel"];
const options = { multiple: true };
try {
const contacts = await navigator.contacts.select(properties, options);
console.log(contacts);
} catch (ex) {
// Handle any errors here.
console.error(ex);
}
};
절전 모드에 빠지지 않게 하기 (wake lock)
화면을 오래 보다 보면 절전모드로 빠질 수가 있다. 아래 스크립트를 적용하면 절전모드로 빠지지 않는다.
https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API
if ("wakeLock" in navigator) {
try {
console.log('"wakeLock" in navigator', "wakeLock" in navigator);
wakeLock = await navigator.wakeLock.request("screen");
console.log("Wake Lock is active!");
wakeLock.addEventListener("release", () => {
console.log(`Screen Wake Lock released: ${wakeLock.released}`);
});
} catch (err) {
// The Wake Lock request has failed - usually system related, such as battery.
console.log(`${err.name}, ${err.message}`);
}
}