[SKKU DT] 31일차 -유니티 네트워크 -파이어베이스(Firebase) 실시간 데이터베이스, Storage

2023. 12. 12. 18:14SKKU DT

728x90
반응형

이전 글에서 마지막 부분 실시간 데이터베이스에서, 자료 표시 형태가 조금 다른데, 중괄호 형태는 SetValueAsync를, Hierarchy 형태는 SetRawJsonValueAsync를 사용하였다.

databaseReference.Child(name).SetValueAsync(jsonData);
databaseReference.Child(name).SetRawJsonValueAsync(jsonData);

 

 


 

 

데이터베이스 로드

이전 글에서 데이터베이스를 저장했던 스크립트 아래에 이번에는 데이터를 로드하는 코드를 작성해서 넣는다.

public void OnClickLoad()
{
    databaseReference.Child(name).GetValueAsync().ContinueWithOnMainThread(task =>
    {
        if (task.IsCanceled)
        {
            action.text = "로드 취소";
        }
        if (task.IsFaulted)
        {
            action.text = "로드 실패";
        }
        else
        {
            var dataSnapshot = task.Result;
            string dataString = "";
            foreach (var data in dataSnapshot.Children)
            {
                dataString += data.Key + "   " + data.Value + "\n"; //줄바꿈 하면서 키 + 밸류
            }
            action.text = dataString;
            Debug.Log("로드" + dataString);
        }
    });
}

 

 

전체 코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Firebase.Database;
using TMPro;
using Firebase.Extensions;

public class DatabaseManager : MonoBehaviour
{
    public TMP_InputField nameInput;
    public TMP_InputField levelInput;
    public TMP_InputField goldInput;
    public TMP_Text action;

    public string name;
    public string level;
    public string gold;

    public class Data
    {
        public string level;
        public string gold;

        public Data(string level, string gold)
        {
            this.level = level;
            this.gold = gold;
        }
    }
    private DatabaseReference databaseReference; //변수 초기화
    void Start()
    {
        databaseReference = FirebaseDatabase.DefaultInstance.RootReference; //실시간 데이터베이스 웹의 URL 부분이 Root
    }

    public void OnClickSave()
    {
        name = nameInput.text;
        level = levelInput.text;
        gold = goldInput.text;

        var data = new Data(level, gold);
        string jsonData = JsonUtility.ToJson(data);

        //databaseReference.Child(name).SetValueAsync(jsonData);
        databaseReference.Child(name).SetRawJsonValueAsync(jsonData); //Root에 Child에 name을 쓰겠다.

        action.text = "저장";
    }

    public void OnClickLoad()
    {
        databaseReference.Child(name).GetValueAsync().ContinueWithOnMainThread(task =>
        {
            if (task.IsCanceled)
            {
                action.text = "로드 취소";
            }
            if (task.IsFaulted)
            {
                action.text = "로드 실패";
            }
            else
            {
                var dataSnapshot = task.Result;
                string dataString = "";
                foreach (var data in dataSnapshot.Children)
                {
                    dataString += data.Key + "   " + data.Value + "\n"; //줄바꿈 하면서 키 + 밸류
                }
                action.text = dataString;
                Debug.Log("로드" + dataString);
            }
        });
    }
}

 

 

이렇게 하면 직전에 저장했던 값이 최상단의 action.text자리에 다시 들어온다.

 

 


 

 

채팅용 데이터베이스

빠르게 읽고 쓰는 데이터베이스처리의 연속이라고 볼 수 있다.

 

 

파이어베이스 웹에서 직접 아래와 같은 구조를 만든다.

 

 

이전에 만들었던 FirebaseController 스크립트를 수정한다. 익명 로그인 아래에 이어서 쓴다.

