원격 구성 업데이트의 실시간 전파

Firebase용 Cloud Functions에서 제공하는 원격 구성 백그라운드 함수 트리거를 FCM과 함께 사용하면 실시간으로 원격 구성 업데이트를 전파할 수 있습니다. 이 시나리오에서는 대시보드 또는 API의 원격 구성 템플릿을 게시하거나 롤백할 때 트리거되는 함수를 만듭니다. 템플릿을 업데이트하면 FCM 메시지를 보내는 함수가 트리거되고, 이 함수를 통해 클라이언트는 기존 구성이 비활성 상태이고 다음번에 해당 서버에서 구성을 가져와야 한다는 사실을 알 수 있습니다.

Cloud Functions를 통해 FCM 알림을 트리거하는 원격 구성 업데이트를 보여주는 다이어그램

이 문서의 나머지 부분에 나와 있는 단계별 안내에 따라 실시간으로 원격 구성 업데이트를 전파할 수 있습니다.

클라이언트 앱 인스턴스의 FCM 주제 구독

전체 사용자층과 같은 대규모 그룹의 클라이언트 앱 인스턴스를 대상으로 FCM 메시지를 보내는 데에는 주제 메시징이 가장 효율적인 메커니즘입니다. 실시간 원격 구성 업데이트를 수신하려면 각 앱 인스턴스가 주제 이름(예: PUSH_RC)을 구독해야 합니다.

Swift

extension AppDelegate : MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
        messaging.subscribe(toTopic: "PUSH_RC") { error in
            print("Subscribed to PUSH_RC topic")
        }
    }
}
    

Objective-C

- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
    [[FIRMessaging messaging] subscribeToTopic:@"PUSH_RC" completion:^(NSError * _Nullable error) {
        NSLog(@"Subscribed to PUSH_RC topic");
    }];
}
    

Android

@Override
public void onNewToken(String s) {
    FirebaseMessaging.getInstance().subscribeToTopic("PUSH_RC");
}
    

템플릿 업데이트 시 FCM 핑을 보내는 함수 만들기

새로운 구성 버전 게시 또는 이전 버전으로 롤백을 포함한 원격 구성 이벤트에 대한 응답으로 함수를 트리거할 수 있습니다. 실시간으로 템플릿 업데이트를 전파하려면 템플릿 게시 이벤트를 수신 대기하는 함수를 만든 다음 함수의 FCM Admin SDK를 사용하여 클라이언트 앱 인스턴스로 자동 핑을 보내세요.

exports.pushConfig = functions.remoteConfig.onUpdate(versionMetadata => {
  // Create FCM payload to send data message to PUSH_RC topic.
  const payload = {
    topic: "PUSH_RC",
    data: {
      "CONFIG_STATE": "STALE"
    }
  };
  // Use the Admin SDK to send the ping via FCM.
  return admin.messaging().send(payload).then(resp => {
    console.log(resp);
    return null;
  });
});

이 함수는 CONFIG_STATE 매개변수를 설정한 후 PUSH_RC 주제를 구독한 모든 클라이언트에게 FCM 메시지의 데이터 페이로드로 보냅니다.

클라이언트의 원격 구성 상태 설정

앞 단계에서 나온 데이터 페이로드는 앱의 공유 환경설정에서 CONFIG_STATE를 항상 STALE로 설정합니다. 이는 앞서 앱에 저장되었던 원격 구성 템플릿이 새로 업데이트된 템플릿의 생성으로 인해 비활성 상태가 되었으며 새로운 템플릿의 게시가 함수를 트리거했음을 나타냅니다. 이 조건을 테스트하려면 알림 핸들러를 업데이트하세요.

Swift

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

    if (userInfo.index(forKey: "CONFIG_STATE") != nil) {
        print("Config set to stale")
        UserDefaults.standard.set(true, forKey:"CONFIG_STALE")
    }

    completionHandler(UIBackgroundFetchResult.newData)
}
    

