반응형
250x250
Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

가끔 보자, 하늘.

Unity 에서 Firebase 연동하기 - 유저 데이터 저장/불러오기 본문

개발 이야기/개발 및 서비스

Unity 에서 Firebase 연동하기 - 유저 데이터 저장/불러오기

가온아 2024. 12. 28. 19:12

아직 익명 로그인만 지원하고 있지만 한 유저의 정보를 firebase realtime database(이하 rtb)에 저장해 보겠습니다.

firestore와 rtb 를 선택할 때는 데이터의 복잡성, 규모, 비용등을 고려해야 하지만 일단 싸고 단순한 rtb로 시작하면 좋습니다. 추후 마이그레이션도 그렇게 복잡하지는(케바케인거.. 아시죠 ^^a) 않습니다.

우선 firebase 콘솔 왼쪽 화면에서 rtb를 선택해 생성을 시도합니다. 그럼 아래와 같은 주소가 할당됩니다. 

그리고 프로젝트 설정 > 일반 > 내 앱 > SDK 설정 및 구성 에서 google-services.json을 (애플 기기 서비스 사용자는 GoogleService-Info.plist파일도 같이) 다운받아 Unity -> Assets 폴더에 덮어씌우기 합니다. 

그리고 rtb 화면에서 규칙 탭으로 이동해 아래와 같이 rule을 입력합니다.

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    }
  }
}

로그인한 계정의 데이터만 read, write할 수 있다는 규칙입니다. 규칙에 대해서는 아래 버튼을 눌러 물어볼 수 있습니다.

예를들어 무료 사용자의 저장 횟수를 제한하는 룰을 만들어 달라고 해봤습니다. 궁금하신 분들은 아래 내용을 클릭해 읽어보세요. :)

더보기

하루에 유저 별 저장 횟수를 10회로 제한하는 방법은?

 

 

안녕하세요! Firebase 전문가입니다. Firebase Realtime Database에서 하루에 10번만 저장할 수 있도록 쓰기 횟수를 제한하는 방법을 알려드릴게요.

1. 쓰기 횟수를 기록하는 노드 생성:

  • 먼저, 데이터베이스에 쓰기 횟수를 기록하는 별도의 노드를 생성해야 합니다. 예를 들어, writeCount 라는 노드를 만들고, 이 노드에 하루 동안 발생한 쓰기 횟수를 저장합니다.

2. 규칙 설정:

  • 다음으로, writeCount 노드에 대한 규칙을 설정하여 하루 10번 이상 쓰기가 발생하면 쓰기를 거부하도록 합니다.
{
  "rules": {
    "writeCount": {
      ".write": "newData.val() <= 10 && now.getDay() === data.child('lastWriteDay').val() || now.getDay() !== data.child('lastWriteDay').val()"
    },
    ".write": "newData.hasChildren() && !data.hasChildren() || newData.hasChildren() && data.hasChildren() && newData.children().val() !== data.children().val()"
  }

}

설명:

  • .write 규칙은 데이터베이스에 쓰기가 발생할 때 실행됩니다.
  • newData.val() <= 10 은 쓰기 횟수가 10 이하인 경우를 의미합니다.
  • now.getDay() === data.child('lastWriteDay').val() 은 현재 날짜가 마지막 쓰기 날짜와 같은 경우를 의미합니다.
  • now.getDay() !== data.child('lastWriteDay').val() 은 현재 날짜가 마지막 쓰기 날짜와 다른 경우를 의미합니다.
  • newData.hasChildren() && !data.hasChildren() 은 새로운 데이터가 존재하고 기존 데이터가 없는 경우를 의미합니다.
  • newData.hasChildren() && data.hasChildren() && newData.children().val() !== data.children().val() 은 새로운 데이터와 기존 데이터가 모두 존재하고 값이 다른 경우를 의미합니다.

3. 쓰기 발생 시 writeCount 노드 업데이트:

  • 앱에서 데이터베이스에 쓰기가 발생할 때마다 writeCount 노드를 업데이트해야 합니다.
  • 현재 날짜가 마지막 쓰기 날짜와 다르면 writeCount 노드의 값을 1로 초기화하고 lastWriteDay 노드에 현재 날짜를 저장합니다.
  • 현재 날짜가 마지막 쓰기 날짜와 같으면 writeCount 노드의 값을 1 증가시킵니다.

주의 사항:

  • 위 규칙은 하루 10번 이상 쓰기가 발생하면 모든 쓰기를 거부합니다.
  • 앱에서 쓰기 횟수를 제한하는 경우, 앱의 성능에 영향을 줄 수 있습니다.
  • 규칙을 복잡하게 만들면 유지 관리가 어려워질 수 있습니다.

추가 정보:

  • Firebase Realtime Database 규칙에 대한 자세한 내용은 Firebase 문서  를 참조하세요.

궁금한 점이 있으면 언제든지 저에게 물어보세요! 😊

