개발/FRONT

Firebase Cloud Messaging(FCM) 완벽 설정 가이드 - 푸시 알림의 모든 것

Hdev&Shoes 2025. 12. 21. 17:16
728x90

 

Firebase Cloud Messaging(FCM) 완벽 설정 가이드

푸시 알림의 모든 것

Angular + Ionic + Capacitor 프로젝트에 Firebase Cloud Messaging을 연동하는 방법을 실제 프로덕션 환경에서 사용하는 패턴으로 설명한다. 토큰 관리부터 알림 수신 처리, Android/iOS 설정까지 모든 것을 다룬다.


1. Firebase Console 설정

Firebase Console에서 Cloud Messaging을 활성화한다.

1.1 Cloud Messaging 활성화

  1. 1Firebase Console 접속
  2. 2프로젝트 선택
  3. 3좌측 메뉴에서 "Cloud Messaging" 선택
  4. 4Cloud Messaging API 활성화 (자동으로 활성화됨)

1.2 서버 키 확인

서버에서 푸시 알림을 보낼 때 필요한 서버 키를 확인한다:

  1. 프로젝트 설정 (⚙️) → 클라우드 메시징 탭
  2. "클라우드 메시징 API 서버 키" 복사
⚠️ 주의: 서버 키는 절대 공개하지 말자. 백엔드 서버에서만 사용해야 한다.

2. 필요한 패키지 설치

FCM을 사용하기 위해 필요한 패키지를 설치한다.

2.1 Firebase 패키지

Firebase는 이미 설치되어 있다면 생략:

npm install firebase @angular/fire

2.2 Capacitor 플러그인 (선택사항)

네이티브 앱에서 푸시 알림을 받으려면 Capacitor 플러그인이 필요하다:

npm install @capacitor/push-notifications
npx cap sync
💡 참고: 웹 앱에서는 @angular/fire/messaging만으로도 충분하다. 네이티브 앱에서만 Capacitor 플러그인이 필요하다.

3. AppModule 설정

app.module.ts에 Messaging 모듈을 추가한다.

3.1 Messaging 모듈 추가

import { NgModule } from '@angular/core';
import { provideMessaging, getMessaging } from '@angular/fire/messaging';
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
import { environment } from 'src/environments/environment';

@NgModule({
  imports: [
    // ... other imports
    
    // Firebase 초기화
    provideFirebaseApp(() => initializeApp(environment.firebaseConfig)),
    
    // Messaging 초기화
    provideMessaging(() => getMessaging()),
  ],
})
export class AppModule {}
💡 참고: getMessaging()는 Firebase App이 먼저 초기화되어 있어야 한다.

4. FCM 서비스 구현

FCM 토큰 관리와 알림 수신을 처리하는 서비스를 만든다.

4.1 FCMService 기본 구조

import { Injectable } from '@angular/core';
import { Messaging, getToken, onMessage } from '@angular/fire/messaging';
import { Platform } from '@ionic/angular';
import { Capacitor } from '@capacitor/core';

@Injectable({
  providedIn: 'root',
})
export class FCMService {
  constructor(
    private messaging: Messaging,
    private platform: Platform
  ) {}

  /**
   * FCM 토큰 가져오기
   */
  async getToken(): Promise<string | null> {
    try {
      // 웹 환경에서만 동작
      if (!Capacitor.isNativePlatform()) {
        const token = await getToken(this.messaging, {
          vapidKey: '[VAPID_KEY]', // Firebase Console에서 발급받은 키
        });
        return token;
      }
      
      // 네이티브 앱은 Capacitor 플러그인 사용
      return null;
    } catch (error) {
      console.error('토큰 가져오기 실패:', error);
      return null;
    }
  }

  /**
   * 포그라운드에서 알림 수신 처리
   */
  onMessage() {
    return onMessage(this.messaging);
  }
}

4.2 VAPID 키 발급

웹에서 FCM을 사용하려면 VAPID 키가 필요하다:

  1. Firebase Console → 프로젝트 설정 (⚙️)
  2. 클라우드 메시징 탭
  3. "웹 푸시 인증서" 섹션에서 키 생성 또는 복사
⚠️ 주의: VAPID 키가 없으면 웹에서 토큰을 가져올 수 없다.

5. 토큰 가져오기 및 저장

앱 시작 시 FCM 토큰을 가져와서 Firestore에 저장한다.

