Firebase 공식 문서를 참고하여 작성하였음
Cloud Function으로 문서에 변화를 감지해서, 특정 코드를 실행할 때 사용하는 것이다. Cloud Function은 무료 Spark 요금제가 아닌 유료 종량제 요금으로 변경해야 사용할 수 있지만, 연습용으로는 과금이 거의 없기 때문에 괜찮다고 생각한다. 하루 20만번의 Functino 호출까지는 무료.
주로FCM
을 사용할 때 이용하는데, 그 외에도 흔히 SNS에서 쓰는 팔로잉, 팔로우, 좋아요 등의 기능들에 탈퇴한 회원의 데이터를 삭제할 때도 유용하다.
Trigger 이름 | 설명 |
---|---|
onCreate | 해당 문서가 처음으로 생성(기록)될 때 트리거 |
onUpdate | 이미 존재하는 문서의 Field 값이 Update(값이 변경)될 때 트리거 |
onDelete | 데이터가 있는 문서가 삭제될 때 트리거 |
onWrite | 위의 onCreate, onUpdate, onDelete 세 개의 트리거 되는 조건에 트리거, 즉, 해당 문서가 create, update, delete 될 때 트리거 |
users
collection의terman
문서의 변화에 의해 트리거되는 것인데,onWrite
이기 때문에terman
document에 생성, 변경, 삭제 어떤 변화가 생겨도 트리거 된다.
exports.myFunctionName = functions.firestore
.document('users/terman').onWrite((change, context) => {
// ... Your code here
});
users/{userId}
로 컬렉션 내 문서들의 이벤트를 모두 listen하는데{uerId}
처럼{}
안에 위치하는 것을와일드카드(wildcard)
라고 한다.
해당 함수는user
collection 내 문서들의 이벤트에만 트리거 되면users
의 하위 컬렉션에는 해당되지 않는다.
// Listen for changes in all documents in the 'users' collection
exports.useWildcard = functions.firestore
.document('users/{userId}')
.onWrite((change, context) => {
// If we set `/users/marie` to {name: "Marie"} then
// context.params.userId == "marie"
// ... and ...
// change.after.data() == {name: "Marie"}
});
아래 코드처럼 개수 제한 없이 원하는 만큼 와일드 카드를 정의하여 collection 또는 documentID를 대체할 수 있다.
와일드카드 위치는 문서 경로에서 추출되어context.params
에 저장된다.
// Listen for changes in all documents in the 'users' collection and all subcollections
exports.useMultipleWildcards = functions.firestore
.document('users/{userId}/{messageCollectionId}/{messageId}')
.onWrite((change, context) => {
// If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
// context.params.userId == "marie";
// context.params.messageCollectionId == "incoming_messages";
// context.params.messageId == "134";
// ... and ...
// change.after.data() == {body: "Hello"}
});
아래와 같은 코드가 있다고 가정했을 때,
onCreate
내 builder 내 파라미터이snap
은 추가되는 데이터를context
는documentID
인userId
를 나타낸다.
//dart 코드
documentCollection = FirebaseFirestore.instance.collection("users");
documentCollection.add({
'name': 'Marie',
'age': 66,
});
exports.createUser = functions.firestore
.document('users/{userId}')
.onCreate((snap, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = snap.data();
// access a particular field as you would any JS property
const name = newValue.name; // == 'Marie'
const age = newValue.age; // == 66
// perform desired operations ...
});
onUpdate는
snap
이 아니라change
가 파라미터로 있는데,change.before.data()
는 변경 전 데이터를,
change.after.data()
는 변경되는 데이터를 담고 있다.
그냥change.data()
는 존재하지 않기에 해당 형식을 쓴 트리거로 함수 호출 시 코드가 제대로 실행되지 않는다. 즉, 에러가 생긴다.
//old data
"users": {
"abced": {
"name": "terman",
"age": 25,
}
}
FirebaseFirestore.instnace.collection("users").doc("abced").update({
'name': 'Marie',
'age': 66,
});
exports.updateUser = functions.firestore
.document('users/{userId}')
.onUpdate((change, context) => {
const newValue = change.after.data();
// ...or the previous value before this update
const previousValue = change.before.data();
// access a particular field as you would any JS property
const newName = newValue.name; // == 'Marie'
const newName = newValue.age; // == 66
const previousName = previousValue.name; // == 'terman'
const previousAge = previousValue.age; // == 25
// perform desired operations ...
});
문서가 삭제될 때의 트리거이므로
snap
은 삭제되는 데이터를 담고 있다.
exports.deleteUser = functions.firestore
.document('users/{userID}')
.onDelete((snap, context) => {
// Get an object representing the document prior to deletion
// e.g. {'name': 'Marie', 'age': 66}
const deletedValue = snap.data();
// perform desired operations ...
});
가장 많이 쓰이는 트리거로 생성, 변경, 삭제 중 아무 이벤트나 발생될 시 트리거 되는데,
onUpdate
와 마찬가지로snap
이 아닌change
를 파라미터로 가진다.
나 같은 경우에는 FCM을 보낼 때 한 명이 똑같은 형태의 알림, 예를 들어서 동일한 유저를 계속해서 언팔, 팔로우를 반복할 때,
Flutter
에서collection('collectionName').add({})
를 쓰면 동일한 문서가 계속 쌓이기 때문에 비효율적이라고 생각이 들어서
collection('collectionName').doc('docId').set({})
으로 사용하는데 이 때는onCreate
와onUpdate
모두 필요하므로onWrite
를 트리거로 사용해서 Cloud Function을 호출한다.
exports.modifyUser = functions.firestore
.document('users/{userID}')
.onWrite((change, context) => {
// Get an object with the current document value.
// If the document does not exist, it has been deleted.
const document = change.after.exists ? change.after.data() : null;
// Get an object with the previous document value (for update or delete)
const oldDocument = change.before.data();
// perform desired operations ...
});
// flutter code
static addNotificationToFirebase(String uid, String groupTodoId, String type,
String targetUid, String des) async {
String docUid = "";
groupTodoId.isEmpty ? docUid = uid : docUid = groupTodoId;
final documentRef = FirebaseFirestore.instance
.collection('users')
.doc(targetUid)
.collection("notification")
.doc(docUid);
return await documentRef.set({
'activationId': docUid,
'sendDay': DateTime.now().toUtc(),
'senderUid': uid,
'type': type,
'isConfirmed': "false",
'targetUid': targetUid,
'groupUid': groupTodoId,
'des': des,
});
}
//index.js (firebase cloude function)
exports.sendNotification = functions.firestore
.document("users/{uid}/notification/{notificationId}")
.onWrite(async (change, context) => {
const value = change.after.data();
const myUid = context.params.uid;
const senderUid = value.senderUid;
const groupUid = value.groupUid;
const type = value.type;
const usrDoc = await firestore.collection("users").doc(myUid).get();
const senDoc = await firestore.collection("users").doc(senderUid).get();
const tokens = usrDoc.data().tokens;
const lang = usrDoc.data().lang;
const senderName = senDoc.data().displayName;
console.log("tokens : ", tokens);
console.log("senderName : ", senderName);
var payload = await makePayload(type, groupUid, senderName, lang); // payload를 생성하는 `user-defined 함수`
admin.messaging().sendToDevice(tokens, payload);
});