Ensuring Data Consistency, Atomicity and UX Optimization (feat.Firebase)
Firebase 구독 관리: 데이터 일관성, 원자성 및 UX 최적화
개요
간단한 로직 구현
//구독
@Transactional
public void createSubscription(long userId, String topicName) {
/* 흐름 시각화
1. 유저 정보를 조회힙니다.
└─ 2.존재하지 않으면 Exception을 터트립니다.
3.유저의 FCM토큰을 가져옵니다.
4.구독 정보를 조회합니다.
└─ 5. 이미 구독 중이면 종료합니다.
6. DB에 구독 정보를 저장합니다.
7. FCM 토큰이 존재하는지 확인합니다.
└─ 8. FCM에 구독 요청을 보냅니다.
├─ 9. 실패하면 Exception을 터트립니다.
└─ 10. DB에 반영되지 않고 롤백됩니다.
*/
var user = userApiRepository.findById(userId).orElseThrow(NotFoundException::new);
var fcmToken = user.getFcmToken();
var topicSubsOpt = topicSubscriptionRepository.findByTopicNameAndUserId(topicName, userId);
if(topicSubsOpt.isPresent()) return;
topicSubscriptionRepository.save(TopicSubscription.of(topicName, userId, fcmToken, true));
if(fcmToken != null) {
try {
FirebaseMessaging.getInstance().subscribeToTopicAsync(List.of(fcmToken), topicName).get();
} catch (Exception e) {
throw new RuntimeException("FCM subscribe error", e);
}
}
}
//구독 취소
@Transactional
public void deleteSubscription(long userId, String topicName) {
/* 흐름 시각화
1.구독 정보를 조회합니다.
└─ 2. 구독 중이지 않으면 종료합니다.
3. DB에 구독 정보를 삭제합니다.
4. FCM 토큰을 확인합니다.
├─ 5. 존재하면 FCM에 구독 취소 요청을 보냅니다.
└─ 6. 존재하지 않으면 Exception을 터트립니다.
└─ 7. DB에 반영되지 않고 롤백됩니다.
*/
var topicSubOpt = topicSubscriptionRepository.findByTopicNameAndUserId(topicName, userId);
if(topicSubOpt.isEmpty()) return;
var topicSub = topicSubOpt.get();
topicSubscriptionRepository.deleteByTopicNameAndUserId(topicName, userId);
if(topicSub.getFcmToken() != null) {
try {
FirebaseMessaging.getInstance().unsubscribeFromTopicAsync(List.of(topicSub.getFcmToken()), topicName).get();
} catch (Exception e) {
throw new RuntimeException("FCM unsubscribe error", e);
}
}
}재시도 로직
유저 속이기(api가 성공한것처럼 처리하기)
불가항력적인 문제점
결론
느낀점
Last updated