카테고리 없음

유니티 코루틴 Coroutine(심화)

wook101118 2026. 6. 1. 07:30

유니티에서 게임을 만들다 보면 이런 상황이 자주 생긴다.

 
1초 기다렸다가 실행하기
공격 후 0.5초 뒤 다시 공격 가능하게 만들기
체력이 천천히 회복되게 만들기
화면이 서서히 어두워지게 만들기
몬스터가 일정 시간마다 움직이게 만들기
 

이런 기능을 만들 때 자주 사용하는 것이 바로 코루틴 Coroutine이다.


1. 코루틴이란?

코루틴은 쉽게 말하면 중간에 멈췄다가 다시 이어서 실행할 수 있는 함수이다.

일반 함수는 실행되면 위에서 아래로 한 번에 끝까지 실행된다.

 
void Test()
{
    Debug.Log("시작");
    Debug.Log("끝");
}
 

이 함수는 시작을 출력하고 바로 끝을 출력한다.

하지만 코루틴은 중간에 기다릴 수 있다.

 
IEnumerator TestCoroutine()
{
    Debug.Log("시작");

    yield return new WaitForSeconds(1f);

    Debug.Log("1초 뒤 실행");
}
 

이 코드는 시작을 출력하고, 1초 기다린 뒤 1초 뒤 실행을 출력한다.

즉, 코루틴은 시간 차를 두고 코드를 실행할 때 사용하는 기능이다.


2. 코루틴을 사용하는 이유

유니티에는 Update()가 있다.

 
void Update()
{
    // 매 프레임 실행
}
 

Update()는 매 프레임 계속 실행된다.
그래서 시간을 직접 계산하려면 보통 이런 식으로 작성해야 한다.

 
float timer = 0f;

void Update()
{
    timer += Time.deltaTime;

    if (timer >= 1f)
    {
        Debug.Log("1초 지남");
        timer = 0f;
    }
}
 

이 방식도 맞지만, 코드가 길어질수록 복잡해진다.

코루틴을 사용하면 훨씬 간단하게 표현할 수 있다.

 
IEnumerator Timer()
{
    yield return new WaitForSeconds(1f);
    Debug.Log("1초 지남");
}
 

코루틴은 특히 기다림, 반복, 순차 실행이 필요한 코드에서 편하다.


3. 코루틴의 기본 형태

코루틴은 보통 IEnumerator를 반환한다.

 
IEnumerator CoroutineName()
{
    yield return null;
}
 

코루틴을 실행할 때는 StartCoroutine()을 사용한다.

 
StartCoroutine(CoroutineName());
 

전체 예시는 다음과 같다.

 
using System.Collections;
using UnityEngine;

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

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

        yield return new WaitForSeconds(2f);

        Debug.Log("2초 뒤 실행");
    }
}
 

여기서 중요한 부분은 3가지다.

 
using System.Collections;
 
 
IEnumerator
 
 
yield return
 

코루틴을 사용하려면 보통 System.Collections가 필요하다.


4. yield return이란?

yield return은 코루틴에서 가장 중요한 부분이다.

yield return은 쉽게 말하면 여기서 잠깐 멈추고, 조건이 되면 다시 실행해라라는 뜻이다.

예를 들어 다음 코드를 보자.

 
IEnumerator Test()
{
    Debug.Log("A");

    yield return new WaitForSeconds(1f);

    Debug.Log("B");
}
 

실행 순서는 이렇다.

 
A 출력
1초 대기
B 출력
 

즉, yield return new WaitForSeconds(1f); 부분에서 코루틴이 잠시 멈춘다.


5. 자주 사용하는 yield return 종류

5-1. yield return null

 
yield return null;
 

yield return null은 한 프레임 기다린다는 뜻이다.

예시:

 
IEnumerator Test()
{
    Debug.Log("현재 프레임");

    yield return null;

    Debug.Log("다음 프레임");
}
 

실행 순서:

 
현재 프레임 출력
한 프레임 대기
다음 프레임 출력
 

yield return null은 부드러운 이동, 페이드 효과, 반복 처리 등에 많이 사용된다.


