🛠 문제 상황

기본적으로 **SSE(EventSource)**를 사용하여 실시간 알림을 구현했지만,

사용자가 접속하지 않은 상태에서 발생한 알림을 받을 수 없는 문제가 발생함.

  1. 초기 구현 방식 및 문제 발생 원인

🔍 해결 방법

백엔드에 API 추가하여, 접속하지 않은 동안 발생한 알림을 조회할 수 있도록 구현

  1. 알림을 DB에 저장
  2. 클라이언트에서 API 호출하여 missed notifications 조회

✅ 적용 코드

📌 1. useFetchMissedNotifications 구현 (백엔드 API 연동)

📌 2. SideMenu.tsx에서 API 데이터 가져와서 상태 업데이트

const { data: missedNotifications } = useFetchMissedNotifications();

useEffect(() => {
  if (missedNotifications?.notifications) {
    const formattedNotifications: NotificationProp[] =
      missedNotifications.notifications.map((notification) => ({
        notificationId: notification.notificationId,
        type: notification.type,
        message: notification.message,
        senderNickname: notification.sender.nickname,
        senderProfileUl: notification.sender.profileUrl,
        timestamp: notification.createdAt,
        isRead: notification.isRead,
      }));

    setMessages(formattedNotifications); 
  }
}, [missedNotifications]

📌 3. SSE를 통한 실시간 알림 수신 (기존 코드 유지)

useEffect(() => {
  if (!token || messages.length === 0) return;
  const eventSource = new EventSourcePolyfill(
    `${import.meta.env.VITE_BASE_SERVER_URL}/notifications/stream`,
    {
      headers: { Authorization: `Bearer ${token}` },
      withCredentials: true,
    }
  );
  eventSource.addEventListener('message', (event) => {
    const data: NotificationProp = JSON.parse(event.data);
    setMessages((prevMessages) => [...prevMessages, data]); 
    setNewNotification(true);
  });
  eventSource.addEventListener('error', () => {
    eventSource.close();
  });
  return () => {
    eventSource.close();
  };
}, [token]);

📌 4**. 백엔드: 읽지 않은 알림 조회 서비스 로직**

async getUnreadNotifications(userId: number) {
    // 1. 읽지 않은 알림 조회
    const unreadNotifications = await this.prisma.notification.findMany({
      where: {
        userId: userId,
        isRead: false,
      },
      include: {
        sender: {
          select: {
            nickname: true,
            profile_url: true,
          },
        },
      },
      orderBy: {
        createdAt: 'desc',
      },
    });
    // 2. 데이터를 변환하여 반환
    const transformedNotifications = unreadNotifications.map(notification => {
      const transformedNotification = {
        notificationId: notification.id, // 
        userId: notification.userId,
        senderId: notification.senderId,
        type: notification.type,
        message: notification.message,
        isRead: notification.isRead,
        createdAt: notification.createdAt,
        sender: {
          nickname: notification.sender.nickname,
          profileUrl: notification.sender.profile_url, // `profile_url` -> `profileUrl`
        },
      };
      return transformedNotification;
    });
    return {
      notifications: transformedNotifications,
    };
  }

🎯 결과