로그인게임하기
Unity C# 강좌

[Unity C# 6] yield return, 필요할 때 하나씩 꺼내는 목록

2026.10.14

Unity C# 강좌 · 6편

목록을 돌려주는 함수는 보통 목록을 다 만든 다음에 돌려줍니다. yield return 을 쓰면 함수가 값을 하나 내놓고 멈췄다가, 다음 값을 달라고 할 때 이어서 실행합니다. 이번 편에서는 foreach 가 그 뒤에서 무엇을 하는지, 끝없는 목록과 중간에 멈추는 목록이 어떻게 동작하는지 봅니다.

수업 정보

2024년 부산 콘텐츠 아카데미 퍼스널 트레이닝 수업에서 MyLesson Unity 실습 프로젝트의 Strategy_06_IEnumarable.cs 로 가르친 내용입니다. 수업은 다섯 단계로 나뉘고, 단계마다 코드가 조금씩 바뀝니다.

STEP 1

멈췄다 이어 가는 함수

Strategy_06_IEnumarable.cs수업 자료 1단계7–25줄
private IEnumerable<string> IntRangeGroup(string first, string last, int min, int max)
{
    yield return first;

    for (int i = min; i <= max; i++)
    {
        yield return i.ToString();
    }

    yield return last;
}

public void Run()
{
    foreach (string value in IntRangeGroup("시작", "종료", 0, 5))
    {
        Program.WriteLine(value);
    }
}
실행 결과게임 화면의 출력 창
시작
0
1
2
3
4
5
종료

IntRangeGroup 은 return 으로 목록을 한 번에 돌려주지 않습니다. yield return 을 만날 때마다 값을 하나 내놓고 그 자리에서 멈춥니다. foreach 가 다음 값을 달라고 하면, 멈췄던 곳 다음 줄부터 이어서 실행합니다. 함수 안의 지역 변수 i 도 그대로 기억합니다.

STEP 2

foreach 를 풀어 쓰면

Strategy_06_IEnumarable.cs수업 자료 2단계19–30줄
public void Run()
{
    IEnumerable<string> enumerable = IntRangeGroup("2부터", "9까지", 2, 9);

    using (IEnumerator<string> enumerator = enumerable.GetEnumerator())
    {
        while (enumerator.MoveNext())
        {
            Program.WriteLine(enumerator.Current);
        }
    }
}

foreach 는 이 코드를 줄여 쓴 것입니다. GetEnumerator() 로 "꺼내는 도구" 를 받고, MoveNext() 로 다음 값으로 넘어가며, Current 로 지금 값을 읽습니다. MoveNext 가 false 를 돌려주면 끝입니다.

이름역할
IEnumerable<T>꺼낼 수 있는 목록. GetEnumerator() 하나만 약속
IEnumerator<T>꺼내는 도구. MoveNext(), Current, Dispose()
MoveNext()멈춘 곳부터 다음 yield return 까지 실행하고, 값이 있으면 true
using (…)다 쓰면 Dispose() 를 불러 정리. foreach 도 몰래 해 준다

1편에서 배열과 List 를 IEnumerable 로 받았던 이유가 여기서 이어집니다. yield 를 쓰는 함수도 IEnumerable 이라, 같은 foreach 로 돌 수 있습니다.

STEP 3

끝없는 목록

Strategy_06_IEnumarable.cs수업 자료 3단계19–40줄
private IEnumerable<int> InfinityIncrement()
{
    int current = 0;

    while (true)
    {
        yield return current;
        current++;
    }
}

public void Run()
{
    foreach (int value in InfinityIncrement())
    {
        Program.WriteLine(value);

        if (value > 15)
        {
            break;
        }
    }

while (true) 안에서 yield return 을 하는 함수는 끝나지 않습니다. 그래도 프로그램은 멈추지 않습니다. 값은 달라고 할 때 하나씩만 만들어지기 때문입니다(지연 실행). 받는 쪽이 15 를 넘었을 때 break 하면, 함수는 그 자리에 멈춘 채 버려집니다.

실행 결과게임 화면의 출력 창
0
1
2
…
15
16
STEP 4

yield break 와 finally

Strategy_06_IEnumarable.cs수업 자료 4단계30–55줄
private IEnumerable<string> FinallyEffect(string first, string last, int min, int max, int endCondition)
{
    try
    {
        Program.WriteLine(first);

        for (int i = min; i <= max; i++)
        {
            Program.WriteLine("Start of loop");
            yield return i.ToString();

            if (i == endCondition)
            {
                yield break;
            }

            Program.WriteLine("End of loop");
        }

        Program.WriteLine(last);
    }
    finally
    {
        Program.WriteLine("Finally block");
    }
}
Strategy_06_IEnumarable.cs수업 자료 4단계57–70줄
public void Run()
{
    foreach (string value in FinallyEffect("시작", "끝", 0, 4, 5))
    {
        Program.WriteLine("value: {0}", value);
    }

    Program.WriteLine();

    foreach (string value in FinallyEffect("시작", "끝", 0, 5, 2))
    {
        Program.WriteLine("value: {0}", value);
    }
}
실행 결과게임 화면의 출력 창
시작
Start of loop
value: 0
End of loop
Start of loop
value: 1
End of loop
…
Start of loop
value: 4
End of loop
끝
Finally block

시작
Start of loop
value: 0
End of loop
Start of loop
value: 1
End of loop
Start of loop
value: 2
Finally block

출력 순서를 보면 실행이 두 함수 사이를 오가는 것이 보입니다. 함수가 "Start of loop" 를 찍고 값을 내놓으면, 받는 쪽이 "value: 0" 을 찍고, 다시 함수로 돌아와 "End of loop" 를 찍습니다.

첫 번째 foreach

끝까지 돈 경우

for 를 다 돌고 "끝" 을 찍은 뒤 finally 가 실행됩니다.

두 번째 foreach

yield break

i 가 2 일 때 목록을 그 자리에서 끝냅니다. 그 아래 "End of loop" 와 "끝" 은 건너뛰지만 finally 는 실행됩니다.

3단계와 같은 경우

받는 쪽이 break

foreach 가 Dispose 를 불러 주기 때문에 이때도 finally 가 실행됩니다. 2단계의 using 이 하는 일입니다.

STEP 5

게임 UI 에서 쓰는 yield

Strategy_06_IEnumarable.cs수업 자료 5단계57–73줄
public void Run()
{
    Transform parent = GameObject.FindObjectOfType<Canvas>().transform;
    UISystemMenu menuWindow = UIWindow<UISystemMenuElement>.Create<UISystemMenu>(parent);

    for (int i = 0; i < 3; ++i)
        menuWindow.AddElement(0);

    IEnumerable<UISystemMenuElement> elements = menuWindow.GetElements(0);

    int cnt = 1;
    foreach (var element in menuWindow.GetElements(0))
    {
        element.SetMenuText("Menu" + cnt);
        cnt++;
    }
}

메뉴 창의 요소 목록을 꺼내는 GetElements(0) 도 yield 로 만든 IEnumerable 입니다. 창 안쪽이 리스트로 되어 있든 사전으로 되어 있든, 쓰는 쪽은 foreach 하나로 돕니다. 이 UI 창 구조는 뒤의 UI 편에서 자세히 봅니다.

RETRO

돌아보며

이번 편에서 얻은 것

  • yield return 은 값을 하나 내놓고 멈췄다가 다음 요청 때 이어서 실행한다
  • foreach 는 GetEnumerator, MoveNext, Current, Dispose 를 줄여 쓴 것이다
  • 목록을 끝까지 돌지 않아도 finally 는 실행된다

다음에 풀 질문

  • 멈췄다 이어 가는 성질로 "3초 기다렸다가 실행" 을 만들 수 있을까 (코루틴)
  • Unity 는 IEnumerator 를 언제 MoveNext 하는가

정리

  1. yield return 은 값을 하나씩 내놓습니다. 함수는 멈췄다가, 다음 값을 달라고 할 때 이어서 실행합니다.
  2. foreach 는 MoveNext 와 Current 입니다. GetEnumerator 로 도구를 받고, MoveNext 가 false 일 때까지 Current 를 읽습니다.
  3. 값은 필요할 때 만들어집니다. 그래서 끝없는 목록도 만들 수 있고, 중간에 멈추면 finally 로 정리됩니다.

다음 편에서는 같은 IEnumerator 로 Unity 가 "기다렸다가 이어 가기" 를 하는 코루틴과, 진짜 동시에 도는 스레드를 비교합니다.

← 개발 노트 목록