UniTask를 이용한 비동기 처리

굥지·2026년 1월 14일

🔷 UniTask - 유니티 최적화 라이브러리

기존 C#의 Task는 무겁기도 하고 GC 할당이 계속 발생해서 UniTask를 사용했습니다.

구조체(struct) 기반이라 메모리 할당이 거의 없어요!

일단 패키지부터 설치해야겠죠?

Window -> Package Manager -> Add package from git URL 누르고 아래 주소 입력!

https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask

아래 링크 넣어서 다운로드

https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask

📄 1. 네트워크 통신 리팩토링 (Coroutine → UniTask)

원래 Coroutine이랑 Action(콜백) 써서 비동기 통신을 구현했었는데, 이게 가독성이 진짜 안 좋아서 UniTask로 싹 바꿔버렸습니다. 그리고 공부할겸! 활용해봤습니다~

① 통신 매니저 (PythonConnectManager.cs)

[Before] 기존 코드: 코루틴 + 콜백

결과값을 바로 return 못하고 callback으로 넘겨줘야 하는게 제일 불편함 ㅠㅠ

// IEnumerator 반환, 결과는 Action 콜백으로 넘겨줌
public IEnumerator MostSimilarty(string inputWord, int num, Action<List<string>> callback)
{
    // ... (중략) ...

    using (UnityWebRequest request = new UnityWebRequest(url, "POST"))
    {
        // [기존 방식] 여기서 yield return으로 대기
        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
            // ... 데이터 파싱 ...
            // [단점] 값을 직접 리턴 못하고 콜백 함수 호출해야 함
            callback(responseData.result);
        }
        else
        {
            callback(new List<string> { "요청 실패" });
        }
    }
}

[After] 개선된 코드: UniTask + Async/Await

async 키워드 붙이고 결과값을 바로 return 받을 수 있어서 완전 직관적임!

using Cysharp.Threading.Tasks; // 필수!

// async 키워드 사용, List<string>을 직접 반환!
public async UniTask<List<string>> MostSimilarty(string inputWord, int num)
{
    // ... (중략) ...

    using (UnityWebRequest request = new UnityWebRequest(url, "POST"))
    {
        // [개선] await로 기다림
        await request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.Success)
        {
             // ... 데이터 파싱 ...
            // [장점] 그냥 바로 return 때리면 됨
            return responseData.result;
        }
        else
        {
            return new List<string> { "요청 실패" };
        }
    }
}

② 호출하는 곳 (AlgorithmCall.cs)

[Before] 기존: 콜백 지옥 (계단식 코드)

StartCoroutine 안에 람다식 (result) => { ... } 들어가니까 들여쓰기가 계속 깊어짐;;

public void OnShowSimilarWord()
{
    // ... 생략 ...
    
    // [단점] StartCoroutine + 람다식 콜백 = 가독성 떨어짐
    StartCoroutine(pythonConnectManager.MostSimilarty(answerWord, 5, (result) =>
    {
        loading?.StopAnim();

        // --- 여기서부터 결과 처리 로직 ---
        if (result == null) { ... }
        
        // 로직이 길어질수록 계속 들여쓰기 해야함 ㅠㅠ
        resultText.text = $"관련 단어 : {result[idx]}";
    }));
}

[After] 개선: 선형적인 코드

// async void로 변경 (유니티 이벤트 함수용)
public async void OnShowSimilarWord()
{
    // ... 생략 ...

    // [장점] await로 결과값 바로 받아옴! (코루틴 삭제)
    List<string> result = await pythonConnectManager.MostSimilarty(answerWord, 5);

    loading?.StopAnim();

    // --- 들여쓰기 없이 바로 로직 작성 가능 ---
    if (result == null) { ... }

    resultText.text = $"관련 단어 : {result[idx]}";
}

📄 2. 비동기 씬 로드

네트워크만 바꾸긴 아쉬워서 씬 로딩도 바꿨습니당.
보통 로딩바 구현할 때 StartCoroutine이랑 yield return null 쓰잖아요? 근데 yield return null이 미세하게 가비지를 만든다는 사실.. UniTask 쓰면 이것도 해결된다고 하더라구요

[Before]

IEnumerator LoadRoutine(string targetScene)
{
    AsyncOperation op = SceneManager.LoadSceneAsync(targetScene);
    op.allowSceneActivation = false;

    while (!op.isDone)
    {
        yield return null; // 매 프레임 가비지 생성 가능성 있음
        // ... 진행률 계산 로직 ...
    }
}

[After]

// Start에서 바로 비동기 함수 호출 가능!
async void Start()
{
    await LoadSceneAsync("TargetScene");
}

async UniTask LoadSceneAsync(string targetScene)
{
    await UniTask.Yield(); // 첫 프레임 대기

    AsyncOperation op = SceneManager.LoadSceneAsync(targetScene);
    op.allowSceneActivation = false;

    while (!op.isDone)
    {
        // [핵심] struct 기반이라 메모리 할당 없음 (Zero Allocation)
        await UniTask.Yield(); 

        // ... 진행률 계산 (기존과 동일) ...
        float raw = Mathf.Clamp01(op.progress / 0.9f);
        loadingUI?.SetProgress(raw);

        if (raw >= 1f)
        {
             op.allowSceneActivation = true;
             break;
        }
    }
}

Start()를 async void로 바꾸고 UniTask.Yield()를 쓰면 끗

💡 UniTask의 효과!

  • 가독성 떡상: 콜백 지옥 탈출하고 코드가 깔끔해짐

  • 메모리 절약: yield return null 대신 UniTask.Yield() 써서 GC 방어

  • 예외 처리: 코루틴에선 안되던 try-catch가 먹힘

0개의 댓글