오늘은 즐거운 데이터베이스 알아보는 날
활용법을 스크린샷으로 기록해두고
그 이후에 데이터베이스 세이브 로드 로직을 구현해보자
생성부터 알아보자
파이어베이스 홈페이지 진입



테스트모드 설명필요
잠금모드로 시작
보안 규칙을 정할 수 있다


수정 시작

어제와 달라졌으니 google-services.json 재갱신 필요
google-services.json 은 Assets 이외의 폴더에 넣으면 터짐

데이터베이스를 사용하기 위해
이거 임포트
파이어베이스 DB 매니저를 만들어보겠다.
현재 목표는 인풋필드에 들어간 내용을 데이터베이스에 Update 시켜주기
using UnityEngine;
using Firebase;
using Firebase.Database;
using Firebase.Auth;
using UnityEngine.UI;
using System.Threading.Tasks;
using System.Collections; //비동기작업용
public class FirebaseDBController : MonoBehaviour
{
DatabaseReference dbRef;
FirebaseUser user;
[SerializeField] InputField moneyField;
[SerializeField] InputField xpField;
[SerializeField] InputField levelField;
private void Start()
{
this.dbRef = FirebaseAuthManager.dbRef;
this.user = FirebaseAuthManager.user;
}
public void SaveToDB()
{
StartCoroutine(UpdateMoney(int.Parse(moneyField.text)));
StartCoroutine(UpdateExp(int.Parse(xpField.text)));
StartCoroutine(UpdateLevel(int.Parse(levelField.text)));
}
IEnumerator UpdateMoney(int money)
{
//users 폴더를 만들 듯이
var DBTask = dbRef.Child("users").Child(user.UserId).Child("money").SetValueAsync(money);
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning($"돈 업데이트 실패, {DBTask.Exception}");
}
else
{
//저장완료 팝업 등
Debug.Log("저장 완료");
}
}
IEnumerator UpdateExp(int exp)
{
//users 폴더를 만들 듯이
var DBTask = dbRef.Child("users").Child(user.UserId).Child("exp").SetValueAsync(exp);
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning($"경험치 업데이트 실패, {DBTask.Exception}");
}
else
{
//저장완료 팝업 등
Debug.Log("저장 완료");
}
}
IEnumerator UpdateLevel(int lvl)
{
//users 폴더를 만들 듯이
var DBTask = dbRef.Child("users").Child(user.UserId).Child("level").SetValueAsync(lvl);
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning($"레벨 업데이트 실패, {DBTask.Exception}");
}
else
{
//저장완료 팝업 등
Debug.Log("저장 완료");
}
}
}
실제로 데이터베이스에 등록되는 지 확인
코드상으론 문제 X


반영 완료
이제 동일한 스크립트에 로드 기능을 만들어 보겠다.
public void LoadFromDB()
{
StartCoroutine(LoadUserData());
}
IEnumerator LoadUserData()
{
var DBTask = dbRef.Child("users").Child(user.UserId).GetValueAsync();
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning("데이터 불러오기 실패 " + DBTask.Exception);
}
else if (DBTask.Result.Value == null)
{
Debug.LogWarning("저장된 데이터 없습니다");
//yield break;
}
else
{
DataSnapshot snapShot = DBTask.Result;
moneyField.text = snapShot.Child("money").Exists ? snapShot.Child("money").Value.ToString() : "0";
}
}
세이브&로드 기능구현 테스트

문제없이 구현 완료
현재 코드의 문제점
각 변수를 하나씩 업로드 해야됨
이걸 참조형 한번에 올리도록 코드를 리팩토링 해보겠다

