1. 유저데이터 저장 시 고민 사항
- 유저데이터를 저장하는 시점 :
- Realtime Database의 특성상 권한이 부여된 후 저장이 되는 것이 바람직
- 가입 메소드에서 수행하면 아직 미인증 상태이므로 기본을 설정된 데이터베이스 권한으로는 저장할 수 없음
- 저장 위치는 onAuthChange 메소드에서 저장
- onAuthChange 저장 메소드 위치 시 로그인할 때마다 프로세스 실행
- 새로 가입한 유저데이터를 저장하는 프로세스를 로그인할 때마다 저장하는 것은 Realtime Database 특성상, 만약에 Users 위치를 클라이언트에서 변화 감지해서 데이터를 받아오는 코드가 작성되어 있다면 불필요한 데이터 로딩을 일으킬 수 있음.
- Realtime Database에 Users데이터가 저장되어 있는지 확인 후 저장을 수행하거나 로컬(localStorage, indexedDB 등등)에 해당 유저의 데이터를 데이터베이스에 기록했는지 여부를 저장하여 확인 후 저장을 수행.
- Users데이터가 저장되어 있는지 매번 확인하는 것은 불필요한 데이터베이스 접근이므로 두가지 방법을 함께 사용하여 코드를 작성.
2. 코드 작성
- offline storage중 하나인 IndexedDB에 가입 시 데이터를 저장하는 코드 추가
- IndexedDB에 대한 내용은 아래 링크 참조
- 코드 설명:
- init메소드에는 saveUserAtIndexedDB메소드에서 사용할 상수들이 정의
- saveUserAtIndexedDB는 가입 시 수행되는 createEmailUser메소드와 signInWithPopup메소드에서 실행 중에 받게되는 user object를 파라미터로 받게되고, userName파라미터는 이메일 가입시에는 유저 이름 파라미터를 user object안에서는 받을 수 없으므로 따로 받도록 파라미터를 추가
- isSave파라미터는 후에 Realtime Database 에 유저 데이터를 저장 한 후에 값을 변경 저장하기 위한 구분 값.
- saveUserAtIndexedDB는 createEmailUser메소드와 signInWithPopup 메소드 안에 추가
/**
* 초기 필드 변수 할당
*/
FirebaseChat.prototype.init = function(){
//...생략
this.INDEXDB_DB_NAME = "USER";
this.INDEXDB_VERSION = 1;
this.INDEXDB_STORE ="Users";
}
/**
* User데이터를 IndexedDB에 저장 및 데이터 변경
*/
FirebaseChat.prototype.saveUserAtIndexedDB = function(user, userName, isSave){
if(indexedDB){
var request = indexedDB.open(this.INDEXDB_DB_NAME , this.INDEXDB_VERSION); // 데이터베이스 접근 요청
var objectName = this.INDEXDB_STORE
request.onupgradeneeded = function(){ // 데이터 베이스 생성 또는 버전 업그레이드 시 수행
var db = request.result; //데이터 베이스 객체
var store = db.createObjectStore(objectName, {keyPath :"uid"}); // 테이블에 해당하는 Object 생성 및 키값 설정
}
request.onsuccess = function() { // 데이터베이스 접근 요청이 성공 시
var db = request.result;
var tx = db.transaction(objectName, "readwrite"); //읽기쓰기 권한으로 트랜잭션을 얻음
var store = tx.objectStore(objectName);
store.get(user.uid).onsuccess = function(event){ //user.uid 기준으로 IndexedDB 데이터를 읽어온다
var data = event.target.result;
console.log('IndexedDB query 결과 : ', data);
console.log('saveUserAtIndexedDB isSave 파라미터', isSave);
if(!data){ //데이터가 없으면 저장
store.put({
uid: user.uid
, email: user.email
, photoURL : user.photoURL ? user.photoURL : ''
, displayName : userName ? userName : user.displayName
, isSave : false
});
}
if(data && isSave){ // 데이터가 존재하고 isSave 파라미터 true이면 데이터를 업데이트
store.put({
uid: user.uid
, email: user.email
, photoURL : user.photoURL ? user.photoURL : ''
, displayName : userName ? userName : user.displayName
, isSave : true
});
}
}
tx.oncomplete = function() {
console.log('IndexedDB 트랜잭션 완료')
db.close();
};
}
}
}
/**
* 이메일로 가입 처리
*/
FirebaseChat.prototype.createEmailUser = function(){
//...생략
var cbCreateUserWithEmail = function(user){
this.saveUserAtIndexedDB(user ,userName, false); //추가 메소드
//...생략
}
//...생략
}
/**
* 지속성 설정 후 sign-in 팝업창
*/
FirebaseChat.prototype.signInWithPopup = function(provider) {
var cbSignIn = function(result){
console.log('로그인 성공')
this.saveUserAtIndexedDB(result.user, null , false);
}
//...생략
}
- onAuthChange메소드 안 setLogin메소드안에서 IndexedDB의 데이터의 데이터로 Users 데이터베이스에 저장하였는지 여부(isSave) 를 확인 한 후 저장하지 않았으면 Realtime Database에 유저데이터를 저장하고 다시 IndexedDB에 값을 저장하였다는 것을 표시하는 코드 추가
- 코드 설명:
- 가입 후 onAuthChange메소드 수행 이후 동작
- IndexedDB의 데이터의 데이터로 Users 데이터베이스에 저장하였는지 여부(isSave) 를 확인하는 코드인 checkAndSaveUser메소드는 saveUserAtIndexedDB메소드와 비슷하게 진행
- IndexedDB를 오픈하고, IndexedDB안에 User Object를 User의 uid 값을 기준으로 검색하여 isSave값이 false인 경우에만 saveUserAtRealDB 메소드를 실행하여 Realtime Database에 유저 데이터를 저장
- saveUserAtRealDB 메소드에서 사용된 Realtime Database 의 Reference객체의 메소드
once
는 데이터를 한번 수신하고, 더 이상 변경사항에 대해서는 데이터를 수신하지 않는 메소드이며, cbUserRefResult 콜백 함수가 수행. cbUserRefResult 콜백함수에서는 dataSnapShot 객체를 얻을 수 있고 자식객체가 있는지를 체크한 후 없다면 set 메소드로 저장을 수행
- 코드 설명:
/**
* 인증 정보가 변화 되었을 시에 변화
*/
FirebaseChat.prototype.onAuthChange = function(user){
if (user) {
console.log('user로그인 : ',JSON.stringify(user));
this.setLogin(user); //파라미터 추가
} else {
console.log('로그아웃');
this.setLogOut();
}
}
/**
* 로그인 후 세팅
*/
FirebaseChat.prototype.setLogin = function(user){ //user 파라미터 추
//...생략
this.checkAndSaveUser(user);
}
/**
* 신규 User를 IndexedDB에서 체크 후 저장
*/
FirebaseChat.prototype.checkAndSaveUser = function(user){
try{
var request = indexedDB.open(this.INDEXDB_DB_NAME , this.INDEXDB_VERSION);
var objectName = this.INDEXDB_STORE;
request.onsuccess = function() {
var db = request.result;
var tx = db.transaction(objectName, "readwrite");
var store = tx.objectStore(objectName);
store.get(user.uid).onsuccess = function(event){ // indexedDB에서 uid값으로 쿼리
var data = event.target.result;
console.log('IndexedDB query 결과 : ', data);
console.log('checkAndSaveUser isSave' , data.isSave);
if(!data.isSave){
fbChat.saveUserAtRealDB(data);
}
}
tx.oncomplete = function() {
console.log('IndexedDB 트랜잭션 완료')
db.close();
};
}
}catch(e){
this.saveUserAtRealDB(user); //indexDB 확인에 실패 하면 Reatime Database 조회 후
}
}
/**
* Realtime Database에서 Users 데이터를 체크 후 저장
*/
FirebaseChat.prototype.saveUserAtRealDB = function(user){
var userRef = this.database.ref('Users/' + user.uid);
var cbUserRefResult = function(dataSnapShot){ // User Ref에 데이터가 없을 경우 데이터 저장
if(!dataSnapShot.hasChildren()){
console.log('saveUserAtRealDB 저장');
userRef.set({ //데이터 저장
email: user.email
, profileImg: user.photoURL ? user.photoURL : ''
, userName : user.displayName
}).then(cbUserAfterSave.bind(this));
}
}
var cbUserAfterSave = function(){ //Realtime Database에 저장이 완료된 후 로컬 IndexedDB isSave 값을 true로 변경
this.saveUserAtIndexedDB(user, null, true);
}
userRef.once('value') // 1회만 검색하고 변경데이터를 수신하지 않음
.then(cbUserRefResult.bind(this));
}
3. 테스트
- 점검을 할 때는 Firebase console화면에서 테스트에 사용할 아이디는 초기화 해주세요.
- 가입을 다시 수행하고 개발자 도구를 열어 크롬의 경우 Application 탭으로 들어가 봅니다.
- Firefox는 개발자 도구에 저장소 라는 탭이 있습니다.
- 좌측 IndexedDB 라는 항목을 보시면 아래와 같이 저장이 되어 있는 것을 확인할 수 있습니다.
- isSave 항목이 정상적으로 모두 true로 변경되어 있음을 확인 할수 있습니다.
- Firebase Realtime Database에서 확인