선행 작업
SDK 다운로드 [내용보기]
이메일/비밀번호 로그인 [내용보기]
FirebaseDatabase SDK 임포트
앞에서 다운로드한 SDK 중에 FirebaseDatabase.unitypackage 를 로그인을 완료한 유니티프로젝트에
임포트합니다. (PS: 임포트할 때, 오류가 생겼는데 다시 유니티를 실행하고 임포트하니 잘되네요.)
파이어베이스 콘솔창 설정 변경
콘솔창에서 Database > 시작하기 버튼 클릭을 합니다.
변경된 google-services.json 파일을 다시 다운받고 유니티의 google-services 파일은 변경해줍니다.
( 위 내용은 앞의 포스팅에서 설명했습니다. )
SetEditorServiceAccountEmail 가져오기
지금 세팅하는 것은 유니티 에디터에서 파이어베이스 데이터베이스를 사용하려면 필요한 세팅입니다. ( 이 세팅을 안해도 모바일에서는 잘 됩니다. )
Firebase 콘솔창에서 좌측 상단 톱니바퀴를 누르시고 설정으로 들어가서 프로젝트ID 를 복사합니다.
https://console.cloud.google.com/iam-admin/serviceaccounts/project?project=프로젝트ID 를 인터넷 주소창에 입력합니다.
그럼 프로젝트의 서비스 계정 어쩌고 하는 페이지로 이동하는데요. 작업이라고 적힌 버튼을 클릭해서 키 만들기를 선택합니다.
P12 형식을 사용하는 코드와의 하위 호환용 을 선택하고 만들기 버튼을 눌러주세요.
그럼 파일 하나를 다운 받는데요. 그거를 유니티의 Editor Defult Resources 로 붙여넣기 합니다.
DatabaseReference 가져오기
먼저 유저정보를 다른 스크립트에서 가져오기 위해
Login.cs 의 FirebaseUser user; 변수를 스테틱으로 선언합니다.
FirebaseUser user -> public static FirebaseUser user;
유니티에 RealtimeDatabase.cs 를 만듭니다.
아래 코드를 입력합니다.
using UnityEngine;
using Firebase.Unity.Editor;
using Firebase.Database;
using Firebase;
public class RealtimeDatabase : MonoBehaviour
{
FirebaseApp firebaseApp;
DatabaseReference databaseReference;
private void Awake()
{
firebaseApp = FirebaseDatabase.DefaultInstance.App;
firebaseApp.SetEditorDatabaseUrl("https://firsttestapp-eac16.firebaseio.com/");
databaseReference = FirebaseDatabase.DefaultInstance.RootReference;
FirebaseApp.DefaultInstance.SetEditorFileName("파일이름.p12");
// 아래 비밀번호에는 특별하게 설정한거 없으면 notasecret 일 겁니다.
FirebaseApp.DefaultInstance.SetEditorP12Password("notasecret");
}
}
firebaseApp.SetEditorDatabaseUrl( "콘솔창의 정보를 입력해야 한다." );
데이터 쓰기
RealtimeDatabase.cs 에 아래 코드를 추가합니다.
public void InitDatabase()
{
if (Login.user != null)
{
WriteNewUser(Login.user.UserId , Login.user.DisplayName, Login.user.Email);
}
}
private void WriteNewUser(string uid, string name, string email)
{
User user = new User(name, email);
string json = JsonUtility.ToJson(user);
databaseReference.Child("users").Child(uid).SetRawJsonValueAsync(json);
}
Hierarchy 에 Database 라는 오브젝트를 만들고 RealtimeDatabase.cs를 연결해줍니다.
데이터를 저장시켜주는 버튼을 만든 다음 클릭이벤트에 Database 를 연결해줍니다.
모바일로 빌드하고 저장버튼을 누르면 유저의 이름과 이메일주소가 저장됩니다.
콘솔창에서 저장된 데이터가 보이네요~
databaseReference.Child("users").Child(uid).SetRawJsonValueAsync(json);
는 동일한 uid 가 들어오면 SetRawJsonValueAsync 의 데이터로 덮어 씌웁니다.
특정 변수값만 변경하고 싶다면
databaseReference.Child("users").Child(uid).Child("username").SetValueAsync(name);
를 입력하시면 됩니다.
특정 필드 업데이트
다른 하위 노드를 덮어쓰지 않고 특정 하위 노드에 여러개를 동시에 쓰기를 하려면
UpdateChildrenAsync()
updateChildValues() 메소드를 사용합니다.
public void OnClickUpdateChild()
{
Dictionary<string, object> childUpdates = new Dictionary<string, object>();
childUpdates["/users/" + Login.user.UserId + "/" + "username"] = Login.user.DisplayName;
childUpdates["/users/" + Login.user.UserId + "/" + "score"] = 100;
databaseReference.UpdateChildrenAsync(childUpdates);
}
데이터 푸쉬
전투 로그처럼 데이터를 덮어씌우지 않고 새로 저장해야 할 때가 있습니다.
이럴 때는 Push() 를 사용 합니다.
public void OnClickPush()
{
Dictionary<string, object> childUpdates = new Dictionary<string, object>();
childUpdates["/users/" + Login.user.UserId + "/" + "username"] = Login.user.DisplayName;
childUpdates["/users/" + Login.user.UserId + "/" + "score"] = 100;
databaseReference.Push().Child(Login.user.UserId).UpdateChildrenAsync(childUpdates);
}
트랜잭션으로 저장하기
여러 사람이 쓰는 데이터는 동시 수정으로 데이터가 손상될 수 있습니다.
예로 랭킹같은 경우 여러 유저가 랭킹데이터를 수정하기 때문에 수정 작업 중에
데이터가 손상되어 각 유저의 랭킹정보가 달라질 수 있죠.
이럴 때 사용하는 것이 트랜잭션이라고 합니다.
public void OnClickTransactionSave()
{
const int MaxScoreRecordCount = 5;
int score = Random.Range(0, 100);
string email = "testEmail";
databaseReference.Child("users").RunTransaction(mutableData => {
List<object> leaders = mutableData.Value as List<object>;
if (leaders == null)
{
leaders = new List<object>();
}
// 랭킹에 등록된 점수를 비교합니다.
else if (mutableData.ChildrenCount >= MaxScoreRecordCount)
{
long minScore = long.MaxValue;
object minVal = null;
foreach (var child in leaders)
{
if (!(child is Dictionary<string, object>))
continue;
long childScore = (long)((Dictionary<string, object>)child)["score"];
if (childScore < minScore)
{
minScore = childScore;
minVal = child;
}
}
if (minScore > score)
{
// 현재 점수가 최하위 점수보다 낮으면 중단합니다.(랭킹에 못오르니깐)
return TransactionResult.Abort();
}
// 기존 최하위 데이터를 제거합니다.(랭킹 변경)
leaders.Remove(minVal);
}
Dictionary<string, object> newScoreMap = new Dictionary<string, object>();
newScoreMap["score"] = score;
newScoreMap["email"] = email;
leaders.Add(newScoreMap);
mutableData.Value = leaders;
return TransactionResult.Success(mutableData);
});
}
기록을 비교하는 중간의코드를 제외하면 이해하기 쉬우실겁니다.
// 가져올 데이터의 이름
databaseReference.Child("users").RunTransaction(mutableData => {
List<object> leaders = mutableData.Value as List<object>;
/* 작업할 내용 시작
// 랭킹 5위의 기록을 비교하면서
// 현재 점수가 5위 안에 들어갈 수 있는지 등을
// 체크
작업할 내용 끝 */
Dictionary<string, object> newScoreMap = new Dictionary<string, object>();
leaders.Add(newScoreMap);
mutableData.Value = leaders; // 랭킹 5위의 기록을 덮어씌웁니다.
return TransactionResult.Success(mutableData); // 값을 전달합니다.
}
기타 설명
네크워크가 종료되어도 자동으로 로컬에 저장한 뒤에 해당 데이터를 원격 데이터베이스 서버 및 다른 클라이언트와 '최선을 다해' 동기화 한다고 하네요. ( 최선을 다한다는게 무섭네요? )
네트워크에 다시 연결되면 앱에서 해당 이벤트 세트를 수신하여 클라이언트와 현재 서버 상태를 동기화한다고 합니다.
그래서 별도의 코드를 작성할 필요가 없다네요.
데이터 삭제
데이터를 삭제하는 코드는 간단합니다.
지울려는 대상.RemoveValueAsync() 를 하면 됩니다.
public void OnClickRemove()
{
databaseReference.Child("users").Child("노드이름").Child("노드이름")
.RemoveValueAsync();
}
데이터 읽기
한번만 읽어오기
public void OnClickDataRead()
{
FirebaseDatabase.DefaultInstance
.GetReference("users") // 읽어올 데이터 이름
.GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
// DataSnapshot 타입에 저장된 값 불러오기
foreach (var item in snapshot.Children)
{
Debug.Log(item.Child("email").Value);
Debug.Log(item.Child("score").Value);
}
}
});
}
이벤트 수신해서 읽어오기
데이터베이스에 저장된 값이 변경/삭제/이동/추가 등을 감지하고 각 클라이언트에게 변경된 내용을
동기화시켜줄 수 도 있습니다.
public void OnClickEventListner()
{
DatabaseReference reference = FirebaseDatabase.DefaultInstance.GetReference("users");
reference.ValueChanged += HandleValueChanged;
reference.ChildAdded += HandleChildAdded;
reference.ChildChanged += HandleChildChanged;
reference.ChildRemoved += HandleChildRemoved;
reference.ChildMoved += HandleChildMoved;
}
//users 데이터 전체의 변화에 수신
void HandleValueChanged(object sender, ValueChangedEventArgs args)
{
if (args.DatabaseError != null)
{
Debug.LogError(args.DatabaseError.Message);
return;
}
SnapshotAllDataRead(args.Snapshot);
}
//users 데이터의 하위목록의 값이 추가되면 수신
void HandleChildAdded(object sender, ChildChangedEventArgs args)
{
if (args.DatabaseError != null)
{
Debug.LogError(args.DatabaseError.Message);
return;
}
SnapshotDataRead(args.Snapshot);
}
//users 데이터의 하위목록의 값이 변경되면 수신
void HandleChildChanged(object sender, ChildChangedEventArgs args)
{
if (args.DatabaseError != null)
{
Debug.LogError(args.DatabaseError.Message);
return;
}
SnapshotDataRead(args.Snapshot);
}
//users 데이터의 하위목록의 값이 제거되면 수신
void HandleChildRemoved(object sender, ChildChangedEventArgs args)
{
if (args.DatabaseError != null)
{
Debug.LogError(args.DatabaseError.Message);
return;
}
SnapshotDataRead(args.Snapshot);
}
//users 데이터의 하위목록의 값이 이동하면 수신
void HandleChildMoved(object sender, ChildChangedEventArgs args)
{
if (args.DatabaseError != null)
{
Debug.LogError(args.DatabaseError.Message);
return;
}
SnapshotDataRead(args.Snapshot);
}
// 데이터를 읽어온다.
public void SnapshotDataRead(DataSnapshot Snapshot)
{
DataSnapshot snapshot = Snapshot;
foreach (var item in snapshot.Children)
{
Debug.Log(item.Value);
}
}
public void SnapshotAllDataRead(DataSnapshot Snapshot)
{
foreach (var item in Snapshot.Children)
{
Debug.Log(item.Child("email").Value +" : "+ item.Child("score").Value);
}
}
데이터 정렬
하위 키의 값에 따라 / 하위 키에 따라 / 하위 값에 따라 결과를 정렬할 수 있습니다.
아래는 score 로 정렬한 방법에 대한 내용입니다.
public void OnClickOrderby()
{
FirebaseDatabase.DefaultInstance
.GetReference("users").OrderByChild("score")
.GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
}
else if (task.IsCompleted)
{
DataSnapshot snapshot = task.Result;
foreach (var item in snapshot.Children)
{
Debug.Log(item.Child("email").Value + " : " + item.Child("score").Value);
}
}
});
}
보안규칙
파이어베이스(firebase) 콘솔창의 Database -> 규칙탭에서 유저의 쓰기/읽기 권한을 조절할 수 있습니다.
예로 쓰기할때는
자신의 유저노드에만 쓸 수 있고 타인의 유저노드는 읽기만 가능하도록 할 수 있습니다.
위의 내용은 디바이스에서 테스트할 때만 적용됩니다.
규칙에서는 데이터 관련 다양한 규칙들을 설정할 수 있습니다.
입력 글자 수 제한 등
'프로그램 강좌 > 유니티 + 파이어베이스' 카테고리의 다른 글
파이어베이스(firebase) Cloud Functions #2 (0) | 2017.11.22 |
---|---|
파이어베이스(firebase) Cloud Functions #1 (2) | 2017.11.22 |
파이어베이스 유니티로 구글로그인 (10) | 2017.11.17 |
파이어베이스 유니티 로그인(이메일/비밀번호) (4) | 2017.11.16 |
댓글