Search
🈳

TIL: firebase.firestore.Query.onSnapshot()으로 받은 데이터가 null이에요!

Date
2021/03/03
Tags
TIL
Backend
Firebase
Created by

Documentation를 잘 읽어보도록 하자

로컬 변경 이벤트

앱에서 로컬로 쓰면 즉시 스냅샷 리스너가 호출됩니다. 이는 '지연 시간 보정'이라는 중요한 기능 때문입니다. 쓰기를 수행하면 데이터가 백엔드로 전송되기 전에 리스너에 새 데이터가 통보됩니다.
검색된 문서의 metadata.hasPendingWrites 속성은 문서에 아직 백엔드에 기록되지 않은 로컬 변경사항이 있는지 여부를 나타냅니다. 이 속성을 사용하여 스냅샷 리스너가 수신한 이벤트의 소스를 확인할 수 있습니다.

문제 상황

토이 프로젝트 "homete!"에 Firebase Firestore를 실시간 데이터베이스 서비스로 사용하고 있다. firebase.firestore.Query.onSnapshot() 메소드로 문서가 추가되는 것을 대기하다가, 문서가 추가되면 데스크탑 알림이 뜨게 하고 싶었다.
하지만 새로 추가된 문서에는 timestamp가 null로 찍혀 나왔다. 무엇이 문제일까?
const listener = db .collection("hometes") // ... .onSnapshot((querySnapshot) => { querySnapshot.docChanges().forEach((change) => { if (change.type === "added") { // 문서가 새로 추가됨 // 여기서 데스크탑 알림을 발생시키면 되지 않을까? } if (change.type === "modified") { // 문서가 수정됨 } if (change.type === "removed") { // 문서가 삭제됨 } }); });
JavaScript
공식 Reference를 잘 보면, 데이터가 백엔드로 전송되기 전에도 리스너에 새 데이터가 통보된다고 적혀 있다. 즉, 리스너에서 받은 데이터는 아직 백엔드로 전송되지 않은 데이터이고, 일부 필드가 null일 수도 있다는 것이다.
그런데 왜 서버로 전송되지 않은 데이터가 일부 null 필드를 가지는가? 나 같은 경우에는 timestamp 필드를 다음 값으로 사용하고 있었고, 이는 공식에서도 권장하는 방법이다.
await db.collection("hometes").doc().set({ // ... timestamp: firebase.firestore.FieldValue.serverTimestamp(), });
JavaScript
이렇게 timestamp나 다른 필드 값을 서버 값으로 사용하는 경우, 클라이언트에서 보낸 데이터는 일부 필드가 누락된 채로 "added" 이벤트가 발생된다.

해결법

클라이언트 로그를 잘 보면, 클라이언트 측에서 문서가 새로 추가될 때는 일단 "added" 이벤트가 먼저 발생하고, 서버에 데이터가 추가된 뒤 "modified" 이벤트가 발생하는 것을 알 수 있다. 따라서 코드를 다음과 같이 수정해주어야 한다.
const listener = db .collection("hometes") // ... .onSnapshot((querySnapshot) => { querySnapshot.docChanges().forEach((change) => { if (change.type === "added") { // 문서가 새로 추가됨. 하지만 서버에는 아직 추가되지 않음 // 지금 받은 데이터는 불완전한 데이터! } if (change.type === "modified") { // 문서가 수정됨 (클라이언트에서 추가한 데이터가 서버에 완전히 도착했음을 의미함) // 여기서 Notification을 발생! } if (change.type === "removed") { // 문서가 삭제됨 } }); });
JavaScript

References