5-2. WaitForSeconds

 
yield return new WaitForSeconds(1f);
 

WaitForSeconds는 게임 시간 기준으로 특정 초만큼 기다린다.

예시:

 
IEnumerator AttackDelay()
{
    Debug.Log("공격");

    yield return new WaitForSeconds(0.5f);

    Debug.Log("다시 공격 가능");
}
 

이 코드는 공격 후 0.5초 뒤 다시 공격 가능하게 만들 때 사용할 수 있다.

주의할 점은 WaitForSeconds는 Time.timeScale의 영향을 받는다는 것이다.

 
Time.timeScale = 0f;
 

이렇게 게임 시간이 멈추면 WaitForSeconds도 같이 멈춘다.


5-3. WaitForSecondsRealtime

 
yield return new WaitForSecondsRealtime(1f);
 

WaitForSecondsRealtime은 실제 시간 기준으로 기다린다.

즉, Time.timeScale이 0이어도 시간이 흐른다.

예를 들어 일시정지 화면에서도 1초 뒤 어떤 UI를 띄우고 싶다면 WaitForSecondsRealtime을 사용할 수 있다.

 
IEnumerator PauseMessage()
{
    Time.timeScale = 0f;

    yield return new WaitForSecondsRealtime(1f);

    Debug.Log("실제 시간 1초 뒤 실행");
}
 

5-4. WaitUntil

 
yield return new WaitUntil(() => 조건);
 

WaitUntil은 조건이 true가 될 때까지 기다린다.

예시:

 
bool isReady = false;

IEnumerator WaitReady()
{
    Debug.Log("준비될 때까지 대기");

    yield return new WaitUntil(() => isReady == true);

    Debug.Log("준비 완료");
}
 

isReady가 true가 될 때까지 코루틴은 멈춰 있다가, true가 되면 다음 줄이 실행된다.


5-5. WaitWhile

 
yield return new WaitWhile(() => 조건);
 

WaitWhile은 조건이 true인 동안 기다린다.

예시:

 
bool isLoading = true;

IEnumerator WaitLoading()
{
    Debug.Log("로딩 중");

    yield return new WaitWhile(() => isLoading == true);

    Debug.Log("로딩 끝");
}
 

isLoading이 true인 동안 기다리다가, false가 되면 다음 코드가 실행된다.


5-6. WaitForEndOfFrame

 
yield return new WaitForEndOfFrame();
 

WaitForEndOfFrame은 현재 프레임의 렌더링이 거의 끝난 뒤 실행되게 한다.

주로 스크린샷 저장, 화면 캡처 같은 기능에서 사용된다.

 
IEnumerator CaptureScreen()
{
    yield return new WaitForEndOfFrame();

    Debug.Log("프레임 끝난 뒤 실행");
}
 

일반적인 게임 로직에서는 자주 쓰이지 않지만, 화면 처리에서는 중요하다.


5-7. WaitForFixedUpdate

 
yield return new WaitForFixedUpdate();
 

WaitForFixedUpdate는 다음 FixedUpdate() 타이밍까지 기다린다.

물리 연산과 관련된 처리를 할 때 사용할 수 있다.

 
IEnumerator PhysicsRoutine()
{
    yield return new WaitForFixedUpdate();

    Debug.Log("FixedUpdate 이후 실행");
}
 

6. 코루틴 실행하기

코루틴은 그냥 함수처럼 호출하면 안 된다.

잘못된 예시:

 
MyCoroutine();
 

이렇게 하면 코루틴이 제대로 실행되지 않는다.

올바른 예시:

 
StartCoroutine(MyCoroutine());
 

예시:

 
void Start()
{
    StartCoroutine(MyCoroutine());
}

IEnumerator MyCoroutine()
{
    yield return new WaitForSeconds(1f);
    Debug.Log("실행 완료");
}
 

7. 문자열로 코루틴 실행하기

코루틴은 문자열로도 실행할 수 있다.

 
StartCoroutine("MyCoroutine");
 

예시:

 
void Start()
{
    StartCoroutine("MyCoroutine");
}

IEnumerator MyCoroutine()
{
    yield return new WaitForSeconds(1f);
    Debug.Log("실행");
}
 

