공부 정리/Unity

Coroutine 으로 구현하는 비동기 프로그래밍 (Async/Await와 비교)

임녕 2025. 3. 23. 14:49

* 본 글에서 다루는 Coroutine은 Unity에서 사용되는 Coroutine에 대한 정보 위주로 작성되었습니다.

 

1. Coroutine

Coroutine은 Unity에서 시간을 기반으로 작업을 처리해야할 때 사용되는 기능입니다. 특정 작업을 한 프레임 단위로 끝내지 않고 여러 프레임에 걸쳐서 실행할 수 있도록 도움을 줍니다.

 

Coroutine을 사용하면 함수를 잠시 중단했다가 이후 특정 시점에서 이어서 진행할 수 있기 때문에 대기 시간을 두어 시간 지연을 주거나 반복되는 작업을 효율적으로 처리할 수 있습니다.

 


2. 사용 방법

1) 전체 스크립트

using System.Collections;
using UnityEngine;

public class Coroutine : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(CoroutineSample());
    }

    IEnumerator CoroutineSample()
    {
        Debug.Log("코루틴 시작 시점");

        yield return new WaitForSeconds(2f);

        Debug.Log("2초 경과");

        yield return new WaitForSeconds(1f);

        Debug.Log("1초 추가 경과, 총 3초 경과");
    }
}

 

 

2) 코드 상세 설명

using System.Collections;

 

IEnumerator를 사용하기 위한 네임스페이스 선언입니다.

 

* IEnumerator란 C#의 Iterator(반복자) 인터페이스로 Unity에서는 IEnumerator를 사용하여 Coroutine을 구현할 수 있습니다.

 

StartCoroutine(CoroutineSample());

 

IEnumerator를 반환하는 Coroutine 함수를 실행합니다.

 

StartCoroutine()으로 실행시킨 Coroutine을 종료하는 방법은 2가지가 있습니다.

 

StopCoroutine(CoroutineSample);
StopAllCoroutines();

 

이름에서 직관적으로 보이듯이 StopCoroutine()은 하나의 Coroutine을 종료하고 StopAllCoroutines()는 실행 중인 모든 Coroutine들을 종료합니다.

 

IEnumerator CoroutineSample()

 

StartCoroutine()으로 호출하여 실행하기 위해선 함수가 IEnumerator를 반환하도록 작성해야 합니다.

 

yield return new WaitForSeconds(2f);

 

yield return은 IEnumerator에서 사용되며 후에 기술되는 조건에 충족했을 때 함수를 이어서 진행하게끔 동작하는 구문입니다. 대기하는 동안 함수는 상태를 그대로 유지합니다.

 

3) 출력 결과

 

* yield return의 다양한 사용법

사용법 설명
yield return null; 다음 프레임까지 대기, 조건 없이 대기합니다.
yield return new WaitForSeconds(1.0f); 지정한 시간(초 단위) 대기, 게임 시간에 영향
yield return new WaitForRealSeconds(1.0f); 지정한 시간(초 단위) 대기, 실제 시간에 영향
yield return new WaitForUntill(i == 1); 특정 조건이 참이 될 때 까지 대기
yield return new WaitWhile(i == 1); 특정 조건이 거짓이 될 때 까지 대기 
yield return new WaitForEndOfFrame(); 현재 프레임이 끝날 때 까지 대기
yield return new WaitForFixedUpdate(); 다음 FixedUpdate()까지 대기 (Unity Life Cycle)

 

yield return의 사용법에 대해 찾아보면 위처럼 다양한 사용법들이 존재하는데, 저는 아직 WaitForSeconds(), WaitForRealSeconds(), WaitForFixedUpdate() 이 3가지만 사용해본 것 같습니다.

 

WaitForSeconds()와 WaitForRealSeconds()는 시간을 기준으로 대기한다는 공통점이 있지만 이 둘의 시간을 체크하는 방식이 다릅니다.

비교 항목 WaitForSeconds() WaitForRealSeconds()
Time.timeScale의 영향 Time.timeScale 수치에 따라 느려지거나 빨라집니다. 영향 없이 항상 동일
일시정지 여부 Time.timeScale = 0 으로 게임이 멈추면 Coroutine도 일시정지 합니다. 정지하지 않고 시간이 동일하게 흐르고 있습니다.
프레임의 영향 프레임에 영향을 받습니다. ⭕ 프레임에 영향을 받지 않습니다.  ❌

 

* 조금 더 효율적으로 사용하는 방법 (자주 사용되는 Coroutine의 경우)

using System.Collections;
using UnityEngine;

public class Coroutine : MonoBehaviour
{
    private IEnumerator coroutineSample;

    void Start()
    {
        coroutineSample = CoroutineSample();
        StartCoroutine(coroutineSample);
    }

    IEnumerator CoroutineSample()
    {
        while (true)
        {
            Debug.Log("코루틴 실행 중");
            yield return new WaitForSeconds(1f);
        }
    }
}

 

StartCoroutine()으로 IEnumerator를 직접 호출하는 경우 호출될 때 마다 매번 IEnumerator 객체가 생성되기 때문에 메모리에 낭비가 생길 수 있습니다. 자주 호출되는 Coroutine의 경우 IEnumerator에 미리 대입 시켜둔 상태로 호출하는 것이 더 효율적입니다.

 

* 한 번 호출되고 말 Coroutine의 경우 굳이 미리 선언해줄 필요는 없습니다. 

 


3. Async / Await와 비교 (비동기 프로그래밍)

Unity에서 비동기 처리를 한다고 하면 보통 Coroutine과 Async / Await 방식을 사용할 것입니다. 이 두 가지 방식의 차이점을 알아보도록 하겠습니다.

비교 항목 Coroutine Async/Await
실행 방식 (기반) Frame Thread
대기 키워드 yield return async, await, Task
Unity와 연계 효율 (WaitForSecoreds 사용 여부)  (프레임과 무관)
네트워크 요청 ❌ (지원하지 않는 기능) ⭕ (Task로 요청 가능)
동시 실행 관리 (병렬 처리) ❌ (어려움) ⭕ (Task.WhenAll로 쉽게 가능)

 

가장 큰 차이는 Coroutine은 유니티 엔진의 프레임과 동기화가 되어있기 때문에 WaitForSeconds를 사용할 수 있는 반면 Async/Await는 유니티 엔진의 프레임과는 별도로 동작하기 때문에 WaitForSeconds를 사용할 수 없고, 대신 반대로 Async/Await는 유니티의 환경에 구애받지 않고 정확한 비동기 프로그래밍을 구현할 수 있습니다.

 

* 이러한 차이점들로 인해 보통 Coroutine은 유니티 내부에서 애니메이션이나 캐릭터, UI의 이동 등 프레임 기반 로직에 많이 사용되고 Async/Await의 경우 실제 비동기 동작을 필요로 하는 네트워크 요청, 파일 입출력 등에 사용됩니다.

 


4. 중요한 점 정리

- WaitForSeconds()와 WaitForRealSeconds()는 시간을 기준으로 대기한다는 공통점이 있지만 WaitForSeconds()는 게임 시간을 기반으로 WaitForRealSeconds()는 현실 시간을 기반으로 시간의 흐름을 체크합니다.

 

- 자주 호출되는 Coroutine의 경우 IEnumerator에 미리 대입 시켜둔 상태로 호출하는 것이 더 효율적입니다.

 

- Coroutine은 유니티 엔진의 프레임을 기반으로 대기하고 Async/Await는 유니티 엔진의 프레임에 구애받지 않고 별도로 대기합니다.