logo
|
Blog

    [ 개인플젝 ] Android 푸시 알림 (React Native + OneSignal + Supabase)

    김보람's avatar
    김보람
    Jan 21, 2026
    [ 개인플젝 ] Android 푸시 알림 (React Native + OneSignal + Supabase)
    Contents
    기술 스택전체 구조 요약1. OneSignal 기본 설정 (Android)(1) OneSignal App 생성(2) OneSignal App ID 확인2. React Native에 OneSignal 연동(1) 설치(2) App.tsx 초기화(3) 로그인 성공 시 External ID 연결3. Supabase: 오늘 인증 안 한 유저 조회 RPC4. Supabase Edge Function에서 푸시 발송(1) Edge Function 생성(2) Edge Function Secrets 설정Edge Function 코드5.있었던 이슈해결 방법

    기술 스택

    • React Native

    • OneSignal (Push Notification)

    • Supabase

      • Database

      • RPC Function

      • Edge Function (Deno)

    • Android (실제 디바이스 테스트)


    전체 구조 요약

    [React Native App]
      └─ OneSignal SDK
          └─ External User ID = Supabase user.id
    
    [Supabase]
      ├─ records 테이블 (오늘 인증 여부)
      ├─ RPC: get_users_without_today_record()
      └─ Edge Function
          └─ OneSignal REST API 호출

    1. OneSignal 기본 설정 (Android)

    (1) OneSignal App 생성

    • Android App 추가

    • Firebase 설정 (FCM JSON 업로드)

    • Android Platform 활성화 확인

    (2) OneSignal App ID 확인

    Settings → Keys & IDs → OneSignal App ID

    2. React Native에 OneSignal 연동

    (1) 설치

    yarn add react-native-onesignal
    cd ios && pod install
    

    (2) App.tsx 초기화

    useEffect(() => {
      OneSignal.initialize(ONESIGNAL_APP_ID);
      OneSignal.Notifications.requestPermission(true);
    }, []);
    

    (3) 로그인 성공 시 External ID 연결

    OneSignal.login(user.id);
    
    • initialize 는 앱 실행 시 1회

    • login 은 로그인 성공 직후 1회

    • render 중 호출 / 여러 번 호출 안되도록 주의


    3. Supabase: 오늘 인증 안 한 유저 조회 RPC

    create or replace function get_users_without_today_record(today_date date)
    returns table (id uuid)
    language sql
    as $$
      select u.id
      from users u
      where not exists (
        select 1
        from records r
        where r.user_id = u.id
          and r.record_date = today_date
      );
    $$;

    4. Supabase Edge Function에서 푸시 발송

    (1) Edge Function 생성

    supabase functions new send_daily_reminder
    

    (2) Edge Function Secrets 설정

    Supabase Dashboard → Edge Functions → Secrets

    SUPABASE_URL
    SUPABASE_SERVICE_ROLE_KEY
    ONESIGNAL_APP_ID
    ONESIGNAL_API_KEY   ← REST API Key (os_v2_...)

    Edge Function 코드

    import { serve } from "https://deno.land/std/http/server.ts";
    import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
    
    serve(async () => {
      const supabase = createClient(
        Deno.env.get("SUPABASE_URL")!,
        Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
      );
    
      const appId = Deno.env.get("ONESIGNAL_APP_ID")!;
      const apiKey = Deno.env.get("ONESIGNAL_API_KEY")!;
    
      // KST 기준 오늘
      const now = new Date();
      now.setHours(now.getHours() + 9);
      const today = now.toISOString().slice(0, 10);
    
      const { data: users } = await supabase.rpc(
        "get_users_without_today_record",
        { today_date: today }
      );
    
      if (!users || users.length === 0) {
        return new Response("No users to notify");
      }
    
      const res = await fetch("https://onesignal.com/api/v1/notifications", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Basic ${apiKey}`,
        },
        body: JSON.stringify({
          app_id: appId,
          include_external_user_ids: users.map(u => u.id),
          target_channel: "push",
          headings: {
            en: "Daily workout reminder",
            ko: "오늘 인증 아직이에요",
          },
          contents: {
            en: "Don't forget to log your workout 💪",
            ko: "지금 운동 인증하고 기록 남겨요 💪",
          },
        }),
      });
    
      const text = await res.text();
      console.log("OneSignal response:", text);
    
      return new Response(text);
    });
    

    배포:

    supabase functions deploy send_daily_reminder --use-api

    5.있었던 이슈

    “All included players are not subscribed”

    {
      "errors": ["All included players are not subscribed"]
    }
    

    External ID는 존재하지만
    푸시를 받을 수 있는 “Subscription”이 없음

    ➡ OneSignal 사용자 상태 문제라고 지피티가 알려줌

    해결 방법

    (1) OneSignal Dashboard → Audience → Subscriptions

    • Android / Subscribed 항목이 떠야 함

    • Users 화면만 보면 안 됨

    (2) 앱 완전 삭제 + 재설치

    • 앱 삭제

    • 디바이스 재부팅

    • 재설치

    • 알림 권한 허용

    • 로그인 → OneSignal.login(user.id)

    (3) Subscriptions에 Android가 생긴 뒤에 Edge Function 호출

    성공 시 응답:

    {
      "id": "notification-id",
      "recipients": 1
    }
    

    → Messages → Push 목록에 기록 생성됨

    Share article
    Contents
    기술 스택전체 구조 요약1. OneSignal 기본 설정 (Android)(1) OneSignal App 생성(2) OneSignal App ID 확인2. React Native에 OneSignal 연동(1) 설치(2) App.tsx 초기화(3) 로그인 성공 시 External ID 연결3. Supabase: 오늘 인증 안 한 유저 조회 RPC4. Supabase Edge Function에서 푸시 발송(1) Edge Function 생성(2) Edge Function Secrets 설정Edge Function 코드5.있었던 이슈해결 방법

    김보람 | 930802qhfka@gmail.com

    RSS·Powered by Inblog