참고 링크

Push Notifications with Firebase in React.js

현재 코드에서 onMessageListener 함수가 새 알림 메시지가 수신되면 해당 메시지의 제목과 내용을 notification state에 저장하고, 해당 state가 업데이트될 때 notify() 함수를 호출하여 토스트 알림을 보여주도록 구현되어 있습니다.

따라서, Notification 컴포넌트는 이미 알림이 오면 notification state가 업데이트되어 토스트 알림이 표시되도록 되어 있습니다. ⇒ 그런데 왜 안될까? 🤷🏻‍♀️

//firebase.ts
import { initializeApp } from 'firebase/app';
import { getMessaging, getToken, onMessage } from 'firebase/messaging';
import { VAPID_KEY } from '../constant/union';

const firebaseConfig = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
  appId: import.meta.env.VITE_FIREBASE_APP_ID,
  measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID,
};

const app = initializeApp(firebaseConfig);
export const foregroundMessaging = getMessaging(app);

export async function getFcmToken() {
  console.log('권한 요청 중...');

  const permission = await Notification.requestPermission();
  if (permission === 'denied') {
    console.log('알림 권한 허용 안됨');
    return;
  }

  console.log('알림 권한이 허용됨');

  const token = await getToken(foregroundMessaging, {
    vapidKey: VAPID_KEY,
  });
  return token;
}

// onMessage 이벤트가 발생할 때마다 payload 인자를 Promise의 해결 값으로 반환 => 앱에서 FCM 푸시 알림 메시지 처리 O
// 이 함수를 사용하여 앱이 포그라운드에 있을 때 FCM에서 수신된 푸시 알림 메시지를 처리할 수 있습니다. => 왜 안됨?
export const onMessageListener = () =>
  new Promise((resolve) => {
    onMessage(foregroundMessaging, (payload) => {
      console.log('payload', payload);
      resolve(payload);
    });
  });
// foregroundNotification.tsx
import { useEffect, useState } from 'react';
import toast, { Toaster } from 'react-hot-toast';
import { getFcmToken, onMessageListener } from './firebase';

// 앱에서 푸시 알림을 표시하는 컴포넌트
const foregroundNotification = () => {
  const [notification, setNotification] = useState({ title: '', body: '' });
  const notify = () => toast(<ToastDisplay />);

  function ToastDisplay() {
    return (
      <div>
        <p>
          <b>{notification?.title}</b>
        </p>
        <p>{notification?.body}</p>
      </div>
    );
  }

  useEffect(() => {
    getFcmToken();
  }, []);

// (parameter) payload: unknown 개체가 '알 수 없는' 형식입니다.
  useEffect(() => {
    onMessageListener()
      .then((payload) => {
        setNotification({ title: payload?.notification?.title, body: payload?.notification?.body });
      })
      .catch((err) => console.log('failed: ', err));
  }, []);

  useEffect(() => {
    if (notification?.title) {
      notify();
    }
  }, [notification]);

  return <Toaster />;
};