하지만 이 방식은 추천하지 않는다.

이유는 다음과 같다.

 
StartCoroutine("MyCoroutine");
 

문자열로 실행하면 함수 이름을 잘못 적어도 컴파일 단계에서 바로 잡기 어렵다.
또한 코드 추적이 어렵고, 리팩토링할 때 실수하기 쉽다.

그래서 보통은 아래 방식을 더 추천한다.

 
StartCoroutine(MyCoroutine());
 

8. 코루틴 멈추기

코루틴을 멈출 때는 StopCoroutine()을 사용한다.

 
StopCoroutine(coroutine);
 

예시:

 
Coroutine myCoroutine;

void Start()
{
    myCoroutine = StartCoroutine(MyCoroutine());
}

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        StopCoroutine(myCoroutine);
    }
}

IEnumerator MyCoroutine()
{
    while (true)
    {
        Debug.Log("반복 중");
        yield return new WaitForSeconds(1f);
    }
}
 

위 코드는 스페이스바를 누르면 실행 중인 코루틴이 멈춘다.


9. StopAllCoroutines

 
StopAllCoroutines();
 

StopAllCoroutines()은 현재 스크립트에서 실행 중인 모든 코루틴을 멈춘다.

예시:

 
void StopAll()
{
    StopAllCoroutines();
}
 

주의할 점은 현재 MonoBehaviour에서 실행한 코루틴만 멈춘다는 것이다.
게임 전체의 모든 코루틴을 멈추는 것이 아니다.


10. 코루틴 안에서 반복문 사용하기

코루틴 안에서도 for, while 같은 반복문을 사용할 수 있다.

for문 예시

 
IEnumerator CountRoutine()
{
    for (int i = 0; i < 5; i++)
    {
        Debug.Log(i);

        yield return new WaitForSeconds(1f);
    }

    Debug.Log("카운트 끝");
}
 

실행 결과:

 
0
1초 대기
1
1초 대기
2
1초 대기
3
1초 대기
4
1초 대기
카운트 끝
 

while문 예시

 
IEnumerator RepeatRoutine()
{
    while (true)
    {
        Debug.Log("계속 반복");

        yield return new WaitForSeconds(1f);
    }
}
 

이 코드는 1초마다 계속 실행된다.

단, while (true)를 사용할 때는 반드시 yield return이 있어야 한다.

잘못된 예시:

 
IEnumerator BadRoutine()
{
    while (true)
    {
        Debug.Log("위험");
    }
}
 

이 코드는 멈추는 지점이 없기 때문에 유니티가 멈출 수 있다.

올바른 예시:

 
IEnumerator GoodRoutine()
{
    while (true)
    {
        Debug.Log("안전");

        yield return null;
    }
}
 

11. 코루틴과 Update의 차이

Update()와 코루틴은 둘 다 반복 실행에 사용할 수 있다.

하지만 쓰임이 조금 다르다.

구분UpdateCoroutine
실행 방식 매 프레임 자동 실행 StartCoroutine으로 실행
시간 대기 직접 타이머 구현 필요 yield return으로 간단히 처리
사용 예시 입력 처리, 실시간 이동 딜레이, 순차 실행, 반복 이벤트
멈춤 처리 bool 변수로 제어 StopCoroutine 사용 가능
코드 흐름 계속 검사하는 느낌 순서대로 진행하는 느낌

예를 들어 키 입력처럼 매 순간 확인해야 하는 것은 Update()가 좋다.

 
void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        Debug.Log("점프");
    }
}
 

반대로 1초 기다렸다가 실행하는 것은 코루틴이 더 좋다.

 
IEnumerator Delay()
{
    yield return new WaitForSeconds(1f);
    Debug.Log("1초 뒤 실행");
}
 

12. 코루틴은 멀티스레드가 아니다

코루틴을 처음 배우면 오해하기 쉬운 부분이 있다.

코루틴은 멀티스레드가 아니다.

즉, 코루틴이 다른 CPU 스레드에서 동시에 실행되는 것이 아니다.
유니티의 메인 스레드에서 실행 흐름을 나누어 처리하는 것이다.

