접속 상태 구현
빌드하는 앱 유형에 따라 어떠한 사용자 또는 기기가 현재 온라인 상태인지 확인하는 것이 유용할 수 있습니다. 이러한 기능을 '접속 상태' 감지라고 합니다.
예를 들어 소셜 네트워크와 같은 앱을 빌드하거나 여러 IoT 기기를 배포할 경우 이 정보를 사용하여 채팅 가능한 온라인 상태인 친구 목록을 표시하거나 IoT 기기를 '최종 접속 시간' 순으로 정렬할 수 있습니다.
Firestore는 기본적으로 접속 상태를 지원하지 않지만 다른 Firebase 제품을 활용하여 접속 상태 시스템을 빌드할 수 있습니다.
솔루션: Cloud Functions 및 실시간 데이터베이스
Firebase 실시간 데이터베이스의 기본 접속 상태 기능에 Firestore를 연결하려면 Cloud Functions를 사용합니다.
실시간 데이터베이스를 사용하여 연결 상태를 보고한 후 Cloud Functions를 사용하여 이 데이터를 Firestore에 미러링합니다.
실시간 데이터베이스에서 접속 상태 사용
먼저 실시간 데이터베이스의 기본 접속 상태 시스템을 살펴보겠습니다.
웹
// Fetch the current user's ID from Firebase Authentication. var uid = firebase.auth().currentUser.uid; // Create a reference to this user's specific status node. // This is where we will store data about being online/offline. var userStatusDatabaseRef = firebase.database().ref('/status/' + uid); // We'll create two constants which we will write to // the Realtime database when this device is offline // or online. var isOfflineForDatabase = { state: 'offline', last_changed: firebase.database.ServerValue.TIMESTAMP, }; var isOnlineForDatabase = { state: 'online', last_changed: firebase.database.ServerValue.TIMESTAMP, }; // Create a reference to the special '.info/connected' path in // Realtime Database. This path returns `true` when connected // and `false` when disconnected. firebase.database().ref('.info/connected').on('value', function(snapshot) { // If we're not currently connected, don't do anything. if (snapshot.val() == false) { return; }; // If we are currently connected, then use the 'onDisconnect()' // method to add a set which will only trigger once this // client has disconnected by closing the app, // losing internet, or any other means. userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() { // The promise returned from .onDisconnect().set() will // resolve as soon as the server acknowledges the onDisconnect() // request, NOT once we've actually disconnected: // https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect // We can now safely set ourselves as 'online' knowing that the // server will mark us as offline once we lose connection. userStatusDatabaseRef.set(isOnlineForDatabase); }); });
이 예시는 완전한 실시간 데이터베이스 접속 상태 시스템으로 여러 연결 해제, 비정상 종료 등을 처리합니다.
Firestore에 연결
Firestore에서 비슷한 솔루션을 구현하려면 동일한 실시간 데이터베이스 코드를 사용한 후 Cloud Functions를 사용하여 실시간 데이터베이스와 Firestore를 동기화합니다.
프로젝트에 실시간 데이터베이스를 아직 추가하지 않았으면 지금 추가하고 위 접속 상태 솔루션을 포함합니다.
다음으로 다음 방법으로 접속 상태를 Firestore에 동기화합니다.
- 앱에서 기기가 오프라인 상태임을 파악하도록 로컬에서 오프라인 기기의 Firestore 캐시에 동기화합니다.
- 전역적으로 Cloud 함수로 사용하여 Cloud Firestore에 액세스하는 다른 모든 기기에서 이 특정한 기기가 오프라인 상태임을 파악하도록 합니다.
Firestore의 로컬 캐시 업데이트
첫 번째 문제, 즉 Firestore의 로컬 캐시 업데이트를 해결하는 데 필요한 변경사항을 살펴보겠습니다.
웹
// ... var userStatusFirestoreRef = firebase.firestore().doc('/status/' + uid); // Firestore uses a different server timestamp value, so we'll // create two more constants for Firestore state. var isOfflineForFirestore = { state: 'offline', last_changed: firebase.firestore.FieldValue.serverTimestamp(), }; var isOnlineForFirestore = { state: 'online', last_changed: firebase.firestore.FieldValue.serverTimestamp(), }; firebase.database().ref('.info/connected').on('value', function(snapshot) { if (snapshot.val() == false) { // Instead of simply returning, we'll also set Firestore's state // to 'offline'. This ensures that our Firestore cache is aware // of the switch to 'offline.' userStatusFirestoreRef.set(isOfflineForFirestore); return; }; userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() { userStatusDatabaseRef.set(isOnlineForDatabase); // We'll also add Firestore set here for when we come online. userStatusFirestoreRef.set(isOnlineForFirestore); }); });
이렇게 변경하면 이제 로컬 Firestore 상태가 항상 기기의 온라인/오프라인 상태를 반영합니다. 따라서 /status/{uid}
문서를 리슨하고 이 데이터를 사용하여 연결 상태를 반영하도록 UI를 변경할 수 있습니다.
웹
userStatusFirestoreRef.onSnapshot(function(doc) { var isOnline = doc.data().state == 'online'; // ... use isOnline });
Firestore 전역 업데이트
이제 애플리케이션이 자신의 온라인 접속 상태를 올바르게 보고합니다. 하지만 '오프라인' 상태 쓰기는 로컬에서만 수행되고 연결이 복원될 때 동기화되지 않으므로 다른 Firestore 앱에서는 이 상태가 아직 정확하게 보고되지 않습니다. 이 문제를 해결하기 위해 실시간 데이터베이스의 status/{uid}
경로를 감시하는 Cloud 함수를 사용합니다. 실시간 데이터베이스 값이 변경되면 Firestore에 동기화되므로 모든 사용자 상태가 올바르게 인식됩니다.
Node.js
firebase.firestore().collection('status') .where('state', '==', 'online') .onSnapshot(function(snapshot) { snapshot.docChanges().forEach(function(change) { if (change.type === 'added') { var msg = 'User ' + change.doc.id + ' is online.'; console.log(msg); // ... } if (change.type === 'removed') { var msg = 'User ' + change.doc.id + ' is offline.'; console.log(msg); // ... } }); });
이 함수를 배포하면 Firestore에서 실행되는 완전한 접속 상태 시스템이 완성됩니다. 다음은 where()
쿼리를 사용하여 온라인 또는 오프라인 상태로 전환하는 모든 사용자를 모니터링하는 예시입니다.
웹
firebase.firestore().collection('status') .where('state', '==', 'online') .onSnapshot(function(snapshot) { snapshot.docChanges().forEach(function(change) { if (change.type === 'added') { var msg = 'User ' + change.doc.id + ' is online.'; console.log(msg); // ... } if (change.type === 'removed') { var msg = 'User ' + change.doc.id + ' is offline.'; console.log(msg); // ... } }); });
제한사항
실시간 데이터베이스를 사용하여 Firestore 앱에 접속 상태를 추가하는 방법은 확장 가능하고 효율적이지만 몇 가지 제한사항이 있습니다.
- 디바운싱 - Firestore에서 실시간 변경사항을 리슨하는 경우 이 솔루션은 여러 변경사항을 트리거할 수 있습니다. 이러한 변경사항으로 인해 이벤트가 너무 많이 트리거되면 Firestore 이벤트를 수동으로 디바운싱합니다.
- 연결 - 이 구현에서는 Firestore가 아닌 실시간 데이터베이스에 대한 연결을 측정합니다. 각 데이터베이스에 대한 연결 상태가 동일하지 않으면 이 솔루션은 부정확한 접속 상태를 보고할 수 있습니다.
- Android - Android에서 실시간 데이터베이스는 비활성 상태가 60초간 지속되면 백엔드에서 연결을 끊습니다. 비활성 상태란 열린 상태의 리스너나 대기 중인 작업이 없다는 의미입니다. 연결을 열린 상태로 유지하려면 값 이벤트 리스너를
.info/connected
외의 경로에 추가하는 것이 좋습니다. 예를 들어 각 세션 시작 시FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced()
를 실행하는 방법이 있습니다. 자세한 내용은 연결 상태 감지를 참조하세요.