설정은 끝났습니다. 이제 유니티에서 유저 데이터를 연동해 보겠습니다. 유니티에서 우리는 다음 항목을 구현해 보겠습니다.

  • 프로그램 시작 시 firebase 연동, 자동 로그인(아직은 게스트), 그리고 데이터 로딩
  • 유저 데이터 저장하기
  • 오프라인 상태에서 유저 데이터 저장, 불러오기
using Firebase;
using Firebase.Auth;
using Firebase.Extensions;
using Firebase.Database; // Realtime Database 사용시

class AuthManager : MonoBehaviour
{
    public FirebaseAuth auth;
    public FirebaseUser currentUser;
    private DatabaseReference dbReference; // Realtime Database 사용시

	void Awake()
    {
        InitializeFirebase();
    }
    
    private void InitializeFirebase()
    {
        FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => 
        {
            if (task.Exception != null)
            {
                Debug.LogError($"Firebase 초기화 실패: {task.Exception}");
                return;
            }
            Debug.Log("Firebase 초기화 성공");

            var dependencyStatus = task.Result;
            Debug.Log("dependencyStatus = " + dependencyStatus);
            if (dependencyStatus == DependencyStatus.Available)
            {
                // DatabaseURL 설정 (이미 설정했다면 중복 제거)
                FirebaseApp.DefaultInstance.Options.DatabaseUrl = 
                    new System.Uri("https://projectyis-default-rtdb.firebaseio.com/");

                if(FirebaseApp.DefaultInstance.Options.DatabaseUrl != null)
                    Debug.Log("DatabaseURL = " + FirebaseApp.DefaultInstance.Options.DatabaseUrl);
                else
                    Debug.LogError("DatabaseURL 설정 실패");
                // Firebase Realtime Database 오프라인 데이터 활성화
                FirebaseDatabase.DefaultInstance.SetPersistenceEnabled(true);
                Debug.Log("Firebase Realtime Database 오프라인 데이터 활성화");
                auth = FirebaseAuth.DefaultInstance;
                dbReference = FirebaseDatabase.DefaultInstance.RootReference;
                // 특정 경로에 대한 동기화 유지
                dbReference.KeepSynced(true);

                auth.StateChanged += AuthStateChanged;
                AuthStateChanged(this, null);
                Debug.Log("Firebase 초기화 성공 (오프라인 데이터 활성화 완료)");
                LoadData();
            }
        });        
    }
    
    // 인증 상태 변경 처리
    private void AuthStateChanged(object sender, EventArgs e)
    {
        if (auth.CurrentUser != currentUser)
        {
            bool signedIn = auth.CurrentUser != null;
            if (signedIn)
            {
                currentUser = auth.CurrentUser;
                Debug.Log($"사용자 로그인: {currentUser.UserId}");
            }
            else
            {
                currentUser = null;
                Debug.Log("사용자 로그아웃");
            }
        }
    }
    
    public void SaveToFirebase()
    {
        if (currentUser == null)
        {
            Debug.LogWarning("Firebase에 로그인되지 않았습니다");
            return;
        }

        // Realtime Database 사용시
        dbReference = FirebaseDatabase.DefaultInstance.RootReference;
        dbReference.Child("users").Child(currentUser.UserId).SetRawJsonValueAsync(JsonUtility.ToJson(playerDatas)).ContinueWithOnMainThread(task => {
            if (task.IsFaulted)
            {
                Debug.LogError($"Firebase Realtime Database에 저장 실패: {task.Exception}");
            }
            else if (task.IsCompleted)
            {
                Debug.Log("Firebase Realtime Database에 저장 성공");
            }
        });
    }
    
    public async Task<bool> LoadFromFirebase()
    {
        if (currentUser == null)
        {
            Debug.LogWarning("Firebase에 로그인되지 않았습니다");
            return false;
        }

        dbReference = FirebaseDatabase.DefaultInstance.RootReference;
        try
        {
            DataSnapshot snapshot = await dbReference.Child("users").Child(currentUser.UserId).GetValueAsync();
            if (snapshot.Exists)
            {
                playerDatas = JsonUtility.FromJson<PlayerDatas>(snapshot.GetRawJsonValue());
                PersistentDataManager.ApplySaveData(playerDatas.save);
                Debug.Log("Firebase에서 불러오기 성공: " + playerDatas.save);
                return true;
            }
            else
            {
                Debug.LogWarning("해당 UserId로 저장된 데이터가 없습니다");
                return false;
            }
        }
        catch (System.Exception e)
        {
            Debug.LogError($"Firebase Realtime Database에서 불러오기 실패: {e}");
            return false;
        }
    }
}

firebase를 이용하면 굳이 로컬 영역에 별도로 저장하지 않아도 오프라인 상태의 작업을 일시적으로 로컬에 저장했다가 온라인으로 연결되면 바로 rtd에 저장하게 됩니다.

잘 테스트 되었나요? :)    다음은 구글 로그인을 정리해 보겠습니다.

반응형