코딩마을방범대

[FCM] FCM 토큰의 관리 방법 본문

💡 백엔드/FCM ( Firebase )

[FCM] FCM 토큰의 관리 방법

신짱구 5세 2023. 7. 7. 16:07
728x90

 

 

FCM 토큰은 정해진 수명이나 갱신 주기가 없다.

따라서, 시간과 관계 없이 아래 이벤트가 발생하지 않는다면 만료가 되지 않는다.

  • 앱이 인스턴스 ID를 삭제한 경우
  • 앱이 새 기기에서 복원되었을 경우
  • 사용자가 앱을 제거 / 재설치한 경우
  • 사용자가 앱 데이터를 지운 경우
* AOS     : onTokenRefresh()
* IOS       : messaging:didReceiveRegistrationToken
* WEB     : onTokenRefresh

위 메소드들은 토큰이 갱신될 때마다 호출된다.
위와 같은 상황에 해야할 동작이 있다면 해당 메소드를 활용하면 된다.
※ 이제 onTokenRefresh() 대신 MyFirebaseMessagingService 를 상속받아 onNewToken()로 사용한다고 한다

 

 


 

고려할 부분

1. 같은 기기에서 다른 아이디를 쓰는 경우

    - 로그아웃 시점에 서버에서 유저 토큰을 삭제

 

2. 같은 아이디로 여러 기기를 쓰는 경우

    - 하나의 유저가 여러 토큰을 보유할 수 있도록 데이터베이스 구성

 

3. 토큰이 갱신되어 서버와 동기화가 필요한 경우

    - 프론트에서 onTokenRefresh() 메소드를 오버라이딩하여, 로그인된 유저의 토큰이 갱신될 경우 서버로 토큰을 업데이트하는 API를 전송

 

4. 오래된 토큰이 있는 비활성 장치에 메시지를 전송하면 리소스를 낭비할 수 있음

 

 

 

 


 

 

 

 

 

 

푸시 메시지 전송 시 FCM 토큰의 값이 유효하지 않다면 아래와 같은 에러코드가 리턴된다.

  • UNREGISTERED(HTTP 404) 
  • INVALID_ARGUMENT(HTTP 400)

 

 

Firebase Docs에 따르면 토큰 타임스탬프의 2개월이 지난 토큰은 비활성화된, 유효하지 않는 토큰으로 간주한다.

따라서, 2개월마다 토큰 유효성 검사 후 토큰 업데이트를 해주는 것을 추천한다고 한다.

 

 


 

 

 

send()를 통해 하나하나의 token에 메시지를 보내고 체크하기엔 효율성이 좋지않다.

 

이 때문에 Topic을 이용해 구독 후 에러가 발생하는 token을 뽑아내려고 했으나, 

test Topice을 남겨놓는 것도 좋지 않을 것 같아 구독 후 다시 unsubscribe를 해줘야하기 때문에 포기했다.

 

예제 1. token 각각 메시지 보내기

public boolean checkToken(String fcmToken){
	try{
		Message message = Message.builder()
			.setToken(fcmToken)
			.build();

		FirebaseMessaging.getInstance().send(message);
		return true;
	}catch (FirebaseMessagingException e){
		return false;
	}
}

 

 

예제 2. Topic 구독 방식

public void checkToken(){
	List<String> tokenList = deviceRepository.findAllBy().stream().map(
		device -> device.getToken()
	).collect(Collectors.toList());

	// 구독
	FirebaseMessaging.getInstance().subscribeToTopic(tokenList, "fcm-test");

	// 구독취소
	FirebaseMessaging.getInstance().unsubscribeFromTopic(tokenList, "fcm-test");
}
만약 device 전체 정보를 가져오는 것이 무겁다면, Interface를 생성해 아래처럼 원하는 정보만 가져올 수 있다.

** Interface
public interface DeviceFCMToken {
    String getToken();
}​

 

** Repository
List<DeviceFCMToken> findAllBy();

 

 


 

 

위에 방법 말고 검색해보니 일괄 메시지 전송이 적합할 것 같았다.

일괄 메시지 전송에는 두가지 방법이 있다.

 

기존 각각의 토큰을 이용해 메시지를 보내는 방법과 같은 sendAll 방식(Message)과

MulticastMessage를 이용하는 sendMulticast 방식이 있다.

 

 

예제1. sendAll

최대 500개의 메시지를 전송할 수 있다고 한다.

sendAll도 BatchResponse를 이용해 결과값을 받을 수 있지만, 메시지를 하나하나 세팅해야해서 이번 취지와는 맞지 않다.

// Create a list containing up to 500 messages.
List<Message> messages = Arrays.asList(
    Message.builder()
        .setNotification(Notification.builder()
            .setTitle("Price drop")
            .setBody("5% off all electronics")
            .build())
        .setToken(registrationToken)
        .build(),
    // ...
    Message.builder()
        .setNotification(Notification.builder()
            .setTitle("Price drop")
            .setBody("2% off all books")
            .build())
        .setTopic("readers-club")
        .build()
);

BatchResponse response = FirebaseMessaging.getInstance().sendAll(messages);
// See the BatchResponse reference documentation
// for the contents of response.
System.out.println(response.getSuccessCount() + " messages were sent successfully");

 

 

예제2. sendMulticast

sendMulticast는 sendAll과 다르게 메시지를 하나하나 세팅하는 것이 아닌,

토큰을 리스트형식으로 받아 전송한다.

response 값을 받아 오류가 발생한 내역도 확인이 가능하다.

public BatchResponse sendMessage(List<String> targetToken) throws FirebaseMessagingException {
	MulticastMessage message = MulticastMessage.builder()
		.addAllTokens(targetToken)
		.build();
	BatchResponse response = FirebaseMessaging.getInstance().sendMulticast(message);
    
	List<SendResponse> responses = response.getResponses();
	for (int i = 0; i < responses.size(); i++) {
		if (!responses.get(i).isSuccessful()) {
			deviceList.get(i).setTokenStatus(TokenStatus.Disabled);
		}
	}
}

 

 

 

 


참고사이트

[Firebase] FCM을 도입할 때 고려할 것들

Firebase docs

앱 서버 전송 요청 작성

 

 

728x90