[Flutter-Cloud Firestore Trigger]onWrite, onCreate, onUpdate, onDelete

Ohgyuchan·2022년 5월 8일
0

Flutter

목록 보기
12/25
post-thumbnail

Firebase 공식 문서를 참고하여 작성하였음
Cloud Function으로 문서에 변화를 감지해서, 특정 코드를 실행할 때 사용하는 것이다. Cloud Function은 무료 Spark 요금제가 아닌 유료 종량제 요금으로 변경해야 사용할 수 있지만, 연습용으로는 과금이 거의 없기 때문에 괜찮다고 생각한다. 하루 20만번의 Functino 호출까지는 무료.
주로 FCM을 사용할 때 이용하는데, 그 외에도 흔히 SNS에서 쓰는 팔로잉, 팔로우, 좋아요 등의 기능들에 탈퇴한 회원의 데이터를 삭제할 때도 유용하다.

Trigger 이름설명
onCreate해당 문서가 처음으로 생성(기록)될 때 트리거
onUpdate이미 존재하는 문서의 Field 값이 Update(값이 변경)될 때 트리거
onDelete데이터가 있는 문서가 삭제될 때 트리거
onWrite위의 onCreate, onUpdate, onDelete 세 개의 트리거 되는 조건에 트리거, 즉, 해당 문서가 create, update, delete 될 때 트리거

1. 특정 문서 지정 이벤트 listen

users collection의 terman 문서의 변화에 의해 트리거되는 것인데, onWrite 이기 때문에 terman document에 생성, 변경, 삭제 어떤 변화가 생겨도 트리거 된다.

exports.myFunctionName = functions.firestore
    .document('users/terman').onWrite((change, context) => {
      // ... Your code here
    });

2. 컬렉션 지정(와일드 카드를 사용한 문서 그룹 지정) 이벤트 Listen

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"}
    });

3. onCreate

아래와 같은 코드가 있다고 가정했을 때, onCreate 내 builder 내 파라미터이 snap은 추가되는 데이터를 contextdocumentIDuserId를 나타낸다.

//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 ...
    });

4. onUpdate

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 ...
    });

5. onDelete

문서가 삭제될 때의 트리거이므로 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 ...
    });

6. onWrite

가장 많이 쓰이는 트리거로 생성, 변경, 삭제 중 아무 이벤트나 발생될 시 트리거 되는데, onUpdate 와 마찬가지로 snap 이 아닌 change를 파라미터로 가진다.

나 같은 경우에는 FCM을 보낼 때 한 명이 똑같은 형태의 알림, 예를 들어서 동일한 유저를 계속해서 언팔, 팔로우를 반복할 때,

Flutter에서 collection('collectionName').add({}) 를 쓰면 동일한 문서가 계속 쌓이기 때문에 비효율적이라고 생각이 들어서

collection('collectionName').doc('docId').set({})으로 사용하는데 이 때는 onCreateonUpdate 모두 필요하므로 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 ...
    });

6.1. onWrite 예시 코드

// 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);
  });
profile
Flutter 개발자

0개의 댓글