그래서 코루틴 안에서 무거운 반복 작업을 하면 게임이 멈출 수 있다.

잘못된 예시:

 
IEnumerator HeavyWork()
{
    for (int i = 0; i < 100000000; i++)
    {
        // 무거운 작업
    }

    yield return null;
}
 

이 코드는 코루틴이라고 해도 한 프레임 안에서 너무 많은 작업을 하기 때문에 게임이 멈출 수 있다.

개선 예시:

 
IEnumerator HeavyWork()
{
    for (int i = 0; i < 100000000; i++)
    {
        if (i % 10000 == 0)
        {
            yield return null;
        }
    }

    Debug.Log("작업 완료");
}
 

중간중간 yield return null을 넣으면 여러 프레임에 나누어 처리할 수 있다.


13. 코루틴에 매개변수 전달하기

코루틴도 일반 함수처럼 매개변수를 받을 수 있다.

 
void Start()
{
    StartCoroutine(PrintAfterDelay("안녕하세요", 2f));
}

IEnumerator PrintAfterDelay(string message, float delay)
{
    yield return new WaitForSeconds(delay);

    Debug.Log(message);
}
 

이 코드는 2초 뒤 "안녕하세요"를 출력한다.


14. 코루틴에서 값 반환하기

코루틴은 일반 함수처럼 바로 값을 반환하기 어렵다.

예를 들어 이런 식으로는 사용할 수 없다.

 
int result = StartCoroutine(MyCoroutine());
 

코루틴은 IEnumerator를 반환하고, 실행 결과를 바로 int, float, string으로 받는 구조가 아니다.

대신 콜백을 사용할 수 있다.

 
void Start()
{
    StartCoroutine(GetValueRoutine((value) =>
    {
        Debug.Log("결과: " + value);
    }));
}

IEnumerator GetValueRoutine(System.Action<int> callback)
{
    yield return new WaitForSeconds(1f);

    int result = 10;

    callback?.Invoke(result);
}
 

이 방식은 코루틴이 끝난 뒤 결과를 전달할 때 사용할 수 있다.


15. 코루틴 안에서 다른 코루틴 기다리기

코루틴 안에서 다른 코루틴이 끝날 때까지 기다릴 수도 있다.

 
IEnumerator MainRoutine()
{
    Debug.Log("메인 시작");

    yield return StartCoroutine(SubRoutine());

    Debug.Log("서브 코루틴 끝난 뒤 실행");
}

IEnumerator SubRoutine()
{
    Debug.Log("서브 시작");

    yield return new WaitForSeconds(2f);

    Debug.Log("서브 끝");
}
 

실행 순서:

 
메인 시작
서브 시작
2초 대기
서브 끝
서브 코루틴 끝난 뒤 실행
 

이렇게 하면 여러 행동을 순서대로 연결할 수 있다.


16. 코루틴 중복 실행 문제

코루틴을 사용할 때 자주 생기는 문제가 있다.
바로 같은 코루틴이 여러 번 실행되는 문제이다.

예를 들어 공격 버튼을 누를 때마다 코루틴을 실행한다고 해보자.

 
void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        StartCoroutine(AttackDelay());
    }
}

IEnumerator AttackDelay()
{
    Debug.Log("공격");

    yield return new WaitForSeconds(1f);

    Debug.Log("공격 딜레이 끝");
}
 

스페이스바를 여러 번 누르면 AttackDelay()가 여러 개 동시에 실행될 수 있다.

이를 막으려면 bool 변수를 사용하면 된다.

 
bool isAttacking = false;

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space) && !isAttacking)
    {
        StartCoroutine(AttackDelay());
    }
}

IEnumerator AttackDelay()
{
    isAttacking = true;

    Debug.Log("공격");

    yield return new WaitForSeconds(1f);

    Debug.Log("공격 딜레이 끝");

    isAttacking = false;
}
 

이렇게 하면 공격 코루틴이 실행 중일 때는 다시 실행되지 않는다.


17. 코루틴과 비활성화

코루틴은 실행 중인 오브젝트나 스크립트 상태에 영향을 받는다.