public void ReadChatMessage()
{
    DatabaseReference chatDB = FirebaseDatabase.DefaultInstance.GetReference("ChatMessage");
    chatDB.GetValueAsync().ContinueWithOnMainThread(task =>
    {
        if (task.IsFaulted)
        {
            Debug.Log("read error");
        }
        if(task.IsCompleted)
        {
            DataSnapshot snapshot = task.Result;
            Debug.Log("" + snapshot.ChildrenCount); //그 밑에 있는 메시지(인덱스1)가 몇 개인가를 센다.
            foreach(var data in snapshot.Children)
            {
            Debug.Log(data.Key + "   " + data.Child("username").Value.ToString() + "   " + data.Child("message").Value.ToString()); //유저 이름과 메시지 내용을 가져온다.
            }
        }
    });
}

 

 

전체 코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Firebase.Auth;
using System.Threading.Tasks; //Task문법을 사용할 예정
using TMPro;
using Firebase.Extensions;
using System;
using UnityEngine.SceneManagement;
using Firebase.Database;

public class FirebaseController : MonoBehaviour
{
    private FirebaseAuth auth;
    private FirebaseUser user;
    void Start()
    {
        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => //버전이 맞는지 체크
        {
            if(task.Result == Firebase.DependencyStatus.Available) //괜찮으면, 문제 없이 시작된다면,
            {
                FirebaseInit();
            }
            else //괜찮지 않다면
            {
                Debug.LogError("");
            }
        });
    }
    private void FirebaseInit()
    {
        auth = FirebaseAuth.DefaultInstance;
        auth.StateChanged += AuthStateChanged; //상태 변화가 있으면 함수를 불러와라
    }
    private void AuthStateChanged(object sender, EventArgs e)
    {
        FirebaseAuth senderAuth = sender as FirebaseAuth; //무슨 일이 생기면 sender와 EventArgs e가 작용한다.
        if(senderAuth != null) //유저가 있다면
        {
            user = senderAuth.CurrentUser;
            if(user != null) //유저가 무엇을 하려 하면(null이 아니면)
            {
                Debug.Log(user.UserId);
            }
        }
    }

    public void SignIn()
    {
        SignInAnonymous();
    }
    private Task SignInAnonymous()
    {
        return auth.SignInAnonymouslyAsync().ContinueWithOnMainThread(task =>
        {
            if(task.IsFaulted)
            {
                Debug.Log("SignIn fail");
            }
            else if(task.IsCompleted)
            {
                Debug.Log("SignIn complete");
                SceneManager.LoadScene(1);
            }
        });
    }
    
    public void ReadChatMessage()
    {
        DatabaseReference chatDB = FirebaseDatabase.DefaultInstance.GetReference("ChatMessage");
        chatDB.GetValueAsync().ContinueWithOnMainThread(task =>
        {
            if (task.IsFaulted)
            {
                Debug.Log("read error");
            }
            if(task.IsCompleted)
            {
                DataSnapshot snapshot = task.Result;
                Debug.Log("" + snapshot.ChildrenCount); //그 밑에 있는 메시지(인덱스1)가 몇 개인가를 센다.
                foreach(var data in snapshot.Children)
                {
                Debug.Log(data.Key + "   " + data.Child("username").Value.ToString() + "   " + data.Child("message").Value.ToString()); //유저 이름과 메시지 내용을 가져온다.
                }
            }
        });
    }
}

 

 

버튼에 ReadChatMessage() 함수를 매핑하고 누르면 콘솔 창에 불러와진다. 숫자 1은 채팅 개수, 아래는 채팅의 내용이다.

 

 

이번엔 두 개의 채팅을 가져온다.

스크립트의 수정은 없이 두 채팅이 가져와진다.

 

 


 

 

채팅 시스템 만들기

 

InputField와 ㅠㅕㅅ새ㅜ 

 

 

컴포넌트를 통해서 Image들을 Vertical Align해주고 [Content Size Fitter]의 [Vertical Fit]을 [Preferred Size]로 변경했다.

 

 

각각의 Image에는 유저 이름과 채팅 내용 두 개의 텍스트가 들어있다.

 

 

Scroll View의 좌우 이동 해제는 Horizontal의 체크박스를 끄면 된다.

 

 

채팅은 다음에 제대로 진행할 예정....

 

 


 

 

FireStore

패키지에서 FirebaseFirestore.unitypackage를 import 한다.

 

 

Firestore Database를 만든다.

보안 규칙은 테스트 모드에서 시작한다.

 

 