5.1 AppComponent에서 토큰 가져오기

import { Component, OnInit } from '@angular/core';
import { FCMService } from './core/services/fcm.service';
import { AuthService } from './core/services/auth.service';
import { DbService } from './core/services/db.service';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
})
export class AppComponent implements OnInit {
  constructor(
    private fcm: FCMService,
    private auth: AuthService,
    private db: DbService
  ) {}

  async ngOnInit() {
    await this.initializeFCM();
  }

  async initializeFCM() {
    // 알림 권한 요청 (웹)
    if ('Notification' in window) {
      const permission = await Notification.requestPermission();
      if (permission === 'granted') {
        await this.saveToken();
      }
    } else {
      // 네이티브 앱
      await this.saveToken();
    }
  }

  async saveToken() {
    try {
      const token = await this.fcm.getToken();
      if (!token) return;

      const user = await this.auth.getUser();
      if (!user) return;

      // Firestore에 토큰 저장
      await this.db.updateAt(`members/${user.uid}`, {
        pushId: token,
        pushIdUpdatedAt: new Date(),
      });

      console.log('FCM 토큰 저장 완료:', token);
    } catch (error) {
      console.error('토큰 저장 실패:', error);
    }
  }
}

5.2 토큰 갱신 처리

토큰이 갱신될 때 자동으로 저장하도록 처리:

import { onTokenRefresh } from '@angular/fire/messaging';

async initializeFCM() {
  // 초기 토큰 가져오기
  await this.saveToken();

  // 토큰 갱신 감지
  onTokenRefresh(this.messaging).subscribe(async (token) => {
    console.log('토큰 갱신됨:', token);
    await this.saveToken();
  });
}
💡 팁: 토큰은 앱 재설치, 데이터 삭제, 앱 복원 등에서 변경될 수 있다. 토큰 갱신을 감지해서 항상 최신 토큰을 유지하자.

6. 푸시 알림 수신 처리

앱이 포그라운드에 있을 때와 백그라운드에 있을 때 알림을 다르게 처리한다.

6.1 포그라운드 알림 처리

앱이 열려있을 때 알림을 받으면 onMessage로 처리한다:

import { Component, OnInit } from '@angular/core';
import { FCMService } from './core/services/fcm.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
})
export class AppComponent implements OnInit {
  constructor(
    private fcm: FCMService,
    private router: Router
  ) {}

  ngOnInit() {
    this.setupMessageListener();
  }

  setupMessageListener() {
    this.fcm.onMessage().subscribe((payload) => {
      console.log('포그라운드 알림 수신:', payload);
      
      // 알림 데이터 추출
      const notification = payload.notification;
      const data = payload.data;

      // 커스텀 알림 표시 (선택사항)
      if (Notification.permission === 'granted') {
        new Notification(notification?.title || '알림', {
          body: notification?.body,
          icon: '/assets/icon/favicon.png',
        });
      }

      // 알림 클릭 시 페이지 이동
      if (data && data.url) {
        this.router.navigateByUrl(data.url);
      }
    });
  }
}

6.2 백그라운드 알림 처리 (Service Worker)

앱이 백그라운드에 있을 때는 Service Worker에서 처리한다.

1. src/firebase-messaging-sw.js 파일 생성:

// firebase-messaging-sw.js
importScripts('https://www.gstatic.com/firebasejs/10.7.1/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/10.7.1/firebase-messaging-compat.js');

firebase.initializeApp({
  apiKey: '[apiKey]',
  authDomain: '[authDomain]',
  projectId: '[projectId]',
  storageBucket: '[storageBucket]',
  messagingSenderId: '[messagingSenderId]',
  appId: '[appId]',
});

const messaging = firebase.messaging();

// 백그라운드 알림 수신 처리
messaging.onBackgroundMessage((payload) => {
  console.log('백그라운드 알림 수신:', payload);
  
  const notificationTitle = payload.notification.title;
  const notificationOptions = {
    body: payload.notification.body,
    icon: '/assets/icon/favicon.png',
  };

  return self.registration.showNotification(notificationTitle, notificationOptions);
});

2. angular.json에 Service Worker 등록:

{
  "projects": {
    "app": {
      "architect": {
        "build": {
          "options": {
            "serviceWorker": true,
            "assets": [
              "src/firebase-messaging-sw.js"
            ]
          }
        }
      }
    }
  }
}

