Unity 개발/기술 향상

[Unity] Async/Await과 Coroutine의 차이점

내공부방 2025. 2. 27. 22:19
반응형

Unity에서 비동기 처리는 필수적인 개념인데 오늘은 이것의 개념을 다시 한번 정리해볼겸 작성해봤다.

 

대표적인 방법은 크게 두 가지가 떠오를텐데 바로 Async/Await과  Coroutine이다.

이 두 방식은 비슷해 보이지만 동작 방식과 적용할 수 있는 상황이 다르다!! 

 

개념과 특징, 예시를 정리해보자 

 

1. Async/Await

(1) 개념

C#의 비동기 프로그래밍 기능을 활용하는 방식으로 주로 네트워크 요청, DB 연동, 파일 입출력 등의 CPU 또는 I/O 바운드 작업에 사용된다

 

(2) 특징 및 예시  

(2-1) Unity 엔진을 직접 조작할 수 없다

public class AsyncAwaitExample : MonoBehaviour
{
    void Start()
    {
        RunAsyncOperations();
    }

    async void RunAsyncOperations()
    {
        Debug.Log("Start Test!!!!");
        await TestUnityEngineAccess();
    }

    async Task TestUnityEngineAccess()
    {
        try
        {
            await Task.Run(() =>
            {
                transform.position = new Vector3(10, 0, 0);
            });
        }
        catch (Exception ex)
        {
            Debug.LogError($"Error Message : {ex.Message}");
        }
    }
}

 

: Unity 엔진을 직접 조작을 시도했을 때 아래와 같은 에러가 발생한다

 

(2-2)   try-catch를 활용해 예외 처리가 가능하다

public class AsyncAwaitExample : MonoBehaviour
{
    void Start()
    {
        RunAsyncOperations();
    }

    async void RunAsyncOperations()
    {
        Debug.Log("Start Test!!!!");
        await HandleExceptionAsync();
    }

    async Task HandleExceptionAsync()
    {
        try
        {
            Debug.Log("네트워크 요청 시뮬레이션...");
            await SimulateNetworkRequest();
        }
        catch (Exception ex)
        {
            Debug.LogError($"예외 발생: {ex.Message}");
        }
    }
    async Task SimulateNetworkRequest()
    {
        await Task.Delay(1000); // 네트워크 요청 시뮬레이션
        throw new Exception("네트워크 연결 실패!");
    }
}

: SimulateNetworkRequest()에서 강제로 예외를 발생 => try-catch를 사용하여 예외를 안전하게 처리!

 

(2-3) CancellationToken을 사용하면 비동기 작업을 취소할 수 있다! 

public class AsyncAwaitExample : MonoBehaviour
{
    private CancellationTokenSource _cancellationTokenSource;

    void Start()
    {
        _cancellationTokenSource = new CancellationTokenSource();
        RunAsyncOperations();
    }

    async void RunAsyncOperations()
    {
        Debug.Log("Start Test!!!!");
        await TaskWithCancellation(_cancellationTokenSource.Token);
    }

    async Task TaskWithCancellation(CancellationToken cancellationToken)
    {
        Debug.Log("5초간 작업 수행 중... (취소 가능)");

        try
        {
            for (int i = 0; i < 5; i++)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    Debug.LogWarning("작업 취소됨!");
                    return;
                }

                Debug.Log($"진행 중임... {i + 1}초 경과");
                await Task.Delay(1000, cancellationToken);
            }

            Debug.Log("작업 끝!");
        }
        catch (TaskCanceledException)
        {
            Debug.LogWarning("작업이 강제로 취소됨!");
        }
    }

    void OnDestroy()
    {
        // 게임 종료 시 작업 취소
        _cancellationTokenSource.Cancel();
        _cancellationTokenSource.Dispose();
    }
}

: 오브젝트가 Destory 될 때 _cancellationTokenSource.Cancel();을 호출되면서 작업이 취소됨! 

 

(2-4)  멀티스레드 활용이 가능하다! 

 

 

2. Coroutine

(1)개념

: Unity 엔진 메인 스레드에서 실행되며, 프레임 단위로 코드를 실행할 수 있다.

즉 게임 루프와 직접적으로 연결되며, 프레임 단위로 코드 실행을 멈췄다가 다시 실행할 수 있는 기능이 있다! 

 

(2) 특징 및 예시  

using System.Collections;
using UnityEngine;
using UnityEngine.UI;

//(2-1) 메인 스레드에서 실행되기 때문에 GameObject, Transform, UI 등을 직접 조작할 수 있다.
//(2-2) 특정 시간 동안 대기(Wait) 후 실행할 수 있음 (WaitForSeconds, WaitUntil 등)
//(2-3) StopCoroutine()을 사용하면 중단 가능하지만, 종료 후에는 재개할 방법이 없음
//(2-4) Unity가 종료되면 자동으로 중단됨
public class CoroutineExample : MonoBehaviour
{
    public GameObject cube; // 움직일 오브젝트
    public Text statusText; // UI 텍스트
    private Coroutine moveCoroutine; // 코루틴 핸들 저장

    void Start()
    {
        moveCoroutine = StartCoroutine(MoveCubeCoroutine());
    }

    IEnumerator MoveCubeCoroutine()
    {
        while (true)
        {
            cube.transform.position += Vector3.right * 0.1f; // (2-1) GameObject 직접 조작
            yield return new WaitForSeconds(0.5f); // (2-2) 0.5초마다 이동
        }
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (moveCoroutine != null)
            {
                // (2-3) StopCoroutine()을 사용하여 중단 (다시 실행 불가)
                StopCoroutine(moveCoroutine);
                moveCoroutine = null;
            }
        }
    }

    void OnApplicationQuit()
    {
        // (2-4) Unity가 종료되면 자동으로 중단됨
        Debug.Log("Unity 종료 - 모든 코루틴 자동 중단됨");
    }
}

 

 

 

Async/Await을 사용하는 경우

  • 네트워크 요청 (서버에서 데이터 가져오기, API 호출)
  • 파일 입출력 (로컬 데이터 저장/로드)
  • 데이터베이스 연동 (Firebase, SQLite)
  • Unity 엔진과 무관한 백그라운드 작업

Coroutine을 사용하는 경우

  • 게임 내에서 프레임 단위로 실행해야 하는 작업
  • 애니메이션, UI 효과, NPC 움직임, 특정 시간 후 실행되는 로직
  • Unity의 메인 스레드에서 실행해야 하는 작업
반응형