[진행중]프로젝트 3D_Idle

11일차 - Firebase Realtime Database 연동하기

네,가능합니다 2025. 2. 5. 18:06

 

1. Firebase 초기화 및 로그인 개선

 

작업 배경

 

Firebase 인증 및 데이터베이스 연결 시 "Failed to get FirebaseDatabase instance: Specify DatabaseURL..." 오류와 함께, 구글 로그인 이후 추가 설정(닉네임 설정)이 제대로 동작하지 않는 문제가 있었습니다.

 

이를 해결하기 위해 Firebase 초기화 설정, 그리고 Google SignIn 후 사용자 데이터 로드 로직을 개선했습니다.

 

주요 변경 사항

 

  • Firebase 앱 초기화 시 옵션 지정

 

Firebase App이 없을 경우, Firebase.AppOption을 통해 정확한 DatabaseUrl을 설정하도록 수정했습니다.

 

  // Firebase 앱 초기화 (FirebaseManager)
  var app = Firebase.FirebaseApp.DefaultInstance;
  if (app == null)
  {
      var options = new Firebase.AppOptions
      {
          DatabaseUrl = new System.Uri("https://p-ro-jec-t.firebaseio.com")
      };
      app = Firebase.FirebaseApp.Create(options);
  }
  else
  {
      if (string.IsNullOrEmpty(app.Options.DatabaseUrl?.AbsoluteUri))
      {
          app.Options.DatabaseUrl = new System.Uri("https://p-ro-jec-t.firebaseio.com");
      }
  }

 

  • Google 로그인 후 사용자 데이터 처리 개선

 

로그인 성공 후, 기존 데이터가 없거나 닉네임이 없는 경우에는 `UINicknameSetup` UI를 표시하고 그렇지 않으면 바로 게임 씬으로 전환하도록 분기 처리를 추가했습니다.

 

  // Firebase 인증 및 구글 로그인 (FirebaseManager)
  var credential = GoogleAuthProvider.GetCredential(googleUser.IdToken, null);
  var result = await auth.SignInWithCredentialAsync(credential);
  
  if (result == null)
  {
      Logger.ErrorLog("Firebase 인증 결과가 null입니다.");
      return false;
  }
  
  // 유저 데이터 로드 또는 생성
  bool hasExistingData = await Managers.UserData.LoadOrCreateUserDataAsync(result.UserId);
  
  // 닉네임이 없는 경우 닉네임 설정 UI를 표시하고 여기서 리턴
  if (!hasExistingData || !Managers.UserData.HasNickname)
  {
      Managers.UI.Show<UINicknameSetup>();
      return true; // 로그인은 성공했지만 추가 설정이 필요함
  }
  
  return true; // 모든 데이터가 정상적으로 있는 경우

 

2. UserDataManager 개선

 

작업 배경

 

사용자 데이터 로드 혹은 저장 도중 내부 Task Faulted 에러가 발생하는 문제가 있었습니다.

이로 인해 닉네임 설정이나 데이터 저장이 실패하는 경우가 있었으므로, 에러 처리를 강화하고 저장 전 사전 체크를 추가했습니다,.

 

주요 변경 사항

 

  • 데이터베이스 참조 및 null 체크 추가

 

저장할 유저 데이터와 데이터베이스 참조가 null인 경우 에러 로그를 남기고 예외를 발생시키도록 처리했습니다.

 

  // 유저 데이터 저장 함수 (UserDataManager.cs)
  private async Task SaveUserDataAsync()
  {
      if (currentUserData == null)
      {
          Logger.ErrorLog("저장할 유저 데이터가 없습니다.");
          return;
      }
  
      try
      {
          if (databaseReference == null)
          {
              throw new Exception("데이터베이스 참조가 null입니다.");
          }
  
          string json = JsonUtility.ToJson(currentUserData);
          Logger.Log($"저장할 데이터: {json}");
  
          var userRef = databaseReference.Child("users").Child(currentUserData.UserId);
          await userRef.SetRawJsonValueAsync(json);
          
          Logger.Log($"유저 데이터 저장 성공: {currentUserData.UserId}");
      }
      catch (Exception ex)
      {
          Logger.ErrorLog($"유저 데이터 저장 실패: {ex.Message}\n{ex.StackTrace}");
          throw;
      }
  }

 

3. UIManager 및 UI 화면 개선

 