셋밸류의 인자값이 오브젝트이므로 리스트는 그냥 넣어도 들어감
딕셔너리의 경우엔?
public void SaveInventoryAndDict()
{
dbRef.Child("users").Child(user.UserId).Child("Inventory").SetValueAsync(inventory); //배열계열은 딸깍
//▼ 딕셔너리는 오브젝트로 변환해서 올리는 작업 필요(인자값이 오브젝트 이기에)
Dictionary<string, object> invenDicObj = new Dictionary<string, object>();
foreach (var kvp in inventoryDicVer)
{
invenDicObj[kvp.Key] = kvp.Value;
}
dbRef.Child("users").Child(user.UserId).Child("InventoryDict").SetValueAsync(invenDicObj);
}
조금 단계가 복잡해지지만 새로 담기만 하면 된다.
using UnityEngine;
using Firebase;
using Firebase.Database;
using System.Collections.Generic;
using Firebase.Auth;
using UnityEngine.UI;
using System.Threading.Tasks;
using System.Collections; //비동기작업용
public class FirebaseDBController : MonoBehaviour
{
DatabaseReference dbRef;
FirebaseUser user;
[SerializeField] InputField moneyField;
[SerializeField] InputField xpField;
[SerializeField] InputField levelField;
public List<string> inventory = new List<string> { "테스트아이템1", "테스트아이템2", "테스트아이템3" };
public Dictionary<string, int> inventoryDicVer = new Dictionary<string, int>()
{
{"딕셔너리테스트1", 1 },
{"딕셔너리테스트2", 3 },
{"딕셔너리테스트3", 6 }
};
private void Start()
{
this.dbRef = FirebaseAuthManager.dbRef;
this.user = FirebaseAuthManager.user;
}
public void SaveInventoryAndDict()
{
dbRef.Child("users").Child(user.UserId).Child("Inventory").SetValueAsync(inventory); //배열계열은 딸깍
//▼ 딕셔너리는 오브젝트로 변환해서 올리는 작업 필요(인자값이 오브젝트 이기에)
Dictionary<string, object> invenDicObj = new Dictionary<string, object>();
foreach (var kvp in inventoryDicVer)
{
invenDicObj[kvp.Key] = kvp.Value;
}
dbRef.Child("users").Child(user.UserId).Child("InventoryDict").SetValueAsync(invenDicObj);
}
IEnumerator LoadInvenCoroutine()
{
var DBTask = dbRef.Child("users").Child(user.UserId).Child("Inventory").GetValueAsync();
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning("인벤토리 불러오기 실패 " + DBTask.Exception);
}
else if (DBTask.Result.Value == null)
{
Debug.LogWarning("인벤토리가 비었습니다");
}
else
{
inventory.Clear(); //기존 로컬상의 인벤토리 깔끔히 날리고
foreach (DataSnapshot item in DBTask.Result.Children) //Children 이기에 전부 데려옴
{
inventory.Add(item.Value.ToString());
//yield return null; 등 부하를 나눠담기? 이러면 데이터 손상 가능성이 있음
}
Debug.Log("인벤토리 로드 완료" + string.Join(", ", inventory));
}
}
IEnumerator LoadInvenDictCoroutine()
{
var DBTask = dbRef.Child("users").Child(user.UserId).Child("InventoryDict").GetValueAsync();
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning("인벤토리 딕셔너리 불러오기 실패 " + DBTask.Exception);
}
else if (DBTask.Result.Value == null)
{
Debug.LogWarning("인벤토리 딕셔너리가 비었습니다");
}
else
{
inventoryDicVer.Clear(); //기존 로컬상의 인벤토리 깔끔히 날리고
foreach (DataSnapshot item in DBTask.Result.Children)
{
string key = item.Key;
if (int.TryParse(item.Value.ToString(), out int value))
{
inventoryDicVer[key] = value;
}
else
{
Debug.LogWarning($"딕셔너리 값 변환 실패: {item.Key} = {item.Value}");
}
}
}
}
public void LoadInventory()
{
StartCoroutine(LoadInvenDictCoroutine());
StartCoroutine(LoadInvenCoroutine());
}
public void SaveToDB()
{
StartCoroutine(UpdateMoney(int.Parse(moneyField.text)));
StartCoroutine(UpdateExp(int.Parse(xpField.text)));
StartCoroutine(UpdateLevel(int.Parse(levelField.text)));
SaveInventoryAndDict();
}
public void LoadFromDB()
{
StartCoroutine(LoadUserData());
}
IEnumerator LoadUserData()
{
var DBTask = dbRef.Child("users").Child(user.UserId).GetValueAsync();
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning("데이터 불러오기 실패 " + DBTask.Exception);
}
else if (DBTask.Result.Value == null)
{
Debug.LogWarning("저장된 데이터 없습니다");
//yield break;
}
else
{
DataSnapshot snapShot = DBTask.Result;
moneyField.text = snapShot.Child("money").Exists ? snapShot.Child("money").Value.ToString() : "0";
xpField.text = snapShot.Child("exp").Exists ? snapShot.Child("exp").Value.ToString() : "0";
levelField.text = snapShot.Child("level").Exists ? snapShot.Child("level").Value.ToString() : "0";
}
}
IEnumerator UpdateMoney(int money)
{
//users 폴더를 만들 듯이
var DBTask = dbRef.Child("users").Child(user.UserId).Child("money").SetValueAsync(money);
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning($"돈 업데이트 실패, {DBTask.Exception}");
}
else
{
//저장완료 팝업 등
Debug.Log("저장 완료");
}
}
IEnumerator UpdateExp(int exp)
{
//users 폴더를 만들 듯이
var DBTask = dbRef.Child("users").Child(user.UserId).Child("exp").SetValueAsync(exp);
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning($"경험치 업데이트 실패, {DBTask.Exception}");
}
else
{
//저장완료 팝업 등
Debug.Log("저장 완료");
}
}
IEnumerator UpdateLevel(int lvl)
{
//users 폴더를 만들 듯이
var DBTask = dbRef.Child("users").Child(user.UserId).Child("level").SetValueAsync(lvl);
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning($"레벨 업데이트 실패, {DBTask.Exception}");
}
else
{
//저장완료 팝업 등
Debug.Log("저장 완료");
}
}
}

일단 에디터 상 Load 체크 완료
파이어베이스도 가보자

SAVE 정상 작동 확인
마지막으로 지금까지의 스크립트에 수정이 필요할 부분에 대해 정리해두자
UpdateChildrenAsync 등을 활용하여 딕셔너리 하나에 money, exp, level을 다 담아서 보내기JSON 직렬화는 예시만 적어두겠다
방식은 C# 클래스를 하나 만들고 JsonUtility 직렬화
[System.Serializable]
public class UserData {
public int money;
public int exp;
public int level;
public List<string> inventory;
}
//저장 시
UserData data = new UserData();
~~~데이터 채우기
string json = JsonUtility.ToJson(data);
dbRef.Child("users").Child(userId).SetRawJsonValueAsync(json);
오늘은 파이어베이스의 데이터베이스 활용 방식과
그에 맞는 코드 작성을 연습해봤다.
처음 C# 공부할 때처럼 리팩토링 할 부분이 수도 없이 많다.
천천히 익숙해져가자.