Unity - Coroutine, yield, Invoke, IEnumerator

2025. 10. 17. 16:42·Unity

코루틴 (Coroutine)

Unity에서 제공하는 특수한 함수 실행 방식이다.

일반적인 동기 함수는 한 번에 호출되면 끝까지 즉시 실행되는 반면,

코루틴은 중간에 잠시 멈췄다가(yield) 나중에 다시 이어서 실행할 수 있다.

마치 비동기처럼 동작하는 듯이 보이는데 사실 비동기와는 조금 다르다.

진행 중인 쓰레드에서 일시정지 후, 잠시 다른 로직의 진행에 쓰레드 자리를 내어준 뒤, 재실행 되는 듯한 구조를 보인다.

 

아래는 코루틴의 기본 구조다.

public class CoroutineExample : MonoBehaviour
{
    void Start()
    {
        StartCoroutine(PrintNumbers());
    }

    IEnumerator PrintNumbers()
    {
        for (int i = 1; i <= 5; i++)
        {
            Debug.Log(i);
            yield return new WaitForSeconds(1f); // 1초 대기
        }
    }
}

 

  1. StartCoroutine(PrintNumbers()) 호출 시, Unity는 IEnumerator를 받아서 코루틴 스케쥴러에 등록한다.
  2. yield return new WaitForSeconds(1f) 라인에서 해당 코루틴의 실행을 잠시 일시정지 한다.
  3. Unity 엔진이 1초 후, 다음 프레임에서 MoveNext() 를 호출하여 중단 지점 다음 코드부터 실행을 이어간다.
  4. 이렇게 반복하여 시간 기반의 순차 실행 로직을 구현할 수 있다.

 

  일반 함수 코루틴
실행 흐름 한 번 호출 시 끝까지 즉시 실행 실행 도중 yield로 중단 가능
제어 가능성 없음 시간, 프레임, 조건 기반 실행 가능
반환 타입 void / return 값 반드시 IEnumerator
사용 예시 즉시 계산 이펙트, 대기, 타이밍 제어

 

 

주요 yield return 구문

yield return null 다음 프레임에서 실행 재개 매 프레임 반복 처리
yield return new WaitForSeconds(t) t초 뒤 재개 일정 시간 지연
yield return new WaitUntil(() => condition) 조건이 참이 될 때까지 대기 특정 이벤트 대기
yield return new WaitWhile(() => condition) 조건이 거짓이 될 때까지 대기 조건 유지 중 대기

 

엔진 내부에서 C++로 연산

WaitForSeconds 의 내부 구현을 보기 위해 까봤을 때 YieldInstruction 클래스를 상속받고 있었다.

그래서 들어가봤더니 이게 무슨 일?

더보기
#region Assembly UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// C:\Program Files\Unity\Hub\Editor\6000.2.7f2\Editor\Data\Managed\UnityEngine\UnityEngine.CoreModule.dll
// Decompiled with ICSharpCode.Decompiler 9.1.0.7988
#endregion

using System.Runtime.InteropServices;
using UnityEngine.Scripting;

namespace UnityEngine;

//
// Summary:
//     Base class for all yield instructions.
[StructLayout(LayoutKind.Sequential)]
[UsedByNativeCode]
public class YieldInstruction
{
}
#if false // Decompilation log
'274' items in cache
------------------
Resolve: 'netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
Found single assembly: 'netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
Load from: 'C:\Program Files\Unity\Hub\Editor\6000.2.7f2\Editor\Data\NetStandard\ref\2.1.0\netstandard.dll'
------------------
Resolve: 'UnityEngine.SharedInternalsModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'
Found single assembly: 'UnityEngine.SharedInternalsModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'
Load from: 'C:\Program Files\Unity\Hub\Editor\6000.2.7f2\Editor\Data\Managed\UnityEngine\UnityEngine.SharedInternalsModule.dll'
------------------
Resolve: 'System.Runtime.InteropServices, Version=2.1.0.0, Culture=neutral, PublicKeyToken=null'
Found single assembly: 'System.Runtime.InteropServices, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
WARN: Version mismatch. Expected: '2.1.0.0', Got: '4.1.2.0'
Load from: 'C:\Program Files\Unity\Hub\Editor\6000.2.7f2\Editor\Data\NetStandard\compat\2.1.0\shims\netstandard\System.Runtime.InteropServices.dll'
------------------
Resolve: 'System.Runtime.CompilerServices.Unsafe, Version=2.1.0.0, Culture=neutral, PublicKeyToken=null'
Could not find by name: 'System.Runtime.CompilerServices.Unsafe, Version=2.1.0.0, Culture=neutral, PublicKeyToken=null'
#endif

빈 클래스???

 

이게 뭔가 했더니... 로직은 엔진 내부적으로 C++로 구현되어 있고 해당 클래스는 마커 역할로 기능을 묶어주는 역할만 한다는 모양이다.