작업 배경

 

UIManager의 UpdateFrontUI 메서드에서 "Transform child out of bounds" 에러가 발생하는 문제가 있었으며, 각 플랫폼 별 버튼표시를 다르게하고 닉네임 설정 후 게임씬이동 로직이 필요했습니다.

 

주요 변경 사항

 

  • UIManager의 UpdateFrontUI 개선

 

부모 Transform의 유효성 및 자식 개수를 체크하여 safe-check 로직을 추가했습니다.

 

  // UIManager.cs
  private void UpdateFrontUI(eUIPosition up)
  {
      if (parents == null || parents.Count == 0 || (int)up >= parents.Count)
      {
          Logger.ErrorLog($"UI 부모 객체가 없거나 잘못된 UI 위치입니다: {up}");
          return;
      }
  
      Transform parent = parents[(int)up];
      if (parent == null)
      {
          Logger.ErrorLog($"UI 부모 객체가 null입니다: {up}");
          return;
      }
  
      // 자식이 없는 경우 처리
      if (parent.childCount == 0)
      {
          FrontUI = null;
          return;
      }
  
      // 마지막 자식을 FrontUI로 설정
      GameObject lastChild = parent.GetChild(parent.childCount - 1).gameObject;
      FrontUI = lastChild.GetComponent<UIBase>();
  }
  • 플랫폼별 UI 버튼 제어

 

모든 버튼을 초기에는 비활성화 한 후, 플랫폼별로 활성화하여 구글 로그인, 게스트 로그인, 에디터 전용 버튼을 제어하도록 수정했습니다.

 

  // UITitleScene.cs
  private void InitializeUI()
  {
      // 모든 버튼 비활성화로 시작
      if (googleSignInButton != null) googleSignInButton.gameObject.SetActive(false);
      if (guestSignInButton != null) guestSignInButton.gameObject.SetActive(false);
  
  #if UNITY_EDITOR
      if (editorSignInButton != null)
      {
          editorSignInButton.gameObject.SetActive(true);
          editorSignInButton.onClick.AddListener(EditorSignIn);
          editorSignInButton.interactable = true;
      }
  #elif UNITY_ANDROID
      if (googleSignInButton != null)
      {
          googleSignInButton.gameObject.SetActive(true);
          googleSignInButton.onClick.AddListener(() => SignInAsync(true));
          googleSignInButton.interactable = false;  // 초기화 완료 후 활성화
      }
      if (guestSignInButton != null)
      {
          guestSignInButton.gameObject.SetActive(true);
          guestSignInButton.onClick.AddListener(() => SignInAsync(false));
          guestSignInButton.interactable = true;
      }
      
      var loadingPanel = Managers.UI.Show<UILoadingPanel>();
      loadingPanel.ShowWithMessage("게임 준비 중...");
      CheckInitialization();
  #else
      if (guestSignInButton != null)
      {
          guestSignInButton.gameObject.SetActive(true);
          guestSignInButton.onClick.AddListener(() => SignInAsync(false));
          guestSignInButton.interactable = true;
      }
  #endif
  }
  • 닉네임 설정 후 씬 전환

 

닉네임이 성공적으로 설정되면 현재 UI를 숨기고 게임 씬으로 전환하는 코드를 추가했습니다.

 

  // UINicknameSetup.cs
  private async void OnConfirmButtonClick()
  {
      if (string.IsNullOrEmpty(nicknameInput.text)) return;
  
      var loadingPanel = Managers.UI.Show<UILoadingPanel>();
      loadingPanel.ShowWithMessage("닉네임 설정 중...");
      confirmButton.interactable = false;
  
      try
      {
          bool success = await Managers.UserData.SetNicknameAsync(nicknameInput.text);
  
          if (success)
          {
              Managers.UI.Hide<UINicknameSetup>();
              Managers.Scene.LoadScene(EnumTypes.SceneType.GameScene);
          }
          else
          {
              statusText.text = "이미 사용 중인 닉네임입니다.";
              confirmButton.interactable = true;
          }
      }
      catch (Exception ex)
      {
          statusText.text = "닉네임 설정에 실패했습니다.";
          Logger.ErrorLog($"닉네임 설정 실패: {ex.Message}");
          confirmButton.interactable = true;
      }
      finally
      {
          Managers.UI.Hide<UILoadingPanel>();
      }
  }