XR플밍 - 12. UnityEngine3D 네트워크 프로그래밍 - 파이어베이스 데이터베이스 - 네트워크 데이터 드리븐 (7/21)

이형원·2025년 7월 21일
0

XR플밍

목록 보기
141/215

1. RunTransaction()

RunTransaction의 경우, 그 기능의 특징 상 실행에 실패했을 경우 성공할 때까지 계속 실행하는 특성이 있다.
이 때문에 실제 반응속도가 살짝 느릴 수 있으며 사용하기 적절한 상황은 동시에 여러 사람이 한 데이터에 관여할 경우라고 할 수 있다.

또한 RunTransaction()은 여러 번 호출되므로 null일 때 값이 호출될 가능성이 있으므로, 해당 상황에 대한 조건 처리가 있어야 한다.

DatabaseReference root = BackendManager.Database.RootReference;
DatabaseReference totalUser = root.Child("TotalUser");

totalUser.RunTransaction(mutableData =>
{
	if(mutableData.Value == null)
    {
    	mutableData.Value = 1;
        return TransactionResult.Suc;
    }
    
    long userCount = (long)mutableData.Value;
    mutableData.Value = userCount + 1;
    return TransactionResult.Success(mutableData);
});

2. 데이터베이스 이벤트

데이터베이스 이벤트에 대해 알아보고자 한다.
파이어베이스 데이터베이스에는 아래와 같은 이벤트를 제공하고 있다.

이 중에서도 ValueChanged 메소드를 사용해보고자 한다.
아래와 같이 코드를 작성해보자.

[SerializeField] Button UpButton;
[SerializeField] Button DownButton;

[SerializeField] PlayerData data;

private DatabaseReference userLevel;

private void Awake()
{
    testButton.onClick.AddListener(Test4);

    UpButton.onClick.AddListener(Up);
    DownButton.onClick.AddListener(Down);
}

private void OnEnable()
{
    FirebaseUser user = FirebaseManager.Auth.CurrentUser;

    DatabaseReference root = FirebaseManager.Database.RootReference;
    userLevel = root.Child("UserData").Child(user.UserId).Child("Level");

    userLevel.ValueChanged += UserLevel_ValueChanged;
}

private void OnDisable()
{
    userLevel.ValueChanged -= UserLevel_ValueChanged;
}

private void UserLevel_ValueChanged(object sender, ValueChangedEventArgs args)
{
    DataSnapshot snapshot = args.Snapshot;
    data.Level = (int)(long)snapshot.Value;
    Debug.Log($"Level이 {data.Level}로 변경됨.");
}

private void Up()
{
    userLevel.SetValueAsync(data.Level + 1);
    Debug.Log("서버에 레벨업을 신청함");
}

private void Down()
{
    userLevel.SetValueAsync(data.Level - 1);
    Debug.Log("서버에 레벨업을 신청함");
}

여기에서 벌도의 Invoke 없이 이벤트가 처리되는 것을 알 수 있는데, UserLeve_ValueChanged의 ValueChangedEventArgs 가 파이어베이스에서 제공되는 기능이라 Invoke를 파이어베이스에서 처리한다.

이와 같이 잘 적용되는 것은 확인할 수 있었다.

3. 데이터 정렬과 필터링

3.1 데이터 정렬

데이터 정렬의 경우 자체적으로 구성하는 방법도 있으나, 기본적으로 제공되는 기능 또한 있다.

  1. OrderByKey()

기본적으로 파이어베이스 데이터가 딕셔너리로 이루어져 있기 때문에 해당 키 값을 기준으로 정렬하는 방식이다.
아래와 같이 코드를 작성해보자.

private DatabaseReference userLevel;

private void Awake()
{
    testButton.onClick.AddListener(Test4);
}

private void OnEnable()
{
    FirebaseUser user = FirebaseManager.Auth.CurrentUser;

    DatabaseReference root = FirebaseManager.Database.RootReference;
    userLevel = root.Child("UserData").Child(user.UserId).Child("Level");

    userLevel.ValueChanged += UserLevel_ValueChanged;
}

private void OnDisable()
{
    userLevel.ValueChanged -= UserLevel_ValueChanged;
}

private void Test3()
{
    DatabaseReference root = FirebaseManager.Database.RootReference;
    DatabaseReference wordRef = root.Child("Word");

    wordRef.OrderByKey().GetValueAsync()
        .ContinueWithOnMainThread(task =>
        {
            if(task.IsCanceled)
            {
                Debug.Log("데이터베이스 읽기 취소");
                return;
            }
            if(task.IsFaulted)
            {
                Debug.Log($"데이터베이스 읽기 실패. 이유 : {task.Exception}");
                return;
            }

            Debug.Log("데이터베이스 읽기 성공!");
            DataSnapshot snapshot = task.Result;
            foreach(DataSnapshot child in snapshot.Children)
            {
                Debug.Log($"{child.Key} : {child.Value}");
            }
        });
}

해당 스크립트로 정렬한 원본 데이터와 결과는 다음과 같이 나온다.

여기서 유의해야 할 것은, 키값으로 적혀 있는 숫자의 경우 숫자가 아닌 문자열로 인식이 되므로, 의도치 않은 정렬 결과가 나오지 않도록 유의하도록 한다.

ex) 3 은 27보다 작지만, 실제로 정렬해보면 27이 앞에 오고 3이 뒤에 온다.
앞자리만 먼저 비교해서 이렇게 나오는 건지 모르겠지만, 03과 27을 비교해야지 비로소 의도한 대로 정렬된다.

OrderByKey 는 키 값을 기준으로 정렬하는 것이고, OrderByValue는 값을 기준으로 정렬한다.

  1. OrderByChild