결국 Unity도 깊게 공부하려면 C++까지 공부해야하는 것인가...

 

 

 

WaitForSeconds 캐싱 이슈 및 최적화 방안

WaitForSeconds 는 단순한 타이머 객체지만,

Unity는 내부적으로 이를 YieldInstruction 타입으로 관리한다.

즉, yield return 구문에서 Uniity의 코루틴 스케쥴러는 매번 새로운 WaitForSeconds 객체를 등록하여 대기 상태를 추적하게 된다.

 

코루틴에서 yield return new WaitForSeconds(t) 를 자주 호출할 경우,

매번 새로운 WaitForSeconds 객체가 생성되고, 이는 일시적으로 GC 부담을 증가시키게 되는데

특히 짧은 주기의 반복에서 프레임 드라입을 유발할 수 있다.

Profiler에도 GC Alloc 로그가 쌓이게 된다.

 

위에서 작성했던 코드에서는 이 문제가 발생하는 상태다.

이 문제를 해결하기 위해서는 다음 코드와 같이 WaitForSeconds 인스턴스를 미리 생성하여 캐싱해두는 패턴을 사용하면 된다.

 

private WaitForSeconds _delay;

void Start()
{
    // .....
    _delay = new WaitForSeconds(spawnInterval);

    StartCoroutine(SpawnLoop());
}

private void SpawnEnemy()
{
    // ......
}

private IEnumerator SpawnLoop()
{
    if (!enemyPrefab || _spawnPoints.Length == 0) yield break;

    while (true)
    {
        SpawnEnemy();
        yield return _delay;
    }
}

 

 


 

yield

대체 그럼 yield 이놈은 무엇이고 어디서 왔는가?

사실 yield는 Unity 전용 기능이 아니라 표준 C#의 문법이다.

yield는 "중간 값을 하나씩 반환하며, 실행을 일시 정지하는 기능"을 제공한다.

즉 Iterator(반복자) 를 단순하게 만들기 위한 키워드다.

 

다음 코드는 1, 2, 3 을 순차적으로 반환하는 반복자다.

IEnumerable<int> GetNumbers()
{
    yield return 1;
    yield return 2;
    yield return 3;
}

 

하지만 이 함수를 호출해도 즉시 실행되지 않는다.

내부적으로 IEnumerator 객체가 반환되고,

MoveNext() 호출될 때마다 yield return 다음 줄부터 다시 실행된다.

foreach (var n in GetNumbers()) Console.WriteLine(n);
// (실행 흐름)
// 1
// 정지
// 2
// 정지
// 3
// 종료

 

쉽게 요약하자면 yield return은 "값을 하나 반환하고 잠시 멈추는 신호" 라고 보면 된다.

 

그리고 Unity 에서는 "이 코루틴을 언제 다시 실행할지"를 알려주는 트리거로 사용되는 것이다.


 

Invoke

Coroutine 보다 비교적 간단한 지연 실행 메서드다.

특정 메서드를 일정 시간이 지난 뒤 자등오르 호출하도록 예약하는 타이머형 함수 호출이다.

 

public class InvokeExample : MonoBehaviour
{
    void Start()
    {
        // 2초 뒤 한 번 실행
        Invoke("Hello", 2f);

        // 1초 후부터 3초마다 반복 실행
        InvokeRepeating("RepeatHello", 1f, 3f);
    }

    void Hello()
    {
        Debug.Log("Hello after 2 sec");
    }

    void RepeatHello()
    {
        Debug.Log("Repeating every 3 sec");
    }

    void StopRepeating()
    {
        CancelInvoke("RepeatHello"); // 반복 중단
    }
}

 

장점

코드가 단순. Update()에서 직접 타이머 관리할 필요 없음

 

단점

문자열 기반으로 인한 컴파일러 체크 불가능 이슈(오타 위험)

흐름 제어가 제한적. 반복 외에는 유연하지 않아 복잡한 로직에는 부적합

 

단순 시간 지연에는 Invoke()
복합적인 시퀀스 제어에는 Coroutine

 

 


 

IEnumerator

C#에서 제공하는 반복자(Enumerator) 인터페이스로

컬렉션의 요소를 순회하거나 순차적으로 접근할 수 있게 해주는 구조다.

IEnumerator numbers = new int[] { 1, 2, 3 }.GetEnumerator();

while (numbers.MoveNext())
{
    Console.WriteLine(numbers.Current);
}

 

  • MoveNext() : 다음 요소로 이동
  • Current : 현재 요소 반환
  • 반복이 끝나면 MoveNext()는 false를 반환

이 구조를 Unity는 시간의 순회로 확장하여 코루틴에 적용한다.

 

 

코루틴은 내부적으로 IEnumerator를 기반으로 한다.

