expo-notifications

HongBoogie·2025년 5월 26일

서론

모바일 앱에 필수적인 요소는 몇 가지가 있지만, 그중 알림 기능은 없으면 서운하다고 생각한다.

알림을 통해 사용자에게 필요한 정보를 보낼 수도 있고, 접속률도 높일 수 있으니까.

expo-notifications 라이브러리 없이도 푸시 알림을 구현하는것은 가능하지만
해당 라이브러리를 이용하면 좋은 점이 몇개 있는데,

  1. 기존의 Androids, iOS 환경에 따라 다르게 구현해야 할 알림 전송 기능을 하나로 퉁칠 수 있다.
    개발자가 해야 할 것은 expo push token을 받아서 저장하는 것과 자격 증명을 하는 것이다.
  2. 혹은 앱이 foreground(현재 사용자가 사용중인 상태), background(백그라운드 혹은 꺼져있는 상태) 일때의 처리를 쉽게 구현할 수 있다.

한 가지 주의할 점은, 실제로 알림이 오는지 테스트할 땐 시뮬레이터 말고 실기기를 사용해야한다.
시뮬레이터는 토큰이 안온다.

핵심 로직

알림 허용 및 푸시토큰 받기

import * as Device from "expo-device";
import * as Notifications from "expo-notifications";
import Constants from "expo-constants";

import { Platform } from "react-native";

function handleRegistrationError(errorMessage: string) {
  alert(errorMessage);
  throw new Error(errorMessage);
}

export async function RegisterForPushNotificationsAsync() {
  if (Platform.OS === "android") {
    Notifications.setNotificationChannelAsync("default", {
      name: "default",
      importance: Notifications.AndroidImportance.MAX,
      vibrationPattern: [0, 250, 250, 250],
      lightColor: "#FF231F7C",
    });
  }

  if (Device.isDevice) {
    const { status: existingStatus } =
      await Notifications.getPermissionsAsync();
    let finalStatus = existingStatus;

    if (existingStatus !== "granted") {
      const { status } = await Notifications.requestPermissionsAsync();
      finalStatus = status;
    }
    if (finalStatus !== "granted") {
      handleRegistrationError(
        "Permission not granted to get push token for push notification!"
      );
      return;
    }
    const projectId =
      Constants?.expoConfig?.extra?.eas?.projectId ??
      Constants?.easConfig?.projectId;

    if (!projectId) {
      handleRegistrationError("Project ID not found");
    }

    try {
      const pushTokenString = (
        await Notifications.getExpoPushTokenAsync({
          projectId,
        })
      ).data;
      console.log(pushTokenString);
      return pushTokenString;
    } catch (e: unknown) {
      handleRegistrationError(`${e}`);
    }
  } else {
    handleRegistrationError("Must use physical device for push notifications");
  }
}

푸시 알림 허용과 함께 푸시 토큰을 받아오는 코드다. 공식 문서에서 잘 짜줬으니 그대로 사용하면 된다.
여기서 projectId는 말 그대로 내 프로젝트 id이다.

이렇게 받아온 expo push token은 https://expo.dev/notifications에서 테스트하거나, 서버에 건네서 알람을 보내게 하는 용도로 사용할 수 있다.

시스템 알람 처리

앱을 사용 중일땐 기본적으로 시스템 알람이 뜨지 않는다.

Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldPlaySound: true,
    shouldSetBadge: true,
    shouldShowBanner: true,
    shouldShowList: true,
    shouldShowAlert: true,
  }),
});

이런식으로 선언해주면 앱을 사용중일때도 시스템 알람처럼 작동한다!
또한 알람을 받았을 때 내가 원하는 형태로 보여주는 것도 가능한데,

  const [notification, setNotification] = useState<
    Notifications.Notification | undefined
  >(undefined);

notification 변수를 선언해주고,

 const notificationListener = Notifications.addNotificationReceivedListener(
      (notification) => {
        setNotification(notification);
      }
    );

Notifications.addNotificationReceivedListener 메서드를 이용해 저장한 후, 데이터를 이용해 원하는 형태로 띄우는 것도 가능할 것이다.

알람을 눌렀을 때

알람을 눌렀을 때 어떤 행동을 할지에 대해서도 정해야 한다. 가령, 데이터를 담은 모달을 띄운다던가, 앱 내의 어딘가로 이동시킨다던가(Deep Link) 등의 행동이 있을 것 같다.

import { useEffect } from 'react';
import * as Notifications from 'expo-notifications';
import { router } from 'expo-router';

function useNotificationObserver() {
  useEffect(() => {
    let isMounted = true;

    function redirect(notification: Notifications.Notification) {
      const url = notification.request.content.data?.url;
      if (url) {
        router.push(url);
      }
    }

    Notifications.getLastNotificationResponseAsync()
      .then(response => {
        if (!isMounted || !response?.notification) {
          return;
        }
        redirect(response?.notification);
      });

    const subscription = Notifications.addNotificationResponseReceivedListener(response => {
      redirect(response.notification);
    });

    return () => {
      isMounted = false;
      subscription.remove();
    };
  }, []);
}

export default function Layout() {
  useNotificationObserver();

  return <Slot />;
}

expo router를 이용할 때의 예제인데, 실제 우리 앱에선 특정 링크를 알림이 담고 있다면 웹뷰에 해당 url을 부여하는 식으로 구현이 가능할 것 같다는 생각이 든다.

profile
개발이 즐겁고 노는게 즐거워요

0개의 댓글