OrderByChild는 다음과 같은 상황에서 쓰인다.

예를 들어 이와 같이 데이터의 묶음으로 이루어진 데이터에서, 데이터의 특정 값을 기준으로 정렬하고 싶을 수도 있을 겻이다. 이때, 해당 데이터를 Level을 기준으로 정렬하려면 아래와 같이 코드를 작성한다.

private void Test4()
{
    DatabaseReference root = FirebaseManager.Database.RootReference;
    DatabaseReference wordRef = root.Child("UserData");

    wordRef.OrderByChild("Level").GetValueAsync()
        .ContinueWithOnMainThread(task =>
        {
            if (task.IsCanceled)
            {
                Debug.Log("데이터베이스 읽기 취소");
                return;
            }
            if (task.IsFaulted)
            {
                Debug.Log($"데이터베이스 읽기 실패. 이유 : {task.Exception}");
                return;
            }

            Debug.Log("데이터베이스 읽기 성공!");
            DataSnapshot snapshot = task.Result;
            foreach (DataSnapshot child in snapshot.Children)
            {
                Debug.Log($"{child.Child("Name").Value} : {child.Child("Level").Value}");
            }
        });
}

이와 같이 데이터를 읽은 것을 확인할 수 있다.

3.2 데이터 필터링

위에서 데이터를 정렬하는 방법에 대해서 배웠다면, 이 데이터를 필터링할 수 있는 방법도 있다.

해당 방식의 경우, 여러 개를 중첩시켜 사용할 수 있으며, StartAt() ~ EqualTo() 의 세 가지 메소드는 정렬되었을 때에만 사용할 수 있으니 참고하도록 한다.

사용 예시

private void Test3()
{
    DatabaseReference root = FirebaseManager.Database.RootReference;
    DatabaseReference wordRef = root.Child("Word");

    wordRef.OrderByKey().LimitToFirst(2).GetValueAsync()
        .ContinueWithOnMainThread(task =>
        {
            if(task.IsCanceled)
            {
                Debug.Log("데이터베이스 읽기 취소");
                return;
            }
            if(task.IsFaulted)
            {
                Debug.Log($"데이터베이스 읽기 실패. 이유 : {task.Exception}");
                return;
            }

            Debug.Log("데이터베이스 읽기 성공!");
            DataSnapshot snapshot = task.Result;
            foreach(DataSnapshot child in snapshot.Children)
            {
                Debug.Log($"{child.Key} : {child.Value}");
            }
        });
}

출력할 수 있는 개수를 2개로 제한해보았다.

4. 데이터베이스 규칙

데이터베이스의 규칙은 조건에 따른 읽기와 쓰기 권한 등을 부여하거나, 조건 등을 추가하여 데이터의 변조 및 악용을 방지하는 기능을 말한다.

데이터의 규칙 유형은 다음과 같이 있다.

파이어베이스의 경우 자바스크립트와 유사한 언어로 스크립트를 작성하기 때문에 문법이 다소 익숙하지 않을 수 있다.

간단하게 몇 가지 예시를 들으며 규칙을 이해해보자.

4.1 .read, .write

  • 모든 사람이 모든 항목에 대해 읽는 것은 허용하나, 쓰는 것은 불가함.
{
  "rules": {
      ".read": true,
      ".write": false
  }
}
  • UserData를 읽는 것은 자유로우며, UserData의 내용은 유저 자신의 데이터만 수정할 수 있다.
{
  "rules": {
    ".read": true,
    ".write" : false,
    "UserData": {
      "$uid" : {
        ".read" : true,
        ".write" : "$uid === auth.uid",
      }
    }
  }
}

4.2 .validate

값의 올바른 형식, 하위 속성을 갖는지 여부 및 데이터 유형을 정의한다.
가령, 레벨이란 수치에 숫자 외의 데이터가 들어가지 않도록 아래와 같이 정의할 수 있다.

{
  "rules": {
    ".read": true,
    ".write" : false,
    "UserData": {
      "$uid" : {
        ".read" : true,
        ".write" : "$uid === auth.uid",
        "Level" : {
          ".validate" : "newData.isNumber()",        
        }
      }
    }
  }
}
  • .indexOn

.indexOn은 데이터에 대해 임의위 하위 키를 사용하는 임시 쿼리를 허용한다. 다만 게임 개발에서는 정렬 기준이 자주 바뀌는 경우가 많기 때문에 잘 쓰이지는 않는 방식이어서 예시만 확인하고 넘어간다.

4.3 규칙 플레이그라운드

이와 같이 데이터베이스 규칙을 만들면 데이터베이스를 관리하는 데 더 용이해지는 것은 확실하다. 하지만, 이 규칙이 제대로 적용됐는지 일일히 테스트하는 과정은 번거로울 것이다.

규칙 플레이그라운드는 이와 같이 특정 조건에서의 권한 설정이 잘 되어 있는지 테스트할 수 있게 하는 기능이다.

  1. 권한을 확인해보기

예시로 이와 같이 설정해보자.

UserData에 AAAA라는 uid로 계정이 있다고 가정해보자.

인증된 계정이면서 uid가 22인 계정으로는, 해당 하위 데이터의 level을 변경할 수 없다고 확인이 되었다.
그러면 uid를 일치시키고 다시 실행시켜보자.

이와 같이 어디에서 거부되었고 허용되었는지 쉽게 확인이 가능하다.

  1. validate

이와 같이 형식에 맞지 않는 텍스트를 사용했을 때에도 체크가 가능하다.

profile
게임 만들러 코딩 공부중

0개의 댓글