260108 Firebase

2026. 1. 8. 14:18·📖TIL

네트워크 데이터 베이스

  • Json 파일은 절대 public 으로 노출되면 안된다.

oom room = PhotonNetwork.CurrentRoom;  // 현재 참가한 룸을 확인

// 룸 커스텀 프로퍼티 설정
ExitGames.Client.Photon.Hashtable roomProperty = new ExitGames.Client.Photon.Hashtabl> ();
roomProperty["Map"] = "Select Map";
room.SetCustomProperties(roomProperty);

// 룸 커스텀 프로퍼티 확인
string curMap = (string)room.CustomProperties["Map"];

Player player = PhotonNetwork.LocalPlayer;  // 자신 플레이어를 확인

// 플레이어 커스텀 프로퍼티 설정
ExitGames.Client.Photon.Hashtable playerProperty = new ExitGames.Client.Photon> Hashtable();
playerProperty["Ready"] = true;
player.SetCustomProperties(playerProperty);

// 플레이어 커스텀 프로퍼티 확인
bool ready = (bool)player.CustomProperties["Ready"];
public class NetworkManager : MonoBehaviourPunCallbacks
{
    public override void OnRoomPropertiesUpdate(ExitGames.Client.Photon.Hashtable propertiesThatChanged)
    {
        // 현재 참여한 방의 프로퍼티가 업데이트시 호출됨
    }

    public override void OnPlayerPropertiesUpdate(Player targetPlayer, ExitGames.Client.Photon.Hashtable changedProps)
    {
        // 같은 방의 플레이어의 프로퍼티가 업데이트시 호출됨
    }
}

└ 이벤트와 업데이트 차이

  • RPC 는 너무 많이 쓰면 성능이 좋지 않음,
    ( 함수를 호출한다. 네트워크 부하 , RPC 스팸 일어나면 중복, 순서 등 문제 발생 )
  • 변수동기화는 콜백도 없고 함수 호출도 강제되지 않는다. 하지만 네트워크 틱마다 호출
  • 커스텀프로퍼티는 변하면 콜백을 날려야 한다 ( 콜백 계열이라 실시간 처리에 부적합함 )
  • 랜덤은 한명이 기준점을 잡고 랜덤을 뽑고 그 값을 RPC

  1. 방장이 나가도 살아있어야 하나?
  2. 누가 만들었는지가 중요한가?
  3. 마스터만 제어하는 게 맞나?

using UnityEngine;
using UnityEngine.UI;
using Firebase.Auth; // 로그인 기능을 쓰기 위해 필요
using Firebase; // 기본
using Firebase.Extensions; // 비동기를 위해 사용
using TMPro;

public class FirebaseAuthManager : MonoBehaviour
{
    public FirebaseAuth auth; // 인증 진행을 위한 객체
    public FirebaseUser user; // 인증이 다 되고 나서, 인증된 유저 정보 들고 있게함, 웹개발로 치면 토큰

    [SerializeField] Button startButton;
    [SerializeField] TMP_InputField emailField;
    [SerializeField] TMP_InputField pwField;