일반적으로 MonoBehaviour가 비활성화되거나 오브젝트가 비활성화되면 그 스크립트에서 실행 중인 코루틴도 멈출 수 있다.

예를 들어 다음과 같은 상황이다.

 
gameObject.SetActive(false);
 

이렇게 오브젝트 자체를 비활성화하면, 그 오브젝트의 스크립트에서 실행 중이던 코루틴도 중단될 수 있다.

그래서 중요한 코루틴은 비활성화되지 않는 매니저 오브젝트에서 실행하는 것이 좋다.

예시:

 
GameManager
SoundManager
UIManager
 

18. 코루틴과 Time.timeScale

코루틴에서 시간 대기를 할 때 Time.timeScale의 영향을 받는지 알아야 한다.

WaitForSeconds

 
yield return new WaitForSeconds(1f);
 

WaitForSeconds는 Time.timeScale의 영향을 받는다.

 
Time.timeScale = 0f;
 

게임이 일시정지되면 WaitForSeconds도 멈춘다.


WaitForSecondsRealtime

 
yield return new WaitForSecondsRealtime(1f);
 

WaitForSecondsRealtime은 Time.timeScale의 영향을 받지 않는다.

그래서 일시정지 중에도 시간이 흐른다.

대기 방식Time.timeScale 영향
WaitForSeconds 받음
WaitForSecondsRealtime 받지 않음
yield return null 프레임 기준
WaitUntil 조건 기준
WaitWhile 조건 기준

19. 코루틴으로 페이드 효과 만들기

코루틴은 UI 페이드 효과에 자주 사용된다.

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

public class FadeExample : MonoBehaviour
{
    public Image fadeImage;

    void Start()
    {
        StartCoroutine(FadeOut());
    }

    IEnumerator FadeOut()
    {
        Color color = fadeImage.color;

        while (color.a > 0f)
        {
            color.a -= Time.deltaTime;
            fadeImage.color = color;

            yield return null;
        }

        color.a = 0f;
        fadeImage.color = color;
    }
}
 

이 코드는 이미지의 알파값을 천천히 줄여서 서서히 사라지게 만든다.

여기서 yield return null을 사용했기 때문에 매 프레임 조금씩 알파값이 줄어든다.


20. 코루틴으로 공격 쿨타임 만들기

공격 후 일정 시간 동안 다시 공격하지 못하게 만들 수 있다.

 
using System.Collections;
using UnityEngine;

public class AttackExample : MonoBehaviour
{
    bool canAttack = true;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space) && canAttack)
        {
            StartCoroutine(AttackRoutine());
        }
    }

    IEnumerator AttackRoutine()
    {
        canAttack = false;

        Debug.Log("공격 실행");

        yield return new WaitForSeconds(0.5f);

        canAttack = true;

        Debug.Log("다시 공격 가능");
    }
}
 

이런 구조는 게임에서 정말 많이 사용된다.


21. 코루틴으로 체력 회복 만들기

체력을 일정 시간마다 회복시키는 것도 가능하다.

 
using System.Collections;
using UnityEngine;

public class HealExample : MonoBehaviour
{
    int hp = 50;
    int maxHp = 100;

    void Start()
    {
        StartCoroutine(HealRoutine());
    }

    IEnumerator HealRoutine()
    {
        while (hp < maxHp)
        {
            hp += 10;

            if (hp > maxHp)
            {
                hp = maxHp;
            }

            Debug.Log("현재 체력: " + hp);

            yield return new WaitForSeconds(1f);
        }

        Debug.Log("체력 회복 완료");
    }
}
 

이 코드는 체력이 최대 체력이 될 때까지 1초마다 체력을 10씩 회복한다.


22. 코루틴으로 몬스터 스폰 만들기

일정 시간마다 몬스터를 생성할 때도 코루틴을 사용할 수 있다.

 
using System.Collections;
using UnityEngine;

public class SpawnExample : MonoBehaviour
{
    public GameObject monsterPrefab;
    public Transform spawnPoint;

    void Start()
    {
        StartCoroutine(SpawnRoutine());
    }