Unity는 StartCoroutine() 호출 시 다음과 같은 과정을 거친다.

  1. 코루틴 함수(IEnumerator)를 실행 → IEnumerator 객체 반환
  2. Unity의 내부 코루틴 스케쥴러가 프레임마다 MoveNext()를 호출
  3. yield return 에 따라 언제 다시 MoveNext()를 실행할지 결정

 

다음과 같은 구조로 동작한다.

IEnumerator PrintNumbers()
{
    for (int i = 1; i <= 5; i++)
    {
        Debug.Log(i);
        yield return new WaitForSeconds(1f);
    }
}
  • MoveNext() 호출 → 1 출력 후 일시정지
  • 1초 뒤 MoveNext() 재호출 → 2 출력
  • 반복, 5까지 출력 후 종료

 

 

메서드

MoveNext() 다음 실행 단계로 이동
Current 현재 반환 값 (Unity에선 yield 대상 객체)
Reset() 반복 초기화 (일반적으로 사용 안 함)

 

 

 

Coroutine vs Inkovke

"간단한 로직에서는 Invoke" 라고만 외우면 충분히 개념을 숙지했다고 보기 어렵다.

두 기능 모두 "일정 시간 후에 실행" 이라는 점에서 비슷해 보이지만 실제로는 작동 원리와 제어 방식이 다르다.

 

Invoke()

쓰레드가 아닌 메인 루프 기반의 지연 호출. 별도의 쓰레드가 생성되는 것이 아닌 엔진 내부의 메인 쓰레드 (메인 게임 루프) 에서 다음과 같은 방식으로 관리된다.

(pseudo code)
void Update()
{
    foreach (var invoke in scheduledInvokes)
    {
        invoke.timeRemaining -= Time.deltaTime;
        if (invoke.timeRemaining <= 0)
            invoke.method.Invoke();
    }
}

 

즉 Invoke()는 내부적으로 시간을 체크하다가 조건이 만족되면 실행하는 구조다.

3초 동안 쓰레드가 일시정지 되는 것과 다르다.

그렇기 때문에 코루틴에 비해 매우 가볍다.

 

Coroutine()

일시정지 가능한 실행 시퀀스. 단순한 지연 실행이 아니라 실행 도중 멈췄다 이어서 실행할 수 있는 함수로 이 역시 별도 스레드를 사용하진 않지만 Unity에서 Coroutine 스케줄러가 매 프레임마다 yield 상태를 체크한다.

루프와 조합해 반복 타이밍 제어가 가능하다.

단순 지연이 아니라 루프, 조건이 들어가면 코루틴이 적합하다.

 

  Invoke Coroutine
실행 형태 일정 시간 후 한 번 호출 실행 도중 멈췄다가 재개 가능
제어 주체 Unity 메인 루프의 Invoke 큐 Unity의 Coroutine 스케줄러
스레드 사용 없음 (싱글 스레드) 없음 (싱글 스레드)
반복 실행 InvokeRepeating() 필요 while + yield로 자유롭게 가능
내부 상태 유지 불가능 가능 (yield 시점부터 이어짐)
대표 용도 일정 시간 후 이벤트 트리거 순차 연출, 루프 타이밍 제어

 

 

'Unity' 카테고리의 다른 글

Unity - Spawner에 Singleton Pattern 적용하기  (0) 2025.10.20
Unity - EventSystem (이벤트 시스템)  (0) 2025.10.20
Unity - Rigidbody, Collider - 물리, 충돌 처리  (0) 2025.10.16
Unity - Transform, 위치 이동  (0) 2025.10.15
Unity - 오브젝트(Object), 컴포넌트(Component), 오브젝트 풀링(Object Pooling)  (0) 2025.10.14
'Unity' 카테고리의 다른 글
  • Unity - Spawner에 Singleton Pattern 적용하기
  • Unity - EventSystem (이벤트 시스템)
  • Unity - Rigidbody, Collider - 물리, 충돌 처리
  • Unity - Transform, 위치 이동
ellug
ellug
Web / App / Game Dev
  • ellug
    dev.ellug
    ellug
  • 전체
    오늘
    어제
    • 분류 전체보기 (95)
      • Javascript (React, Next) (1)
      • C# (66)
      • Graphics (1)
      • Unity (25)
      • 프로그래머스 (0)
        • Lv.0 (0)
        • Lv.1 (0)
        • Lv.2 (0)
        • Lv.3 (0)
      • AI Agent (0)
      • StableDiffusion (0)
      • ETC (1)
      • 잡담 (0)
      • 유머 (0)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    interface
    function
    알고리즘
    콘솔
    Abstract
    Design Pattern
    struct
    스택
    list
    터미널
    Network
    inputsystem
    class
    게임
    useState
    firebase
    자료구조
    console
    Photon
    추상 클래스
    유니티
    Queue
    최적화
    큐
    클래스
    C#
    stack
    unity
    네트워크
    delegate
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
ellug
Unity - Coroutine, yield, Invoke, IEnumerator
상단으로

티스토리툴바