    private void Start()
    {
        // 프로그램 구동과 동시에, 알아서 프로젝트와 맞지 않는 코드들 다 고쳐줌
        // 비동기 작업은 중간 내역을 확인하기 어렵다
        // 문제는? PC 가 아닌 모바일 같은 플랫폼에서 아래 코드 수행 ContinueWithOnMainThread 로 변경 (기존 ContinueWith)
        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
        {
            var dependencyStatus = task.Result; // 비동기 작업 결과를 기억시킴
            if(dependencyStatus == Firebase.DependencyStatus.Available) // 가능하다는 결과 받았으면?
            {
                auth = Firebase.Auth.FirebaseAuth.DefaultInstance; // 인증 정보 기억시킴
                startButton.interactable = true; // 초기화 성공 후 인게임 버튼 활성화
            }
            else
            {
                // 실패하면 로그 띄운다
                UnityEngine.Debug.LogError(System.String.Format("뭔가 잘못되었음 " + dependencyStatus));
            }
        });
    }

    public void Login()
    {
        auth.SignInWithEmailAndPasswordAsync(emailField.text, pwField.text).ContinueWithOnMainThread(task =>
        {
            if(task.IsFaulted)
            {
                Debug.Log("로그인 오류");
                return;
            }
            if(task.IsCanceled)
            {
                Debug.Log("로그인 취소");
                return;
            }
            // 지금 이 시점에선, 로그인 완료된 정보가 task 에 들어있음

            user = task.Result.User;
        });
    }

    public void Register()
    {
        auth.CreateUserWithEmailAndPasswordAsync(emailField.text, pwField.text).ContinueWithOnMainThread(task =>
        {
            if (task.IsFaulted)
            {
                Debug.Log("회원가입 오류");
                return;
            }
            if (task.IsCanceled)
            {
                Debug.Log("회원가입 취소");
                return;
            }
            // 지금 이 시점에선, 로그인 완료된 정보가 task 에 들어있음

            user = task.Result.User;
        });
    }
}
  • 파이어베이스는 로컬로 관리하고
  • 깃 이그노어에 추가하는 것이 좋다

▼ 위의 코드를 리팩토링해보자

using UnityEngine;
using UnityEngine.UI;
using Firebase.Auth; // 로그인 기능을 쓰기 위해 필요
using Firebase; // 기본
using Firebase.Extensions; // 비동기를 위해 사용
using TMPro;
using System.Collections;
using System.Threading.Tasks;

public class FirebaseAuthManager : MonoBehaviour
{
    public FirebaseAuth auth; // 인증 진행을 위한 객체
    static public FirebaseUser user; // 인증이 다 되고 나서, 인증된 유저 정보 들고 있게함, 웹개발로 치면 토큰

    [SerializeField] Button startButton;
    [SerializeField] TMP_InputField emailField;
    [SerializeField] TMP_InputField pwField;
    [SerializeField] TMP_InputField nickField; // 닉네임 기억할 것

    public TextMeshProUGUI warningText;
    public TextMeshProUGUI confirmText;