    IEnumerator SpawnRoutine()
    {
        while (true)
        {
            Instantiate(monsterPrefab, spawnPoint.position, Quaternion.identity);

            yield return new WaitForSeconds(3f);
        }
    }
}
 

이 코드는 3초마다 몬스터를 생성한다.


23. 코루틴으로 순서 있는 이벤트 만들기

코루틴은 이벤트를 순서대로 진행할 때도 좋다.

 
IEnumerator EventRoutine()
{
    Debug.Log("문이 열린다");

    yield return new WaitForSeconds(1f);

    Debug.Log("몬스터가 등장한다");

    yield return new WaitForSeconds(2f);

    Debug.Log("보스전 시작");
}
 

이런 방식은 튜토리얼, 컷신, 스테이지 이벤트 등에 사용할 수 있다.


24. 코루틴과 Invoke의 차이

유니티에는 Invoke()라는 기능도 있다.

 
Invoke("Test", 1f);
 

이 코드는 1초 뒤 Test() 함수를 실행한다.

 
void Test()
{
    Debug.Log("실행");
}
 

하지만 Invoke()는 단순한 지연 실행에 가깝다.

구분InvokeCoroutine
사용 목적 일정 시간 뒤 함수 실행 시간 대기, 반복, 순서 제어
복잡한 흐름 불편함 편함
매개변수 전달 불편함 가능
중간 대기 여러 번 어려움 쉬움
코드 가독성 간단한 경우 좋음 복잡한 흐름에 좋음

단순히 1초 뒤 함수 하나 실행할 때는 Invoke()도 괜찮다.
하지만 여러 단계가 있는 로직은 코루틴이 더 좋다.


25. 코루틴과 async/await의 차이

C#에는 async/await도 있다.

코루틴과 비슷하게 기다리는 코드를 작성할 수 있지만, 목적이 조금 다르다.

구분Coroutineasync/await
유니티에서의 사용 게임 로직에 자주 사용 비동기 작업에 자주 사용
반환 형태 IEnumerator Task
대기 방식 yield return await
사용 예시 딜레이, 애니메이션, 반복 이벤트 파일 로드, 서버 통신, 비동기 처리
MonoBehaviour 필요 보통 필요 꼭 필요하지는 않음

유니티 게임 로직에서는 코루틴이 많이 쓰이고,
서버 통신이나 파일 작업 같은 비동기 작업에서는 async/await이 쓰일 수 있다.


26. 코루틴 사용 시 주의할 점

26-1. 무한 반복에는 yield return을 꼭 넣기

잘못된 코드:

 
IEnumerator BadRoutine()
{
    while (true)
    {
        Debug.Log("무한 반복");
    }
}
 

이 코드는 유니티가 멈출 수 있다.

올바른 코드:

 
IEnumerator GoodRoutine()
{
    while (true)
    {
        Debug.Log("무한 반복");

        yield return null;
    }
}
 

26-2. 중복 실행 조심하기

 
StartCoroutine(MyCoroutine());
StartCoroutine(MyCoroutine());
StartCoroutine(MyCoroutine());
 

이렇게 하면 같은 코루틴이 3개 동시에 실행된다.

중복 실행을 막으려면 bool 변수를 사용한다.

 
bool isRunning = false;

IEnumerator MyCoroutine()
{
    if (isRunning)
    {
        yield break;
    }

    isRunning = true;

    yield return new WaitForSeconds(1f);

    isRunning = false;
}
 

26-3. StopCoroutine을 정확히 사용하기

코루틴을 멈추려면 실행한 코루틴을 변수에 저장해두는 것이 좋다.

 
Coroutine routine;

void Start()
{
    routine = StartCoroutine(MyCoroutine());
}

void Stop()
{
    StopCoroutine(routine);
}
 

아래처럼 실행 방식과 중지 방식이 섞이면 문제가 생길 수 있다.

 
StartCoroutine(MyCoroutine());
StopCoroutine(MyCoroutine());
 

이 방식은 새 IEnumerator를 만들어서 넘기는 형태라, 이미 실행 중인 코루틴을 제대로 멈추지 못할 수 있다.


26-4. 오브젝트 비활성화에 주의하기