컬렉션 시작을 눌러 내용을 채운다.

 

 

json이 업데이트 되었으므로 유니티의 [Assets] - [StreamingAssets] 폴더에 json을 다시 넣는다.

 

 

FireStoreController 스크립트를 생성한다. Firebase.Firestore; 네임스페이스 추가

using Firebase.Firestore;

 

 

18번째 줄 DocumentReference docRef = db.Collection("data").Document("doc");에서, "data"에 접근하면 이미 위에서 만들었던 컬렉션에서 내용을 수정할 수 있다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Firebase.Firestore;
using Firebase;
using Firebase.Extensions;

public class FireStoreController : MonoBehaviour
{
    void Start()
    {
        FirebaseApp app = FirebaseApp.DefaultInstance;
    }

    public void AddData()
    {
        FirebaseFirestore db = FirebaseFirestore.DefaultInstance;
        DocumentReference docRef = db.Collection("data").Document("doc");
        Dictionary<string, object> user = new Dictionary<string, object>
        {
            {"gold", 100}, {"job", "Turing" }, {"user", "1912" }
        };
        docRef.SetAsync(user).ContinueWithOnMainThread(task =>
        {
            if(task.IsFaulted)
            {
                Debug.Log("Fail");
            }
            if(task.IsCanceled)
            {
                Debug.Log("Cancel");
            }
            Debug.Log("Complete");
        });
    }
}

 

 

AppData 함수를 버튼에 매핑해서 실행 후 누르면 실시간으로 FireStore Database에 수정이 된다.

 

 


 

 

FireStore -ReadData() 생성하기

public void ReadData()
{
    FirebaseFirestore db = FirebaseFirestore.DefaultInstance;

    DocumentReference docRef = db.Collection("data").Document("doc"); //Collection과 Document에 내가 가진 데이터베이스의 필드를 넣는다.
    docRef.GetSnapshotAsync().ContinueWithOnMainThread(task =>
    {
        if (task.IsFaulted)
        {
            //fail
        }
        else
        {
            DocumentSnapshot snapshot = task.Result;
            if(snapshot.Exists) //스냅샷에 값이 있다면
            {
                Dictionary<string, object> user = snapshot.ToDictionary(); //snapshot을 Dictionary로 바꿔서 유저에게 넣는다.
                string firstname = user["job"].ToString();
                Debug.Log("" + firstname);
            }
        }
    });
}

 

 

스크립트에 "data"의 "doc"의  ["job"]을 불러온다고 했기 때문에 콘솔 창에 job의 value인 "Turing"이 들어온다.

 

 


 

 

FireStore -ReadAllData() 모든 데이터 읽기

다음의 함수를 추가하고 버튼에 매핑해서 눌러보면 전체 데이터가 나온다.

public void ReadAllData()
{
    FirebaseFirestore alldb = FirebaseFirestore.DefaultInstance;
    alldb.Collection("data").GetSnapshotAsync().ContinueWithOnMainThread(task =>
    {
        if (task.IsFaulted)
        {
            //실패
        }
        else if (task.IsCanceled)
        {
            //취소
        }
        else
        {
            QuerySnapshot querysnapshot = task.Result; //전체를 받아올 것이기 때문에 QuerySnapshot을 사용한다.
            foreach(DocumentSnapshot document in querysnapshot.Documents) //전체 데이터이기 때문에 foreach를 사용한다.
            {
                Debug.Log("Document ID: " + document.Id);
                Dictionary<string, object> documentData = document.ToDictionary(); //새로운 Dictionary 생성
                foreach(var keyValuePair in documentData) //dictionary에 있는 값을 다시 foreach로 꺼낸다.
                {
                    Debug.Log($"{keyValuePair.Key}:{keyValuePair.Value}");
                }
            }
        }
    });
}

 

전체 코드

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Firebase.Firestore;
using Firebase;
using Firebase.Extensions;

public class FireStoreController : MonoBehaviour
{
    void Start()
    {
        FirebaseApp app = FirebaseApp.DefaultInstance;
    }