    private void Awake()
    {
        // 프로그램 구동과 동시에, 알아서 프로젝트와 맞지 않는 코드들 다 고쳐줌
        // 비동기 작업은 중간 내역을 확인하기 어렵다
        // 문제는? PC 가 아닌 모바일 같은 플랫폼에서 아래 코드 수행 ContinueWithOnMainThread 로 변경 (기존 ContinueWith)
        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
        {
            var dependencyStatus = task.Result; // 비동기 작업 결과를 기억시킴
            if (dependencyStatus == Firebase.DependencyStatus.Available) // 가능하다는 결과 받았으면?
            {
                auth = Firebase.Auth.FirebaseAuth.DefaultInstance; // 인증 정보 기억시킴
                //startButton.interactable = true; // 초기화 성공 후 인게임 버튼 활성화
            }
            else
            {
                // 실패하면 로그 띄운다
                UnityEngine.Debug.LogError(System.String.Format("뭔가 잘못되었음 " + dependencyStatus));
            }
        });
    }

    private void Start()
    {
        startButton.interactable = false; // 시작할 땐 일단 꺼두기
        warningText.text = "";
        confirmText.text = "";
    }

    public void Login()
    {
        StartCoroutine(LoginCor(emailField.text, pwField.text));
    }

    IEnumerator LoginCor(string email, string password)
    {
        Task<AuthResult> LoginTask = auth.SignInWithEmailAndPasswordAsync(email, password);

        yield return new WaitUntil(predicate: () => LoginTask.IsCompleted);

        if(LoginTask.Exception != null) // 로그인에서 문제가 있으면 Exception 에 담김
        {
            Debug.Log("다음과 같은 이유로 로그인 실패: " + LoginTask.Exception);

            // 파이어베이스에서는 에러를 분석할 수 있는 형식을 제공한다
            FirebaseException firebaseEx = LoginTask.Exception.GetBaseException() as FirebaseException;
            AuthError errorCode = (AuthError)firebaseEx.ErrorCode; // 진짜 해석 가능한 형태로 바꿈

            string message = "";
            switch(errorCode)
            {
                case AuthError.MissingEmail:
                    message = "Missing Email";
                    break;
                case AuthError.MissingPassword:
                    message = " Missing Password";
                    break;
                case AuthError.WrongPassword:
                    message = "Wrong Password";
                    break;
                case AuthError.InvalidEmail:
                    message = "Invalid Email";
                    break;
                case AuthError.UserNotFound:
                    message = "User Not Found";
                    break;
                default:
                    message = "Contact your administrator.";
                    break;
            }
            warningText.text = message;
        }
        else // 여기까지 왔다면 성공했다는 뜻
        {
            user = LoginTask.Result.User; // 로그인 잘 되었으니, 유저 정보를 기억
            warningText.text = "";
            nickField.text = user.DisplayName; // 파이어베이스 상에 기억된 닉네임을 가져옴
            confirmText.text = "Welcome " + user.DisplayName;
            startButton.interactable = true; // 로그인 다 해야지만 게임 접속 가능
        }
    }

    IEnumerator RegisterCor(string email, string password, string userName)
    {
        Task<AuthResult> RegisterTask = auth.CreateUserWithEmailAndPasswordAsync(email, password);
        yield return new WaitUntil(predicate: () => RegisterTask.IsCompleted);

        if (RegisterTask.Exception != null)
        {
            Debug.LogWarning(message: "실패 사유" + RegisterTask.Exception);
            FirebaseException firebaseEx = RegisterTask.Exception.GetBaseException() as FirebaseException;
            AuthError errorCode = (AuthError)firebaseEx.ErrorCode;

            string message = "Regist Fail";
            switch (errorCode)
            {
                case AuthError.MissingEmail:
                    message = "Missing Email";
                    break;
                case AuthError.MissingPassword:
                    message = "Missing Password";
                    break;
                case AuthError.WeakPassword:
                    message = "Weak Password";
                    break;
                case AuthError.EmailAlreadyInUse:
                    message = "Email Already in use.";
                    break;
                default:
                    message = "Contact your administrator.";
                    break;
            }
            warningText.text = message;
        }
        else // 생성 완료
        {
            user = RegisterTask.Result.User;

            if(user != null)
            {
                // ▼ 로컬에서 만든것
                UserProfile profile = new UserProfile { DisplayName = userName };

                // ▼ 파이어베이스에 업로드
                Task profileTask = user.UpdateUserProfileAsync(profile);
                yield return new WaitUntil(predicate: () => profileTask.IsCompleted); // 완료까지 기다려줌

                if(profileTask.Exception != null)
                {
                    Debug.LogWarning("닉네임 설정 실패" + profileTask.Exception);
                    FirebaseException firebaseEx = profileTask.Exception.GetBaseException() as FirebaseException;
                    AuthError errorCode = (AuthError)firebaseEx.ErrorCode;
                    warningText.text = "Failed set Nickname.";
                }
                else
                {
                    warningText.text = "";
                    confirmText.text = "Nickname confirmed. Welcome " + user.DisplayName;
                }
            }
        }
    }

    public void Register()
    {
        StartCoroutine(RegisterCor(emailField.text, pwField.text, nickField.text));
    }
}

 

▼ 사용자 프로필 가져오기

FirebaseUser user = BackendManager.Auth.CurrentUser;
if (user == null)
    return;

string name = user.DisplayName;         // 공개용 이름
string email = user.Email;              // 이메일 주소
bool verified = user.IsEmailVerified;   // 이메일 인증여부
System.Url url = user.PhotoUrl;         // 사진 URL 주소
string uid = user.UserId;               // 고유 아이디 (파이어베이스 사용자 식별용)

 

▼ 인증 메일 보내기

FirebaseUser user = BackendManager.Auth.CurrentUser;
if (user == null)
    return;

user.SendEmailVerificationAsync().ContinueWithOnMainThread(task =>
{
    if (task.IsCanceled)
    {
        Debug.LogError("SendEmailVerificationAsync was canceled.");
        return;
    }
    if (task.IsFaulted)
    {
        Debug.LogError("SendEmailVerificationAsync encountered an error: " + task.Exception);
        return;
    }

    Debug.Log("Email sent successfully.");
});

 

