Unity 최종

Unity + Firebase: 랭킹 UI 비동기 처리 방법 3가지 정리 (ContinueWith vs Coroutine vs async/await)

1vdlrwnsv1 2025. 5. 28. 16:38

국비 교육 중 팀원이 만든 Uinty에서 Firebase데이터를 받아와 유저의 랭킹 정보를 표시 하는 기능이 있는데 UI 오브젝트가 생성이 안되는 문제가 생겼다 결국 여러 방법을 시도하며 안정적인 방법을 찾아냈다

 

기존 코드

private void Init()
    {
        FirebaseManager.Instance.DB.Collection("users")
            .OrderByDescending("score")
            .Limit(3)
            .GetSnapshotAsync().ContinueWith(task =>
            {
                if (task.IsCanceled || task.IsFaulted) return;

                var snapshot = task.Result;
                foreach (var document in snapshot.Documents)
                {
                    string nickname = document.GetValue<string>("nickname");
                    int score = document.GetValue<int>("score");
                    Debug.Log($"닉네임: {nickname}, 점수: {score}");


                    var scoreItems = ObjectPoolManager.Instance.GetObject<RankDisplayUI>(contentUI, Vector3.zero, Quaternion.identity);
                    if (scoreItems == null)
                    {
                        Debug.LogError("오브젝트 풀에서 RankDisplayUI 생성 실패!");
                        return;
                    }
                    scoreItems.transform.SetParent(contentsPos.transform, false);

                    scoreItems.SetUI(document);
                }
            });
    }

 

Unity의 메인 스레드에서 실행되지 않아 ui관련작업(Instantiate, SetParent)시 문제가 발생함 어떤 에러도 코드에 적어놓은 디버그 로그도 찍히지 않았음

 

트러블 슈팅

ui작업은 반드시 메인 스레더에서

해결 방법: 코루틴, async/await 방식 전환

 

코루틴 방식

 

private IEnumerator InitCoroutine()
    {
        var task = FirebaseManager.Instance.DB.Collection("users")
            .OrderByDescending("score")
            .Limit(3)
            .GetSnapshotAsync();

        yield return new WaitUntil(() => task.IsCompleted);

        if (task.IsCanceled || task.IsFaulted)
        {
            Debug.LogError("데이터 가져오기 실패");
            yield break;
        }

        var snapshot = task.Result;

        foreach (var document in snapshot.Documents)
        {
            string nickname = document.GetValue<string>("nickname");
            int score = document.GetValue<int>("score");
            Debug.Log($"닉네임: {nickname}, 점수: {score}");


            var scoreItems = ObjectPoolManager.Instance.GetObject<RankDisplayUI>(contentUI, Vector3.zero, Quaternion.identity);
            if (scoreItems == null)
            {
                Debug.LogError("오브젝트 풀에서 RankDisplayUI 생성 실패!");
                continue;
            }
            scoreItems.transform.SetParent(contentsPos.transform, false);

            scoreItems.SetUI(document);
        }

    }

 

장점

 

  • Unity 메인 스레드에서 실행되므로 UI 조작이 안전함.
  • yield return을 활용해 자연스럽게 대기 가능.

단점

 

  • 코드 가독성이 async/await에 비해 떨어짐.
  • 에러 처리가 불편함 (task.Exception 직접 접근 필요).

 

방법 2

 

async/await 방식

private async void Init()
{
    if (contentUI == null || contentsPos == null)
    {
        Debug.LogError("contentUI 또는 contentsPos가 인스펙터에서 설정되지 않았습니다.");
        return;
    }

    var query = FirebaseManager.Instance.DB.Collection("users")
        .OrderByDescending("score")
        .Limit(3);

    QuerySnapshot snapshot;

    try
    {
        snapshot = await query.GetSnapshotAsync();
    }
    catch (System.Exception e)
    {
        Debug.LogError("Firebase 쿼리 실패: " + e.Message);
        return;
    }

    foreach (var document in snapshot.Documents)
    {
        string nickname = document.GetValue<string>("nickname");
        int score = document.GetValue<int>("score");
        Debug.Log($"닉네임: {nickname}, 점수: {score}");

        var scoreItems = ObjectPoolManager.Instance.GetObject<RankDisplayUI>(contentUI, Vector3.zero, Quaternion.identity);
        if (scoreItems == null)
        {
            Debug.LogError("오브젝트 풀에서 RankDisplayUI 생성 실패");
            continue;
        }

        scoreItems.transform.SetParent(contentsPos.transform, false);
        scoreItems.transform.localScale = Vector3.one;
        scoreItems.SetUI(document);
    }
}

장점

 

  • 코드가 깔끔하고 가독성 좋음.
  • try-catch로 예외 처리도 쉽다.
  • 메인 스레드에서 실행되므로 UI 조작도 문제 없음.

 

결론 : 가능하면 async/await로 구현하자 코드 깔끔하고 유지보수 쉬움

 

마무리

Firebase 연동은 단순히 데이터만 가져오는 것 같지만, Unity의 스레드 구조를 이해하지 못하면 UI 깨짐, NullReference 등 여러 문제를 만나게 된다.(필자도 아직 제대로 이해 못함)

이번 포스트가 Firebase 연동하면서 겪는 비동기 문제를 해결하는 데 도움이 되길 바람