코루틴을 실행 중인 오브젝트가 꺼지면 코루틴도 중단될 수 있다.

 
gameObject.SetActive(false);
 

그래서 중요한 흐름은 꺼지지 않는 매니저 오브젝트에서 관리하는 것이 안전하다.


26-5. WaitForSeconds를 너무 많이 새로 만들지 않기

반복문 안에서 new WaitForSeconds()를 계속 만들면 작은 메모리 할당이 반복될 수 있다.

 
while (true)
{
    yield return new WaitForSeconds(1f);
}
 

대부분의 작은 프로젝트에서는 큰 문제가 되지 않지만, 최적화를 신경 써야 하는 경우에는 캐싱할 수 있다.

 
WaitForSeconds waitOneSecond = new WaitForSeconds(1f);

IEnumerator Routine()
{
    while (true)
    {
        yield return waitOneSecond;
    }
}
 

다만 대기 시간이 계속 바뀌는 경우에는 캐싱하기 어렵다.


27. yield break

yield break는 코루틴을 즉시 종료할 때 사용한다.

 
IEnumerator Test()
{
    Debug.Log("시작");

    yield break;

    Debug.Log("이 코드는 실행되지 않음");
}
 

조건에 따라 코루틴을 끝낼 때 유용하다.

 
IEnumerator HealRoutine()
{
    if (hp >= maxHp)
    {
        yield break;
    }

    hp += 10;

    yield return new WaitForSeconds(1f);
}
 

28. 코루틴 실행 순서 이해하기

다음 코드를 보자.

 
void Start()
{
    Debug.Log("A");

    StartCoroutine(Test());

    Debug.Log("B");
}

IEnumerator Test()
{
    Debug.Log("C");

    yield return null;

    Debug.Log("D");
}
 

실행 순서는 다음과 같다.

 
A
C
B
D
 

이유는 StartCoroutine(Test())를 실행하면 코루틴이 바로 시작되고,
첫 번째 yield return을 만날 때까지 실행된다.

그 후 다시 Start()로 돌아와 B가 출력된다.
그리고 다음 프레임에 D가 출력된다.


29. 코루틴을 언제 사용하면 좋을까?

코루틴은 다음 상황에서 사용하면 좋다.

 
몇 초 기다렸다가 실행할 때
일정 시간마다 반복할 때
순서대로 이벤트를 진행할 때
부드러운 UI 효과를 만들 때
공격 쿨타임을 만들 때
스폰 타이밍을 조절할 때
로딩이 끝날 때까지 기다릴 때
조건이 만족될 때까지 기다릴 때
 

반대로 다음 상황에서는 코루틴보다 다른 방식이 나을 수 있다.

 
매 프레임 입력을 감지해야 할 때 → Update
물리 처리를 해야 할 때 → FixedUpdate
단순히 한 번 지연 실행만 할 때 → Invoke도 가능
무거운 비동기 작업 → async/await 또는 별도 처리 고려
 

30. 코루틴 핵심 요약

코루틴은 유니티에서 시간을 다루는 코드를 쉽게 작성할 수 있게 해주는 기능이다.

핵심은 다음과 같다.

 
IEnumerator를 사용한다.
StartCoroutine으로 실행한다.
yield return으로 기다린다.
WaitForSeconds는 게임 시간 기준이다.
WaitForSecondsRealtime은 실제 시간 기준이다.
yield return null은 한 프레임 대기이다.
코루틴은 멀티스레드가 아니다.
중복 실행과 중지 처리에 주의해야 한다.
 

가장 기본 형태는 다음과 같다.

 
IEnumerator MyCoroutine()
{
    Debug.Log("시작");

    yield return new WaitForSeconds(1f);

    Debug.Log("1초 뒤 실행");
}
 

실행은 이렇게 한다.

 
StartCoroutine(MyCoroutine());
 

코루틴을 이해하면 공격 딜레이, 스폰, 회복, UI 애니메이션, 튜토리얼, 이벤트 연출 같은 기능을 훨씬 깔끔하게 만들 수 있다.
처음에는 yield return이 낯설지만, “잠깐 멈췄다가 나중에 이어서 실행하는 함수”라고 생각하면 이해하기 쉽다.