    public void AddData()
    {
        FirebaseFirestore db = FirebaseFirestore.DefaultInstance;
        DocumentReference docRef = db.Collection("data").Document("doc");
        Dictionary<string, object> user = new Dictionary<string, object>
        {
            {"gold", 100}, {"job", "Turing" }, {"user", "1912" }
        };
        docRef.SetAsync(user).ContinueWithOnMainThread(task =>
        {
            if(task.IsFaulted)
            {
                Debug.Log("Fail");
            }
            if(task.IsCanceled)
            {
                Debug.Log("Cancel");
            }
            Debug.Log("Complete");
        });
    }

    public void ReadData()
    {
        FirebaseFirestore db = FirebaseFirestore.DefaultInstance;

        DocumentReference docRef = db.Collection("data").Document("doc");
        docRef.GetSnapshotAsync().ContinueWithOnMainThread(task =>
        {
            if (task.IsFaulted)
            {
                //fail
            }
            else
            {
                DocumentSnapshot snapshot = task.Result;
                if(snapshot.Exists) //스냅샷에 값이 있다면
                {
                    Dictionary<string, object> user = snapshot.ToDictionary(); //snapshot을 Dictionary로 바꿔서 유저에게 넣는다.
                    string firstname = user["job"].ToString();
                    Debug.Log("" + firstname);
                }
            }
        });
    }

    public void ReadAllData()
    {
        FirebaseFirestore alldb = FirebaseFirestore.DefaultInstance;
        alldb.Collection("data").GetSnapshotAsync().ContinueWithOnMainThread(task =>
        {
            if (task.IsFaulted)
            {
                //실패
            }
            else if (task.IsCanceled)
            {
                //취소
            }
            else
            {
                QuerySnapshot querysnapshot = task.Result; //전체를 받아올 것이기 때문에 QuerySnapshot을 사용한다.
                foreach(DocumentSnapshot document in querysnapshot.Documents) //전체 데이터이기 때문에 foreach를 사용한다.
                {
                    Debug.Log("Document ID: " + document.Id);
                    Dictionary<string, object> documentData = document.ToDictionary(); //새로운 Dictionary 생성
                    foreach(var keyValuePair in documentData) //dictionary에 있는 값을 다시 foreach로 꺼낸다.
                    {
                        Debug.Log($"{keyValuePair.Key}:{keyValuePair.Value}");
                    }
                }
            }
        });
    }
}

 

 

콘솔 창에 데이터베이스의 내용이 잘 나온다.

 

 


 

 

Firebase -Storage

FirebaseStorage.unitypackage 패키지를 import 한다.

 

 

유니티의 json을 업데이트 한다. (열어보면 추가로 생성된 코드들을 볼 수 있다.)

 

 

Storage를 들어가서 새로 만든다. 테스트모드로.

 

 

다음의 스크립트를 적용해본다.

using Firebase;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Firebase.Storage;
using Firebase.Extensions;
using TMPro;

public class StorageManager : MonoBehaviour
{
    public TMP_InputField localPath;
    public TMP_InputField fileName;
    FirebaseStorage storage;
    void Start()
    {
        FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => //구글 플레이 버전이 맞는지
        {
            if (task.IsFaulted)
            {
                Debug.LogError("");
                return;
            }
        });
        storage = FirebaseStorage.DefaultInstance; //버전이 맞다면 디폴트 값을 할당한다.
    }

    public void UploadFile() //새로운 함수 생성
    {
        string localFilePath = localPath.text;
        string storageFilePath = fileName.text;

        StorageReference storageRef = storage.GetReference(storageFilePath);

        storageRef.PutFileAsync(localFilePath).ContinueWithOnMainThread(task =>
        {
            if (task.IsFaulted || task.IsCanceled) //안된다면
            {
                Debug.Log("Upload Failed!");
            }
            else //된다면
            {
                Debug.Log("Upload Success!");
            }
        });
    }
}

 

 

InputField를 두 개 만들고, 올리기 버튼 하나 만든 후,

 

 

위의 InputField에는 LocalPath를 입력, 아래 InputField는 파일 이름을 입력.

 

 

버튼을 누르면 Firebase Storage에 파일이 올라간다.

728x90
반응형