이번 프로젝트에서는 firebase를 유용하게 쓰고 있는데, 푸시 알림도 파이어 베이스에서 제공해주고 있어서 간편하게 사용할 수 있다!
백엔드는 fcm 서버에 푸시 알림을 전송하고 프론트엔드는 fcm 서버에 있는 메시지를 해당 디바이스에 알림을 띄워주는 것이다.
📌 Firebase로 메시지 보내기 위한 준비 과정
1, Firebase project 생성 후, 터미널에서 Firebase Admin SDK 설치
$ npm install firebase-admin --save
2. SDK 초기화를 위한 서버 키 다운로드 프로젝트 콘솔로 이동 -> 서비스 계정 -> 키 생성
3. SDK 초기화 코드 작성 위의 사진에 보이는 코드를 복사해서 붙여넣기 한다.
var admin = require("firebase-admin");
var serviceAccount = require("path/to/serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
중간에 "path/to/serviceAccountKey.json"은 해당 키 경로를 써주면 되는데, 나는 프로젝트 폴더 안에 키를 넣어서 관리했기 때문에 아래와 같이 적어주었다. (꼭 .gitignore에 키를 써주어야 합니다!! 깃헙에 올라가면 안 돼요!!!!!!!)
let serviceAccount = require(./다운받은serviceAccountKey값.json);
4. 실제 message 보내기
// 메세지 내용
const message = {
notification: {
title: notificationTitle,
body: notificationContent,
},
token: token,
};
// 메세지 전송
admin
.messaging()
.send(message)
.then(function (response) {
console.log("푸시알림 전송 성공", response);
})
.catch(function (error) {
console.log("푸시알림 전송 실패");
});
};
👆 이 코드가 실제로 fcm 서버로 메시지를 보내는 코드이다. (unicast, 디바이스 토큰 한 개 필요) 디바이스 토큰은 프론트엔트 개발자 분들께 요청해서 푸시 알림이 잘 가는지 테스트했다. 그리고 프로젝트 상에서는 유저가 로그인할 때 db에 그 유저의 디바이스 토큰을 저장하는 코드를 추가했다. 어떤 유저에게 푸시 알림을 보내야 하는지 확인하면서 해당 유저의 디바이스 토큰을 가져와 token 값으로 넣어주었다!
나도선배 프로젝트에서는 api에 이 코드를 넣으면 너무 길어져서 lib안에 메시지를 보내는 코드를 모듈화 해 놓고, 필요한 api에서 호출하는 식으로 코드 리팩토링 중이다! 을 진행했다. (서버 선배들 최고 👍🏻)
밑에 코드를 보면 sendUnicast와 sendMulticast가 있는데
sendUnicast -> send(message) : 하나의 device token에게 보냄
sendMulticast -> sendMulticast(message) : 여러 개의 device token들에게 한 번에 보냄
이렇게 나눠서 볼 수 있다. 왜 multicast를 썼는지는 밑에서 설명하도록 한다.
const admin = require("firebase-admin");
const sendUnicast = function (token, notificationTitle, notificationContent) {
// 메세지 내용
const message = {
notification: {
title: notificationTitle,
body: notificationContent,
},
token: token,
};
// 메세지 전송
admin
.messaging()
.send(message) // 하나의 device token에게 보냄
.then(function (response) {
console.log("푸시알림 전송 성공", response);
})
.catch(function (error) {
console.log("푸시알림 전송 실패");
});
};
const sendMulticast = function (tokens, notificationTitle, notificationContent) {
// 댓글이 있을 때만 푸시알림 전송, 댓글이 없을 경우 tokens가 빈 배열이라서 오류남.
if (tokens.length !== 0) {
// 메세지 내용
const message = {
notification: {
title: notificationTitle,
body: notificationContent,
},
tokens: tokens,
};
// 메세지 전송
admin
.messaging()
.sendMulticast(message) // 여러개의 device token에게 한 번에 보냄
.then((response) => {
console.log("푸시알림 전송 성공", response.successCount);
})
.catch(function (error) {
console.log("푸시알림 전송 실패");
});
}
};
module.exports = {
sendUnicast,
sendMulticast,
};
‼️ 아, 중간에 오류를 한 번 만났는데 그 이유는.... multicast에 빈 배열이 들어간 경우엔 empty array로 에러를 반환하기 때문이었다... 그래서 tokens 배열 길이가 0인 경우는 보내지 않도록 예외 처리를 해주었다.
📌 나도선배에 알람이 필요한 상황! - 왜 multicast가 필요해?
나도선배 앱에는 크게
나에게 온 1:1 질문글 알림(마이페이지에 1:1 질문글이 올라온 경우)
답글 알림(내가 쓴 글에 답글이 달린 경우)
새 답글 알림(내가 답글을 쓴 타인 글에 새 답글이 달린 경우)
이렇게 세 가지의 알림 종류가 있고, 세부적으로는 2, 3번은 질문글 / 정보글로 나누어져서 총 7개의 유형이 있다. 유형마다 알림 문구도 달랐고, 특히 게시글인 경우는 특정 유저 1명에게 보내는 unicast인지 다수에게 보내는 multicast인지 분기 처리를 해주어야 하는 부분이 좀 까다로웠다.
예를 들어, 내가 1번 게시물에 댓글을 단 경우
1번 게시물을 쓴 유저(작성자)에게 "작성하신 질문글에 주현님이 답글을 남겼습니다." 또는 "작성하신 정보글에 주현님이 답글을 남겼습니다."라는 알림을 보낸다.
1번 게시물에 댓글을 달았던 유저들에게 "답글을 작성하신 질문글에 주현님이 답글을 남겼습니다." 또는 "답글을 작성하신 정보글에 주현님이 답글을 남겼습니다."
이런 식으로 많은 case가 있다.
여기서 "유저들" 에게 보내야 했기 때문에 multicast를 사용했다. 만약, 그냥 send를 반복문을 사용해서 푸시 알림을 전송한다면 해당 게시글에 댓글을 단 유저마다 푸시알림을 받는 시간이 달라지기 때문이다. (여러 명에게 한 번에 보낼 땐 multicast를 사용해야 합니다!)
📌 마치며
사실 다른 팀원들에게 도움이 되고 싶어서 다른 API를 구현하면서 틈틈이 푸시 알림 공부를 하면서 코드를 짰었는데 디바이스 토큰이 없어서 직접 알람이 오는 것을 눈으로 확인 못하는 게 아쉬웠다. 비록 앱잼 기간이 얼마 안 남아서 알람 / 이메일 인증 이렇게 두 가지 기능을 나눠서 담당해서 나는 알람 코드에 직접적인 영향(?)을 끼치진 못했지만 혼자 공부하면서 재밌었다. 추가로 시간 설정해서 푸시 알림 보내는 기능도 공부해야겠다! 다음 프로젝트에 푸시 알림이 있다면 혼자 구현할 수... 있지 않을까..? 하하
+ 서버 팀원들과 코드 구현 관련 회의할 때 다들 푸시 알림이 해보고 싶다고 해서 각자 코드를 짜며 공부를 해보기로 했는데, 모두 열심히 해줘서 고마웠다. 다들 고생했어요 🔥