nest

백그라운드 알람....

PGI 2025. 3. 6. 18:28
반응형

웹에서 백그라운드 알람을 하려면 2가지 방법이 있다..

참고로 아래있는거는 ai한테 기본적인거는 가져왓으니 대충봐라... 코드만... 보면 ㅠㅠㅠ 

1. FCM(Firebase Cloud Messaging)

FCM은 Google에서 제공하는 크로스 플랫폼 메시징 솔루션으로, 안정적이고 배터리 효율적인 방식으로 메시지를 전송할 수 있습니다.

 

작동 방식

  1. 서비스 워커 등록: 브라우저에서 Service Worker를 등록하고 FCM 토큰을 받습니다.
  2. 토큰 저장: 서버에 토큰을 저장하여 특정 사용자에게 메시지를 보낼 수 있도록 합니다.
  3. 메시지 전송: 서버는 FCM API를 통해 메시지를 전송합니다.
  4. 알림 표시: 서비스 워커가 메시지를 수신하고 브라우저 알림을 표시합니다.

장점

  • 배터리 효율성: 지속적인 연결이 필요 없어 배터리 소모가 적습니다.
  • 신뢰성: Google의 인프라를 활용하여 안정적인 메시지 전달이 가능합니다.
  • 크로스 플랫폼: 웹, Android, iOS 등 다양한 플랫폼에서 동일한 시스템 사용 가능합니다.
  • 백그라운드 지원: 브라우저가 닫혀있을 때도 알림 전송이 가능합니다.
  • 확장성: 수백만 사용자에게도 메시지를 효율적으로 전송할 수 있습니다.

단점

  • 지연 시간: 실시간성이 웹소켓보다 떨어질 수 있습니다.
  • 제한적인 데이터 크기: 메시지 페이로드 크기에 제한이 있습니다(4KB).
  • 외부 의존성: Google 서비스에 의존하게 됩니다.
  • 설정 복잡성: 초기 설정이 상대적으로 복잡할 수 있습니다.

2. 웹소켓(WebSocket)

웹소켓은 단일 TCP 연결을 통해 전이중 통신 채널을 제공하는 프로토콜입니다.

 

작동 방식

  1. 연결 수립: 클라이언트와 서버 간 웹소켓 연결을 수립합니다.
  2. 지속적 연결: 연결은 명시적으로 종료될 때까지 유지됩니다.
  3. 양방향 통신: 서버와 클라이언트는 언제든지 데이터를 주고받을 수 있습니다.
  4. 알림 표시: 서버로부터 메시지를 받으면 JavaScript를 사용하여 브라우저 알림을 표시합니다.

장점

  • 실시간성: 지연 시간이 매우 짧아 즉각적인 알림 전송이 가능합니다.
  • 양방향 통신: 클라이언트와 서버 간 자유로운 데이터 교환이 가능합니다.
  • 유연성: 메시지 형식과 프로토콜을 자유롭게 정의할 수 있습니다.
  • 독립성: 외부 서비스에 대한 의존성이 없습니다.
  • 데이터 크기 제한 없음: FCM보다 큰 데이터를 전송할 수 있습니다.

단점

  • 배터리 소모: 지속적인 연결 유지로 배터리 소모가 많습니다.
  • 연결 관리 필요: 연결 끊김, 재연결 처리 등 추가 로직이 필요합니다.
  • 백그라운드 제한: 브라우저가 닫히면 연결이 끊어지므로 백그라운드 알림이 어렵습니다.
  • 확장성 문제: 많은 동시 연결을 처리하려면 서버 자원이 많이 필요합니다.
  • 방화벽 문제: 일부 네트워크 환경에서 웹소켓 연결이 차단될 수 있습니다.

머 이렇다고 하는데 보통 요즘 하이브리드로 한다고 한다.. 근데 나는 웹포폴이기때문에 fcm은 오버 스팩이 될수 있기때문에 웹소캣을 써보려고 한다... 

 

프론트는 의외로 간단했다. 코드로 보자면 저런데 의외로 길엇네.....

// 알람 권한 요청
  const requestNoti = useCallback(async () => {
    if (Notification.permission === 'granted') return true;
    try {
      const permission = await Notification.requestPermission();
      return permission === 'granted';
    } catch (error) {
      if (process.env.NODE_ENV === 'development') {
        console.log('알림 권한 요청 실패', error);
      }
      return false;
    }
  }, []);
  // 브라우저 알림 처리
  const browserNoti = useCallback(async (newNoti: INoti) => {
    if (
      getNotiMessageType(newNoti.message) &&
      Notification.permission === 'granted'
    ) {
      new Notification('멘토링 알람', {
        body: newNoti.message,
        icon: '/frontend/public/favicon.svg',
      });
    }
  }, []);
  // 소캣 연결 해봅시다.
  useEffect(() => {
    // 브라우저 알림 권한 요청
    requestNoti();
    // 소캣연결
    const connectSocket = () => {
      const newSocket = io(`${process.env.NEXT_PUBLIC_API_BASE_URL}/noti`, {
        withCredentials: true,
        reconnection: true,
        reconnectionAttempts: 2,
        reconnectionDelay: 1000,
      });
      newSocket.on('connect', () => {
        if (process.env.NODE_ENV === 'development') {
          console.log('알림 소켓 연결 성공');
        }
        setError(null);
      });
      newSocket.on('disconnect', () => {
        if (process.env.NODE_ENV === 'development') {
          console.log('알림 소켓 연결 해제');
        }
      });
      // 알림 오류
      newSocket.on('error', (error) => {
        if (process.env.NODE_ENV === 'development') {
          console.error('알림 소켓 연결 해제 오류:', error);
        }
        setError(error.message);
      });
      // 알람 수신 처리
      newSocket.on('noti', (newNoti: INoti) => {
        // 중요메시지 브라우저 알림 표시
        browserNoti(newNoti);
        // 알림 데이터 갱신
        queryclient.invalidateQueries({ queryKey: ['noti'] });
      });
      return newSocket;
    };
    const socketInstance = connectSocket();
    // 클린업 함수
    return () => {
      socketInstance.disconnect();
    };
  }, [queryclient, browserNoti, requestNoti]);

  // 알림 목록 데이터
  const notifications = notification?.item || [];

  // 브라우저 알림 대상 알림 식별
  const browserNotifications = notifications.filter((noti: INoti) =>
    getNotiMessageType(noti.message)
  );

 

일단 프론트는 저렇다는데 음... 역시 프론트보단 백이 더 힘든거 같다... ㅠㅠ 

반응형