[Spring] 게시판 API (9) PushAlarm FCM 활용
FCM과 PushAlarm의 동작원리를 알고 싶다면 이전 포스팅 해둔 내용을 보면 됩니다.
2023.03.26 - [Spring] - 앱 푸시알림동작 원리와 FCM(Firebase Cloud Messaging)을 알아보자
앱 푸시알림동작 원리와 FCM(Firebase Cloud Messaging)을 알아보자
게시판 앱과 서버에서 푸시알림을 구현하기 전에 푸시알람과 이를 구현해주는 FCM의 원리를 공부해보고 이를 정리해보았습니다. 푸시알림이란 데스크탑 브라우저, 모바일 홈 화면, 모바일 앱의
wans1027.tistory.com
구현해야할 기능
- 새로운 기기가 로그인으로 접근이 되면
- 멤버테이블에 있는 로그인 한 회원에 FCMTOKEN을 추가한다.
순서설계
- 앱은 앱 실행 시점에 FCM에 Binding
- 만약 내부저장소에 토큰이 이미 존재한다면 파라미터 변수인 isNew를 false로
- 내부저장소에 토큰이 없다면 새로운 토큰을 FCM으로부터 받아오고 isNew를 true로 설정합니다.
- 로그인 시점에 isNew가 true라면 FCM토큰 GET요청 헤더에 실어 서버로 전송
- 서버는 이를 받아 회원테이블과 일대다로 연결된 FCMTABLE에 토큰을 추가
이렇게 설계한 이유는 앱을 재설치하거나 등록 기기를 바꾸는게 아닌이상 매번 같은 Token이 전송되므로 서버에서 이 토큰을 항상 대조시키는 로직을 사용하는 것은 비용낭비입니다.
그래서 새로운 토큰인지 판단하는 로직은 앱에서 하는 것이 효율적입니다.
서버는 단지 새로운 요청이들어오면 이를 저장만 하면 됩니다.
Spring에 FCM적용
FireBase에서 받은 비공개 키 json파일을 main/resources/ 에 추가합니다.
build.gradle과 application.yml에 추가합니다.
# build.gradle
implementation group: 'com.google.firebase', name: 'firebase-admin', version: '6.8.1'
# application.yml
fcm:
key:
path: Firebase에서받은키.json
scope: https://www.googleapis.com/auth/cloud-platform
Firbase계정 인증 및 알림서비스 로직
@Slf4j
@Service
public class NotificationServiceImpl {
@Value("${fcm.key.path}")
private String FCM_PRIVATE_KEY_PATH;
//
// 메시징만 권한 설정
@Value("${fcm.key.scope}")
private String fireBaseScope;
// fcm 기본 설정 진행
@PostConstruct
public void init() {
try {
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(
GoogleCredentials
.fromStream(new ClassPathResource(FCM_PRIVATE_KEY_PATH).getInputStream())
.createScoped(List.of(fireBaseScope)))
.build();
if (FirebaseApp.getApps().isEmpty()) {
FirebaseApp.initializeApp(options);
log.info("Firebase application has been initialized");
}
} catch (IOException e) {
log.error(e.getMessage());
// spring 뜰때 알림 서버가 잘 동작하지 않는 것이므로 바로 죽임
throw new RuntimeException(e.getMessage());
}
}
// 알림 보내기
public void sendByTokenList(List<String> tokenList,String title, String body) {
// 메시지 만들기
List<Message> messages = tokenList.stream().map(token -> Message.builder()
.putData("time", LocalDateTime.now().toString())
.setNotification(new Notification(title, body))
.setToken(token)
.build()).collect(Collectors.toList());
// 요청에 대한 응답을 받을 response
BatchResponse response;
try {
// 알림 발송
response = FirebaseMessaging.getInstance().sendAll(messages);
// 요청에 대한 응답 처리
if (response.getFailureCount() > 0) {
List<SendResponse> responses = response.getResponses();
List<String> failedTokens = new ArrayList<>();
for (int i = 0; i < responses.size(); i++) {
if (!responses.get(i).isSuccessful()) {
failedTokens.add(tokenList.get(i));
}
}
log.error("List of tokens are not valid FCM token : " + failedTokens);
}
} catch (FirebaseMessagingException e) {
log.error("cannot send to memberList push message. error info : {}", e.getMessage());
}
}
}
코드출처: https://backtony.github.io/spring/2021-08-20-spring-fcm-1/
여기까지 하면 푸시알림을 보낼 준비가 끝났습니다.!
회원에 매칭시키기
이제 게시판에 활용하기 위해 테이블 관계설정을 해주겠습니다.
토큰 저장 테이블 추가
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FcmToken {
@Id
@GeneratedValue
@Column(name = "fcm_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
private String token;
public FcmToken(Member member, String token) {
this.member = member;
this.token = token;
}
}
회원PK를 외래키로 가지고 토큰을 저장하는 테이블입니다.
Member Entity에 추가
@OneToMany(mappedBy = "member", orphanRemoval = true)
private List<FcmToken> fcmTokens = new ArrayList<>();
회원이 사라지면 저장된 토큰도 사라져야 하기에 orphanRemoval를 true로 설정합니다.
MemberService에 추가
public void addFCMToken(Long memberId, String fcmToken){
Optional<Member> member = memberRepository.findById(memberId);
FcmToken token = new FcmToken(member.orElseThrow(), fcmToken);
fcmTokenRepository.save(token);
}
멤버 주키와 토큰값을 받아와 저장하는 서비스 로직입니다.
Controller 작성
@RestController
@RequiredArgsConstructor
public class FcmController {
private final NotificationServiceImpl notificationService;
private final MemberService memberService;
@GetMapping(value = "/api/fcm/{memberId}",headers = "FCM-TOKEN")
public void pushAlarmMyDevice(@PathVariable("memberId") Long memberId, @RequestHeader("FCM-TOKEN") String token){
memberService.addFCMToken(memberId,token);
}
}
사용자의 GET요청 헤더에 담긴 토큰값을 가져와 저장하면 끝입니다.
푸시알림을 보내고 싶으면 아래 메서드를 사용하면 됩니다.
notificationService.sendByTokenList(토큰, 제목, 내용);
이제 이를 활용하여 앱이 꺼져 있을때도 새로운 글이 올라오거나, 누군가 내글에 댓글을 달거나 등의 상태변화를 스마트폰 백그라운드에서 확인 하는 기능을 만들 수 있습니다.
Reference:
https://backtony.github.io/spring/2021-08-20-spring-fcm-1/
Spring 비동기 FCM 알림서버 구현하기 (Feat.ApplicationEvent)
Java, JPA, Spring을 주로 다루고 공유합니다.
backtony.github.io
Flutter에서 FCM의 동작을 알고싶다면
2023.03.25 - [Flutter] - [Flutter] 푸시알람 FCM 설정하기
[Flutter] 푸시알람 FCM 설정하기
- FireBase Setting https://firebase.google.com/?hl=ko Firebase Firebase는 고품질 앱을 빠르게 개발하고 비즈니스를 성장시키는 데 도움이 되는 Google의 모바일 플랫폼입니다. firebase.google.com FireBase에 로그인하고
wans1027.tistory.com