Objective-C

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

    if (userInfo[@"CONFIG_STATE"]) {
        NSLog(@"Config set to stale");
        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"CONFIG_STALE"];
    }

    completionHandler(UIBackgroundFetchResultNewData);
}
    

Android

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    if (remoteMessage.getData().containsKey("CONFIG_STATE")) {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        sharedPreferences.edit().putBoolean("CONFIG_STALE", true).apply();
    }
}
    

앱 시작 시 원격 구성 업데이트 가져오기

Swift

func fetchConfig() {
  welcomeLabel.text = remoteConfig[loadingPhraseConfigKey].stringValue

  var expirationDuration = 3600
  // If your app is using developer mode, expirationDuration is set to 0, so each fetch will
  // retrieve values from the service.
  if remoteConfig.configSettings.isDeveloperModeEnabled || UserDefaults.standard.bool(forKey: "CONFIG_STALE") {
    expirationDuration = 0
  }

  remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
    if status == .success {
      print("Config fetched!")
      self.remoteConfig.activateFetched()
    } else {
      print("Config not fetched")
      print("Error: \(error?.localizedDescription ?? "No error available.")")
    }
    self.displayWelcome()
  }
}
    

Objective-C

- (void)fetchConfig {
    self.welcomeLabel.text = self.remoteConfig[kLoadingPhraseConfigKey].stringValue;

    long expirationDuration = 3600;
    // If your app is using developer mode, expirationDuration is set to 0, so each fetch will
    // retrieve values from the Remote Config service.
    if (self.remoteConfig.configSettings.isDeveloperModeEnabled || [[NSUserDefaults standardUserDefaults] boolForKey:@"CONFIG_STALE"]) {
        expirationDuration = 0;
    }

    [self.remoteConfig fetchWithExpirationDuration:expirationDuration completionHandler:^(FIRRemoteConfigFetchStatus status, NSError *error) {
        if (status == FIRRemoteConfigFetchStatusSuccess) {
            NSLog(@"Config fetched!");
            [self.remoteConfig activateFetched];
            [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"CONFIG_STALE"];
        } else {
            NSLog(@"Config not fetched");
            NSLog(@"Error %@", error.localizedDescription);
        }
        [self displayWelcome];
    }];
}
    

Android

private void fetchWelcomeMessage() {
    mWelcomeTextView.setText(mFirebaseRemoteConfig.getString("loading_phrase"));

    long cacheExpiration = 43200; // 12 hours in seconds.
    // If your app is using developer mode or cache is stale, cacheExpiration is set to 0,
    // so each fetch will retrieve values from the service.
    if (mFirebaseRemoteConfig.getInfo().getConfigSettings().isDeveloperModeEnabled() ||
            mSharedPreferences.getBoolean("CONFIG_STALE", false)) {
        cacheExpiration = 0;
    }

    mFirebaseRemoteConfig.fetch(cacheExpiration)
            .addOnCompleteListener(this, new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    if (task.isSuccessful()) {
                        Toast.makeText(MainActivity.this, "Fetch Succeeded",
                                Toast.LENGTH_SHORT).show();

                        // After config data is successfully fetched, it must be activated before newly fetched
                        // values are returned.
                        mFirebaseRemoteConfig.activateFetched();
                    } else {
                        Toast.makeText(MainActivity.this, "Fetch Failed",
                                Toast.LENGTH_SHORT).show();
                    }
                    mWelcomeTextView.setText(mFirebaseRemoteConfig.getString("welcome_message"));
                }
            });
}
    

마지막으로 CONFIG_STATESTALE이므로 로컬 스토리지를 무시하고 네트워크에서 원격 구성 가져오기를 강제 적용하는 로직을 앱에 추가하세요. 앱의 네트워크에서 가져오기 빈도가 너무 잦은 경우에는 Firebase에서 제한이 발생할 수 있습니다. 제한을 참조하세요.