모바일 앱을 만들다 보면 사용자에게 중요한 정보를 전달하거나, 앱을 다시 사용하도록 유도하기 위해
푸시 알림
기능을 떠올리게 됩니다. 사용자가 앱을 사용하고 있지 않을 때도 메시지를 보낼 수 있는 강력한 기능이죠. 하지만 처음 설정하려면 조금 복잡하게 느껴질 수 있습니다.
오늘은 Expo를 사용하여 React Native 앱을 개발할 때, 이 푸시 알림 기능을 어떻게 설정하고 사용하는지 차근차근 알아보려 합니다. 필요한 도구를 설치하는 것부터 시작해서, 사용자에게 알림 허락을 받고, 고유한 토큰을 발급받아 서버에서 알림을 보내고, 앱에서 알림을 받는 전체 과정을 상세히 다룰 예정입니다.
expo-notifications 라이브러리는 사용자의 권한을 요청하고 푸시 알림을 전송하기 위한 ExpoPushToken을 가져오는 데 사용됩니다.
expo-device는 앱이 실제 기기에서 실행 중인지 확인하는 데 사용됩니다.
expo-constants는 앱 구성에서 projectId 값을 가져오는 데 사용됩니다.
을 얻을 차례입니다. 이 토큰은 Expo 푸시 서비스를 통해 특정 기기로 알림을 보내는 데 사용되는 고유한 주소와 같습니다.
Notifications.getExpoPushTokenAsync() 함수를 호출하면 토큰 객체를 얻을 수 있고, 그 안의 data 속성이 실제 토큰 값입니다. (위 registerForPushNotificationsAsync 함수 예시에 포함되어 있습니다.)
이렇게 얻은 푸시 토큰은 여러분의 백엔드 서버로 전송하여 저장해야 합니다. 어떤 사용자가 어떤 토큰을 가지고 있는지 매핑해두어야 나중에 특정 사용자에게 푸시 알림을 보낼 수 있습니다.
토큰은 사용자가 앱을 삭제 후 재설치하거나 기기를 변경하면 바뀔 수 있으므로, 앱 실행 시마다 확인하고 필요한 경우 서버에 업데이트하는 로직이 필요할 수 있습니다.
앱이 사용자 눈앞에서 실행 중일 때 알림이 도착하면, 기본적으로는 시스템 알림이 뜨지 않습니다. 하지만 Notifications.addNotificationReceivedListener를 사용하면 알림 데이터를 받아 앱 내에서 원하는 방식으로 처리할 수 있습니다. (예: 인앱 메시지 표시)
사용자가 받은 푸시 알림(앱이 꺼져있거나 백그라운드 상태에서 받은 시스템 알림)을 탭했을 때 어떤 동작을 할지 정의해야 합니다. 예를 들어 특정 화면으로 이동시키거나, 관련 데이터를 보여주는 등의 작업입니다.
이때는 Notifications.addNotificationResponseReceivedListener를 사용합니다. 이 리스너는 사용자가 알림과 상호작용(탭)했을 때 호출되며, 콜백 함수의 인자로 전달되는 response 객체를 통해 알림 정보(response.notification)에 접근할 수 있습니다. 특히 response.notification.request.content.data 안에 서버에서 보낸 추가 데이터가 들어있으므로, 이를 활용하여 분기 처리를 할 수 있습니다. (위 App 컴포넌트 예시 코드 참고)
앱이 완전히 꺼져 있거나 백그라운드 상태일 때 푸시 알림이 도착하면, 운영체제(iOS/Android)의 기본 알림 시스템에 의해 화면 상단이나 잠금 화면 등에 표시됩니다. 이때 알림이 어떻게 보일지(소리, 뱃지, 알림 센터 표시 등)는 Notifications.setNotificationHandler를 통해 앱 시작 시 미리 설정해 둔 방식에 따라 결정됩니다.
지금까지 설명한 내용은 주로 Expo Go 앱 환경에서의 테스트를 가정한 것입니다.
하지만 여러분의 앱을 스토어에 출시하기 위한
독립 실행형 앱(Standalone App)
을 빌드하려면, 각 플랫폼(Android, iOS)별 네이티브 푸시 알림 설정을 반드시 완료해야 합니다.
이 설정을 하지 않으면 실제 배포된 앱에서는 푸시 알림이 동작하지 않습니다!
Expo에서는 EAS(Expo Application Services)를 통해 이 과정을 간편하게 처리할 수 있도록 돕습니다.
Android
-> Google Service Account
-> Manage your Google Service Account for Push Notifications (FCM V1)
-> Set up a Google Service Account Key for Push Notifications (FCM V1)
-> google account 선택
이렇게 업로드된 자격 증명은 EAS Build 시 자동으로 앱에 포함됩니다.
app.json 설정 확인
app.json 파일 내에 android.googleServicesFile 경로가 올바르게 지정되어 있는지 확인합니다.
앱의 고유 식별자인 Bundle Identifier를 설정하고 Apple Developer 사이트에 등록해야 합니다 (app.json의 ios.bundleIdentifier).
Push Notifications 기능 활성화
Apple Developer 사이트에서 해당 앱 ID(Bundle Identifier)에 대해 Push Notifications 기능을 활성화합니다.
APNs 인증 키 생성 (.p8 파일)
Apple Developer 사이트의 'Certificates, Identifiers & Profiles' > 'Keys' 섹션에서 'Apple Push Notifications service (APNs)' 기능이 활성화된 새로운 키를 생성하고 .p8 파일을 다운로드합니다. (인증서 방식(.p12)도 가능하지만, 키 방식(.p8)이 더 권장됩니다.) Key ID와 Team ID도 함께 기록해 둡니다.
EAS CLI로 자격 증명 업로드
프로젝트 디렉토리에서 EAS CLI를 사용하여 다운로드한 .p8 파일과 관련 정보(Key ID, Team ID, Bundle Identifier)를 Expo 서버에 업로드합니다.
eas credentials # 명령 실행 후 플랫폼(ios) 선택, 안내에 따라 Push Key (.p8) 파일 경로 및 관련 정보 입력
마찬가지로 이 자격 증명은 EAS Build 시 사용됩니다.
app.json 설정 확인
ios.bundleIdentifier가 Apple Developer 사이트에 등록된 값과 일치하는지, ios.usesAppleSignIn 등의 설정이 올바른지 확인합니다. ios.entitlements 에 aps-environment 값이 'development' 또는 'production'으로 빌드 환경에 맞게 설정되는지도 중요합니다. (EAS Build가 관리해주는 경우가 많음)
네이티브 설정은 다소 복잡해 보일 수 있지만, Expo 문서와 EAS CLI의 안내를 잘 따르면 충분히 완료할 수 있습니다.
실제 서비스에서는 여러분의 백엔드 서버에서 특정 이벤트가 발생했을 때(예: 새로운 메시지 도착, 공지사항 등록 등) 사용자에게 푸시 알림을 보내야 합니다. 이를 위해서는 Expo 푸시 서비스 API로 HTTP POST 요청을 보내야 합니다.
다음은 Node.js 환경에서 fetch API를 사용하여 푸시 알림을 보내는 간단한 예시입니다.
// 이 코드는 백엔드 서버(Node.js 등)에서 실행되어야 합니다.// 절대로 앱 클라이언트 코드에 API 키나 토큰을 직접 넣지 마세요!const sendPushNotification = async (expoPushToken, title, body, data) => { const message = { to: expoPushToken, // 알림을 받을 대상 토큰 (배열로 여러 명에게 동시 전송 가능) sound: 'default', // 알림 도착 시 소리 ('default' 또는 null) title: title, // 알림 제목 body: body, // 알림 본문 data: data, // 앱에서 추가적으로 사용할 데이터 (JSON 객체) }; try { const response = await fetch('https://exp.host/--/api/v2/push/send', { method: 'POST', headers: { 'Accept': 'application/json', 'Accept-encoding': 'gzip, deflate', 'Content-Type': 'application/json', // 'Authorization': `Bearer YOUR_EXPO_ACCESS_TOKEN` // Access Token 방식 사용 시 (선택 사항) }, body: JSON.stringify(message), }); const result = await response.json(); console.log('Push notification sent:', result); // result 객체 예시: { data: { status: 'ok', id: '...' } } // status가 'error'인 경우 details 객체에 오류 정보 포함 // } catch (error) { console.error('Error sending push notification:', error); }};// 사용 예시: 저장된 사용자 토큰으로 알림 보내기const userToken = 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]'; // 실제 사용자 토큰 사용sendPushNotification( userToken, '새로운 메시지', '친구가 메시지를 보냈습니다!', { messageId: 123, sender: '친구A' } // 앱에서 활용할 추가 데이터);
🖐️
Expo Access Token을 사용하는 경우 등 민감한 정보는 절대로 앱 클라이언트 코드에 포함하면 안 됩니다. 항상 보안이 확보된 백엔드 서버에서 API 요청을 보내야 합니다.
expo-notifications 라이브러리는 알림을 더 효과적으로 사용하기 위한 추가 기능들도 제공합니다.
안드로이드 채널 설정
: Android 8.0 (Oreo) 이상에서는 알림 채널(Channel)을 설정해야 사용자가 알림 종류별로 설정을 관리할 수 있습니다. Notifications.setNotificationChannelAsync() 함수를 사용하여 채널 ID, 이름, 중요도 등을 설정할 수 있습니다. (위 registerForPushNotificationsAsync 예시 참고)
iOS 뱃지 카운트 관리
: 앱 아이콘에 표시되는 빨간색 숫자 뱃지를 관리할 수 있습니다. Notifications.getBadgeCountAsync()로 현재 뱃지 수를 가져오고, Notifications.setBadgeCountAsync()로 뱃지 수를 설정할 수 있습니다. (서버와 동기화하는 로직 필요)
알림 예약
: Notifications.scheduleNotificationAsync()를 사용하면 특정 시간 후에 또는 주기적으로 로컬 알림을 보낼 수 있습니다. (푸시 알림과는 다른 로컬 알림 기능)