프로젝트에서 타이머를 구현했는데 타이머가 종료되면 웹 푸쉬알림이 오도록 구현하고 싶었다. + 소리까지 나온다면 좋을듯! 해서 구현방법을 구글링했다.
PWA와 Notification API가 있었는데 타이머 기능은 팀 프로젝트에서 추가적인 기능이었고 특히나 푸쉬알림은 할 수 있다면 해보자!여서 최대한 간단하고 빠르게 구현이 가능한 것으로 해보기로 했다.
Notification API
사용자 에게 데스크톱 알림을 구성하고 표시하는 데 사용되는 api로,
이러한 알림의 모양과 구체적인 기능은 플랫폼마다 다르지만 일반적으로 사용자에게 비동기적으로 정보를 제공하는 방법을 제공한다.
이걸 참고해서 팀 프로젝트에도 적용해보았다.
export const triggerNotification = async (): Promise<"popup" | "push"> => {
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (!isMobile && Notification.permission === "granted") {
new Notification("타이머 종료", {
body: "타이머가 종료되었습니다!",
icon: "/images/mainLogo.svg"
});
return "push"; // 푸쉬 알림이 성공한 경우
} else {
return "popup"; // 토스트 알림이 필요한 경우
}
};
export const playSound = () => {
const audio = new Audio("/audio/5-Handoff.mp3");
audio.play();
};
조건문을 준 이유는 모바일일 경우 푸쉬알림이 뜨지 않아서 추가해주었다.
모바일까지 하려면 튜토리얼이 더 길어지는데 거기까지 하기엔 시간도 부족했고 메인기능이 아니어서 모바일 환경에서는 토스트 알림이 뜨도록 만들었다.
playSound 함수를 통해 시간이 종료되면 알림음이 뜨도록 만들어주었다.
import { playSound, triggerNotification } from "./Notification";
const Timer: React.FC = memo(() => {
const [popupMessage, setPopupMessage] = useState<string | null>(null);
// 타이머 실행 로직 + 알림음 팝업
useEffect(() => {
let timer: NodeJS.Timeout | null = null;
if (isRunning && timeLeft > 0) {
timer = setInterval(() => {
setTimeLeft((prevTime) => prevTime - INTERVAL);
}, INTERVAL);
} else if (timeLeft <= 0 && isRunning) {
clearInterval(timer!);
setIsRunning(false);
triggerNotification().then((type) => {
if (type === "popup") {
setPopupMessage("타이머가 종료되었습니다!");
}
});
playSound();
}
return () => {
if (timer) clearInterval(timer);
};
}, [isRunning, timeLeft]);
// 팝업 메시지 호출 (5초 뒤에 사라짐)
useEffect(() => {
if (popupMessage) {
const timeout = setTimeout(() => setPopupMessage(null), 5000);
return () => clearTimeout(timeout);
}
}, [popupMessage]);
// 중간 로직 생략
return (
<div className="fixed bottom-20 z-10 ssm:right-4 md:right-16">
{/* 팝업 메시지 호출 */}
{popupMessage && (
<div className="absolute right-0 top-[-50px] rounded-lg bg-Primary-300 p-3 text-white shadow-md">
{popupMessage}
</div>
)}
)
}
아까 만들었던 triggerNotification 함수를 실행하고 모바일 환경일 때는 토스트 알림이 보이도록 조건문을 넣어주었다.
그리고 playSound() 함수를 실행해 타이머 종료시 알림음이 울리도록 했다.
실행시켰는데 소리는 잘 들리지만 중요한 푸쉬알림이 보이지 않았다. 토스트 알림은 보이는 걸 보아서는 notification 코드에도 문제가 있는 것 같지는 않았다.
콘솔에도 granted 로 나오는 걸 보았을 때 이건 코드상의 문제가 아니라 내 컴퓨터의 알림설정과 관련이 있을 수 있겠다 생각이 들어 환경설정에 들어가보았다.
역시 크롬 알림 허용을 비활성화 해둔 상태였고 이를 다시 활성화 시킨 후 다시 실행시켜보았다.
크롬, 사파리 브라우저에서 모두 제대로 구현되는 걸 확인했고
모바일 환경에서도 토스트 알림이 잘 뜨고 있었다! 👏
생각보다 웹푸쉬알림이 간단하게 구현할 수 있어서 재미있고 신기했다.
시간 여유가 됐다면 모바일에서도 푸쉬알림이 나오게 구현해보고 싶다. 꼼꼼하게 알아보지 않았지만 PWA를 더 많이 사용하는 것 같은데 리펙토링을 하거나 다음에도 비슷한 기능을 구현해보게 된다면 PWA 를 사용해서 모바일/웹 모든 환경에서도 동일하게 푸쉬알림이 갈 수 있도록 해봐야겠다.
참고자료