'📖TIL' 카테고리의 다른 글

260109 커맨드 패턴  (0) 2026.01.09
260109 네트워크 데이터 드리븐  (0) 2026.01.09
260107 네트워크 게임플레이  (0) 2026.01.07
260106  (0) 2026.01.06
260106 매치메이킹  (0) 2026.01.06
'📖TIL' 카테고리의 다른 글
  • 260109 커맨드 패턴
  • 260109 네트워크 데이터 드리븐
  • 260107 네트워크 게임플레이
  • 260106
DevHoChan
DevHoChan
맨땅에서 시작하는 코딩 도전
  • DevHoChan
    Debugging Life
    DevHoChan
  • 전체
    오늘
    어제
    • 분류 전체보기 (374)
      • 🕹️Game Life (1)
      • 🖥️Computer Science (5)
      • 📖TIL (141)
        • 🔥Projects (16)
        • 💡DevTips (5)
        • 🤔발생한 문제와 해결 (5)
        • 🔮Unity Graphics (5)
        • 🎤Interview (3)
        • ✅CodingTest (9)
      • 🚀Game Release (4)
      • 🧊Unity Basic (58)
        • 📌용어 사전 (1)
        • 에디터&인터페이스 (3)
        • 디버그 (1)
        • 라이프사이클 (4)
        • 게임오브젝트 (4)
        • 프리팹 (1)
        • 오브젝트풀링 (4)
        • 애트리뷰트 (2)
        • 트랜스폼 (4)
        • 물리&충돌 (1)
        • 프레임&델타타임 (4)
        • 코루틴&이벤트 (7)
        • 수학&보정함수 (3)
        • 디자인패턴 (9)
        • UGUI (3)
        • 벡터 ( Vector ) (3)
        • 씬 ( Scene ) (2)
        • 데이터 관리 (2)
      • ⭐C Sharp (99)
        • 📌용어 사전 (1)
        • 📌문법 사전 (6)
        • 메모리 관리 (3)
        • 00. 문법 (17)
        • 01. 변수 (3)
        • 02. 자료형 (2)
        • 03. 연산자 (6)
        • 04. 조건문 (2)
        • 05. 반복문 (2)
        • 06. 배열 (3)
        • 07. 메서드(함수) (7)
        • 08. 열거형 (3)
        • 09. 구조체 (2)
        • 10. 참조 (2)
        • 11. 객체 지향 (11)
        • 12. 델리게이트 (3)
        • 13. 디자인 패턴 (7)
        • 14. LINQ (1)
        • 📂▼자료구조 (2)
        • 15-1. 제네릭 (3)
        • 15-2. 배열 (4)
        • 15-3. 리스트 (2)
        • 15-4. 스택과 큐 (2)
        • 15-5. 딕셔너리 해시테이블 (2)
        • 15-6. 트리와 그래프 (3)
      • 📊Algorithm (16)
        • BigO (2)
        • 정렬 (4)
        • 셔플 (2)
        • 탐색 (6)
        • 최적화 (1)
      • 📝Game Design (16)
      • 🤖​AI Tools (12)
        • AI 리뷰 분석 (6)
        • Player2 (0)
        • 3D 모델링 (1)
        • 2D 스프라이트 (0)
        • 이미지 (2)
        • 사운드 (1)
        • 동영상 (1)
        • 문서 (1)
      • 🌍Network (6)
      • 🌱Github (11)
        • 기본 개념 (7)
        • 명령어 (1)
        • 도구 활용 (1)
      • ⚙️Visual Studio (5)
        • 🔧설치 및 환경설정 (2)
        • ⌨️HotKey (1)
        • 🚨디버깅 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    디자인패턴
    til
    gamedesign
    algorithm
    게임기획
    문법
    자료구조
    기획
    CodingTest
    객체지향
    게임디자인
    OOP
    유니티
    csharp
    c#
    GitHub
    자료형
    메모리관리
    부트캠프
    unity
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
DevHoChan
260108 Firebase
상단으로

티스토리툴바