본문 바로가기
🔥React 뽀개기

Expo 알림(Notification) 구현하기

by 짱돌보리 2025. 2. 25.
728x90

Expo 알림(Notification) 구현하기

📍구현 목표

- 앱 실행 시 위치 및 알림 권한 요청
- 하루에 세 번 운동 알림 보내기 (오전 10시, 오후 2시, 저녁 8시)
- (테스트용) 즉시 알림을 보내는 테스트 버튼

1. expo-notifications 설치

npx expo install expo-notifications

2. 앱 실행 시 권한 요청

시작 화면에서 권한 요청을 해야한다! 기존코드 위치 권한 아래에 알림 설정 코드를 넣었돠.

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

  const requestPermissions = async () => {
    // 위치 권한 요청
    const { status: notificationStatus } =
      await Notifications.requestPermissionsAsync()

    if (notificationStatus !== 'granted') {
      Alert.alert(
        '알림 권한 필요',
        '운동 알림을 받으려면 알림 권한이 필요합니다.',
        [{ text: '확인' }]
      )
    } else {
      await scheduleNotifications()
    }
  }

  useEffect(() => {
    requestPermissions()
  }, [])

⚠️ 알림 권한이 계속 거부되는 오류

위 코드를 작성했더니 왼쪽사진의 허용/허용 안 함의 alert가 아닌 오른쪽 사진처럼 이미 거부된 alert가 뜬다.

 

이 오류는 Notifications.requestPermissionsAsync()가 항상 denied 상태를 반환하는 문제로, Stack Overflow에서도 동일한 상황을 겪었던 것 같다.

https://stackoverflow.com/questions/74064903/expo-notifications-requestpermissionsasync-always-return-status-denied

 

Expo `Notifications.requestPermissionsAsync()` always return status denied

I’ve built a React native Expo internal android application in the apk format. I’ve setup the app and firebase for push notification correctly. But when I install the apk on an Android phone I alwa...

stackoverflow.com

🔍 원인

  • 사용자가 한 번 거부한 경우, 시스템이 다시 요청하지 않을 수 있음.
  • iOS에서는 설정에서 명시적으로 권한을 변경하지 않으면 다시 요청할 수 없음.
  • 안드로이드에서는 앱이 권한 요청을 트리거할 수 없는 경우가 발생할 수 있음.

✅ 해결 방법

  1. requestPermissionsAsync()를 호출하기 전에 getPermissionsAsync()로 현재 권한 상태를 먼저 확인하자..!
    -> 해도 여전히 거부 alert가 뜸...ㅠ!
  1. 사용자가 직접 설정 화면으로 이동해 권한을 변경할 수 있도록 안내하기
    -> 위가 일시적 오류일 수도 있기 때문에 일단 두고, alert부분에서 사용자가 직접 알림 권한을 설정할 수 있도록 했돠..!
Alert.alert(
  '알림 권한을 허용해주세요!',
  '앱 설정에서 권한을 직접 변경해야 합니다.',
  [{ text: '설정으로 이동', onPress: () => Linking.openSettings() }]
);

 

3. 특정 시간에 알림 보내기 (스케줄링) ⏰

매일 오전 10시, 오후 3시, 오후 8시에 알림이 반복적으로 발생하도록 설정했다! (따로 utils 폴더에 함수 분리해줌!)

import * as Notifications from 'expo-notifications'

export const scheduleNotifications = async () => {
  const notificationSchedules = [
    { hour: 10, message: '🌅 굿모닝! 오늘도 헬스장 가야죠? 💪🏻' },
    { hour: 15, message: '오늘 운동을 완료했나요? 💪🏻' },
    { hour: 20, message: '오늘 운동을 완료했나요? 💪🏻' },
  ]

  for (const { hour, message } of notificationSchedules) {
    await Notifications.scheduleNotificationAsync({
      content: {
        title: '🏋️ 운동할 시간!',
        body: message,
        sound: 'default',
      },
      trigger: {
        type: 'daily',
        hour,
        minute: 0,
        repeats: true,
      } as Notifications.DailyTriggerInput,
    })
  }
}

4. 알림 테스트하기

테스트용으로 즉시 알림을 보내는 함수를 만들었다. 버튼에 함수를 연결하고 테스트해보면 작동이 잘 된다!

export const testNotification = async () => {
  await Notifications.scheduleNotificationAsync({
    content: {
      title: '🚀 테스트 알림',
      body: '테스트 알림입니다!',
      sound: 'default',
    },
    trigger: null, // 즉시 실행
  });
};

 

최종코드

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


  const requestPermissions = async () => {
    // 알림 권한 요청
    const { status: existingNotificationStatus } =
      await Notifications.getPermissionsAsync()

    if (existingNotificationStatus !== 'granted') {
      const { status: notificationStatus } =
        await Notifications.requestPermissionsAsync()

      if (notificationStatus !== 'granted') {
        Alert.alert(
          '알림 권한 필요',
          '운동 알림을 받으려면 알림 권한이 필요합니다.',
          [{ text: '설정으로 이동', onPress: () => Linking.openSettings() }]
        )
        return
      }
      await scheduleNotifications()
    }
  }

  useEffect(() => {
    requestPermissions()
  }, [])