7. Android 설정

Android 앱에서 FCM을 사용하려면 google-services.json 파일이 필요하다.

7.1 google-services.json 다운로드

  1. 1Firebase Console → 프로젝트 설정
  2. 2Android 앱 추가 (또는 기존 앱 선택)
  3. 3패키지 이름 입력 (예: com.mycarbon.knc)
  4. 4google-services.json 다운로드

7.2 파일 배치

다운로드한 파일을 다음 위치에 배치:

android/app/google-services.json

7.3 build.gradle 설정

android/build.gradle (프로젝트 레벨):

buildscript {
  dependencies {
    classpath 'com.google.gms:google-services:4.4.0'
  }
}

android/app/build.gradle (앱 레벨):

plugins {
  id 'com.android.application'
  id 'com.google.gms.google-services'  // 추가
}

dependencies {
  implementation 'com.google.firebase:firebase-messaging:23.4.0'
}

8. iOS 설정

iOS 앱에서 FCM을 사용하려면 GoogleService-Info.plist 파일이 필요하다.

8.1 GoogleService-Info.plist 다운로드

  1. 1Firebase Console → 프로젝트 설정
  2. 2iOS 앱 추가 (또는 기존 앱 선택)
  3. 3번들 ID 입력 (예: com.mycarbon.knc)
  4. 4GoogleService-Info.plist 다운로드

8.2 파일 배치

다운로드한 파일을 Xcode 프로젝트에 추가:

ios/App/App/GoogleService-Info.plist

8.3 Push Notifications 활성화

  1. Xcode에서 프로젝트 열기
  2. Target 선택 → Signing & Capabilities
  3. "+ Capability" 클릭 → "Push Notifications" 추가
  4. "Background Modes" 추가 → "Remote notifications" 체크

8.4 Podfile 설정

ios/App/Podfile에 Firebase 추가:

platform :ios, '13.0'
use_frameworks!

target 'App' do
  pod 'Firebase/Messaging'
end

Pod 설치:

cd ios/App
pod install

9. 실전 팁과 주의사항

9.1 토큰 관리 전략

  • 로그인 시 토큰 저장: 사용자가 로그인할 때마다 최신 토큰을 저장
  • 로그아웃 시 토큰 삭제: 보안을 위해 로그아웃 시 토큰을 제거
  • 토큰 갱신 감지: onTokenRefresh로 자동 갱신

9.2 알림 페이로드 구조

효과적인 알림을 위해 페이로드를 구조화한다:

{
  "notification": {
    "title": "알림 제목",
    "body": "알림 내용"
  },
  "data": {
    "url": "/tabs/home",
    "type": "alarm",
    "id": "123"
  }
}

9.3 자주 하는 실수

  • VAPID 키 누락: 웹에서 토큰을 가져오려면 VAPID 키가 필수
  • Service Worker 미등록: 백그라운드 알림을 받으려면 Service Worker 필요
  • 권한 미요청: 웹에서는 사용자에게 알림 권한을 요청해야 함
  • 토큰 미갱신: 토큰이 변경되어도 갱신하지 않으면 알림을 받을 수 없음

9.4 테스트 방법

  1. 1Firebase Console → Cloud Messaging
  2. 2"새 알림" 클릭
  3. 3알림 제목, 내용 입력
  4. 4대상 선택 (단일 기기 또는 토픽)
  5. 5"테스트 메시지 보내기" 클릭

마무리

Firebase Cloud Messaging을 설정하면 사용자에게 푸시 알림을 보낼 수 있다. 웹, Android, iOS 모두에서 동작하도록 설정하는 것이 중요하다.

토큰 관리와 알림 수신 처리를 제대로 구현하면 사용자 경험이 크게 향상된다. 특히 토큰 갱신을 자동으로 처리하고, 알림 클릭 시 적절한 페이지로 이동하는 것이 중요하다.

✅ 핵심 요약:
  • Firebase Console에서 Cloud Messaging 활성화
  • provideMessaging()로 Messaging 모듈 초기화
  • 웹에서는 VAPID 키 필요, 네이티브는 설정 파일 필요
  • 토큰은 로그인 시 저장, 로그아웃 시 삭제
  • 포그라운드는 onMessage(), 백그라운드는 Service Worker

728x90