Public

[Unity] while 문 그만! Coroutine에서 '우아하게' 기다리기 : WaitUntil, WaitWhile

김치킨. 2025. 12. 20. 00:36

게임 로직을 짜다 보면 "특정 조건이 만족될 때까지 대기"해야 하는 상황이 정말 많이 발생합니다.

  • 플레이어의 HP가 0이 될 때까지 대기
  • 스페이스 바를 누를 때까지 대기
  • 적들이 모두 사라질 때까지 대기

보통 우리는 Coroutine(코루틴)을 사용해 이 문제를 해결하는데요. 여러분은 이 코드를 어떻게 작성하고 계신가요? 오늘은 고전적인 방식과 유니티가 제공하는 조금 더 '우아한' 방식을 비교해 보겠습니다.


1. 고전적인 방식: while 루프와 yield return null

가장 흔하게 사용되는 패턴입니다. while 문으로 조건을 검사하고, 조건이 만족되지 않으면 yield return null을 통해 1 프레임을 쉬는 방식이죠.

예를 들어, 플레이어의 HP가 0 이하가 될 때까지 기다리는 코드는 보통 이렇게 작성합니다.

IEnumerator WaitForDeath_Classic()
{
    Debug.Log("전투 시작! 죽을 때까지 기다립니다...");

    // HP가 0보다 큰 동안 계속 루프를 돕니다.
    while (playerHP > 0)
    {
        // 1 프레임 대기 후 다시 조건을 검사합니다.
        yield return null; 
    }

    Debug.Log("플레이어 사망! 게임 오버 처리를 시작합니다.");
}

이 방식의 단점?

사실 기능상으로는 전혀 문제가 없습니다. 하지만:

  1. 가독성: while 문 안에 yield return null이 들어가는 구조가 매번 반복되어 코드가 길어집니다.
  2. 의도 파악: 코드를 읽을 때 "이 루프가 도대체 무엇을 기다리는 것인가?"를 파악하기 위해 루프 내부 로직을 해석해야 할 때가 있습니다.

2. 우아한 방식: WaitUntilWaitWhile

유니티는 이러한 패턴을 더 직관적으로 작성할 수 있도록 WaitUntilWaitWhile이라는 클래스를 제공합니다. 이들은 델리게이트(Delegate) 를 인자로 받아 조건을 판단합니다.

1) WaitUntil: 조건이 True가 될 때까지 대기

말 그대로 "~할 때까지(Until)" 기다리는 것입니다. 괄호 안의 조건이 true가 되면 대기를 멈추고 다음 줄로 넘어갑니다.

위의 while 문 코드를 리팩토링해 볼까요?

IEnumerator WaitForDeath_Elegant()
{
    Debug.Log("전투 시작! 죽을 때까지 기다립니다...");

    // 플레이어의 HP가 0 이하가 될 때(True)까지 대기합니다.
    yield return new WaitUntil(() => playerHP <= 0);

    Debug.Log("플레이어 사망! 게임 오버 처리를 시작합니다.");
}

단 한 줄로 줄어들었습니다!
() => playerHP <= 0 은 람다식(Lambda Expression)으로, "HP가 0 이하인가?"라는 조건을 실시간으로 체크합니다.

2) WaitWhile: 조건이 True인 동안 대기

이건 반대입니다. "~하는 동안(While)" 계속 기다리는 것입니다. 즉, 괄호 안의 조건이 false가 되어야 탈출합니다.

IEnumerator WaitForInput_Elegant()
{
    Debug.Log("키 입력을 기다리는 중...");

    // 스페이스 바를 누르지 않고 있는 동안(True) 계속 대기합니다.
    // 즉, 스페이스 바를 누르면(False) 대기가 끝납니다.
    yield return new WaitWhile(() => !Input.GetKeyDown(KeyCode.Space));

    Debug.Log("스페이스 바 입력 확인!");
}

3. 비교 및 요약

특징 고전 방식 (while) 우아한 방식 (WaitUntil / WaitWhile)
구문 while(조건) { yield return null; } yield return new WaitUntil(() => 조건);
가독성 루프 구조로 인해 다소 김 영어 문장처럼 읽혀 직관적임
유연성 루프 내부에 추가 로직 넣기 쉬움 오직 '대기' 목적에 특화됨
성능 가비지 생성 없음 new 키워드로 인한 미세한 가비지 생성 (무시할 수준)

언제 무엇을 써야 할까?

  • 단순히 "특정 상태가 변하길 기다리는 것"이 목적이라면 WaitUntil / WaitWhile을 쓰세요. 코드가 훨씬 깔끔해지고 "나는 지금 기다리는 중이다"라는 의도가 명확해집니다.
  • 기다리는 동안 매 프레임 다른 연산(예: 타이머 카운트 다운, 로그 출력 등)을 같이 해야 한다면 기존의 while 문을 쓰는 것이 낫습니다.

마무리

코드는 컴퓨터가 읽는 글이기도 하지만, 동료(혹은 미래의 나)가 읽는 글이기도 합니다.
똑같은 기능을 하더라도 yield return new WaitUntil(() => isFinished);라고 적혀 있다면, 누구나 직관적으로 "아, 끝날 때까지 기다리는구나!"라고 이해할 수 있을 것입니다.

지금 작성 중인 코루틴에 불필요한 while 루프가 있다면, 오늘 한 번 리팩토링해 보는 건 어떨까요?