PWA 페이지에서 앱 설치를 유도해보기 (feat. React)

202302월 15



beforeinstallprompt는 무엇일까?


beforeinstallprompt 사용자가 디바이스에 설치할 수 있는 프로그레시브 웹 앱(PWA)을 방문할 때 브라우저에서 실행되는 이벤트입니다. 이 이벤트는 앱이 사용자에게 앱을 디바이스에 독립 실행형 애플리케이션으로 설치하라는 메시지를 표시할 기회를 제공합니다.

이벤트가 트리거 되면 브라우저는 일반적으로 사용자에게 앱을 설치할지 여부를 묻는 메시지를 표시합니다. 이벤트는 앱당 한 번만 트리거 되며, 앱의 자바스크립트 코드에서 window.addEventListener() 메소드를 사용하여 이벤트를 캡처하고 처리할 수 있습니다.

간단하게 말해서 사용자가 pwa로 만들어진 웹 페이지를 방문했을 때 기존에 설치하려면 크롬 같은 경우에는 따로 설정을 눌러서 앱 설치 버튼을 눌러야 하지만 beforeinstallprompt를 사용하면 브라우저가 설치를 안 한 사용자 여부를 판단해 설치 버튼을 띄워주는 기능을 해줍니다. 저도 PWA 프로젝트 하나를 만들었는데 이 기능을 집어넣었습니다.



React에 적용해보기


addEventListener에 등록하기


useEffect(() => {
  window.addEventListener("beforeinstallprompt", handleBeforeInstallPrompt);

  return () => {
    window.removeEventListener(
      "beforeinstallprompt",
      handleBeforeInstallPrompt
    );
  };
}, []);

useEffect에 의존성에 빈 배열을 넣음으로써 마운트시 한 번만 실행되도록 설정합니다. 또한 메모리 누수를 방지하기 위해서 언 마운트시에는 이벤트리스너를 지워줍니다. 여기서 beforeinstallprompt는 사용자가 해당 앱 설치를 하지 않았을 때만 발생하는 리스너입니다.

const [deferredPrompt, setDeferredPrompt] = useState(null);

const handleBeforeInstallPrompt = (event) => {
  event.preventDefault();

  setDeferredPrompt(event);
};

beforeinstallprompt 리스너가 발동할 때 handleBeforeInstallPrompt 함수가 실행되면서 event를 억제하고 deferredPrompt state에 event 객체를 저장합니다. 이제 deferredPrompt 객체를 활용하면 사용자한테 설치 프롬프트를 띄워줄 수가 있습니다.

return (
  <div>
    {deferredPrompt && <button onClick={handleInstall}>Install</button>}
  </div>
);

deferredPrompt 객체가 존재할 때 null 상태가 아닐 때만 사용자한테 앱 설치 버튼을 띄워 줄 수 있습니다. 또한 button 태그의 onclick event를 handleInstall함수로 받게 합니다.

const handleInstall = () => {
  if (deferredPrompt) {
    deferredPrompt.prompt();

    deferredPrompt.userChoice.then((choiceResult) => {
      if (choiceResult.outcome === "accepted") {
        console.log("사용자가 앱 설치를 동의했습니다.");
      } else {
        console.log("사용자가 앱 설치를 동의하지 않았습니다.");
      }

      setDeferredPrompt(null);
    });
  }
};

handleInstall 함수에서는 우선 deferredPrompt event 객체가 존재하는지 여부를 판단 후 deferredPrompt.prompt()를 사용해 실제로 사용자한테 설치할지를 물어봅니다.

만약 설치한다고 응답을 보내면 앱을 설치하도록 실행합니다. deferredPrompt.userChoice.then()을 사용해서 사용자의 응답 여부를 기다립니다. choiceResult.outcome 값이 accepted일 경우 설치 동의했다는 뜻입니다. 마지막으로 deferredPrompt값을 비워줌으로써 앱 설치를 다시 띄워주지 않습니다.



실제 제 프로젝트에 적용


PWAInstallPrompt.tsx
import React, { useEffect, useState } from "react";
import Button from "../UIElements/Button";

import { RiInstallLine } from "react-icons/ri";

const PWAInstallPrompt = () => {
  const [deferredPrompt, setDeferredPrompt] = useState<any>(null);

  useEffect(() => {
    window.addEventListener("beforeinstallprompt", handleBeforeInstallPrompt);

    return () => {
      window.removeEventListener(
        "beforeinstallprompt",
        handleBeforeInstallPrompt
      );
    };
  }, []);

  const handleBeforeInstallPrompt = (event: Event) => {
    event.preventDefault();
    setDeferredPrompt(event);
  };

  const handleInstallClick = () => {
    if (deferredPrompt) {
      deferredPrompt.prompt();

      deferredPrompt.userChoice.then((choiceResult: { outcome: string }) => {
        if (choiceResult.outcome === "accepted") {
          console.log("사용자가 설치 프롬프트에 동의했습니다.");
        } else {
          console.log("사용자가 설치 프롬프트를 무시했습니다.");
        }

        setDeferredPrompt(null);
      });
    }
  };

  return (
    <React.Fragment>
      {deferredPrompt && (
        <Button size="large" onClick={handleInstallClick}>
          <RiInstallLine />
        </Button>
      )}
    </React.Fragment>
  );
};

export default PWAInstallPrompt;

위 코드는 제가 만든 프로젝트에 실제로 적용을 한 코드 전체입니다.

제가 만든 프로젝트 Click ME!!



참고 문서

자신만의 인앱 설치 경험을 제공하는 방법