# Awaitable을 통한 비동기 프로그래밍 소개

> Understand the key features of Unity's Awaitable and how it compares to both .NET Task and iterator-based coroutines.

[`Awaitable`](/engine/6000.0/script-reference/unity-engine/awaitable.md) 클래스는 대기가 가능하며 C# 비동기 프로그래밍 모델에서 비동기 반환 유형으로 사용할 수 있는 커스텀 Unity 유형입니다. Unity의 대부분의 비동기 API는 다음을 포함하는 `async` 및 `await` 패턴을 지원합니다.

* Unity 코루틴: [`NextFrameAsync`](/engine/6000.0/script-reference/unity-engine/awaitable/next-frame-async.md), [`WaitForSecondsAsync`](/engine/6000.0/script-reference/unity-engine/awaitable/wait-for-seconds-async.md), [`EndOfFrameAsync`](/engine/6000.0/script-reference/unity-engine/awaitable/end-of-frame-async.md), [`FixedUpdateAsync`](/engine/6000.0/script-reference/unity-engine/awaitable/fixed-update-async.md)
* [백그라운드 스레드](/engine/6000.0/script-reference/unity-engine/awaitable/background-thread-async.md) 또는 [메인 스레드](/engine/6000.0/script-reference/unity-engine/awaitable/main-thread-async.md)로 전환
* [`AsyncOperation`](/engine/6000.0/script-reference/unity-engine/async-operation.md)에서 상속하는 모든 유형
* [Unity 이벤트](/engine/6000.0/manual/scripting/object-oriented-development/event-handling/unity-events.md)
* [비동기 GPU 리드백](/engine/6000.0/script-reference/unity-engine-rendering/async-gpu-readback.md)

다음과 같이 자체 코드에서 `Awaitable` 클래스를 `await` 연산자와 함께 사용하거나 `async` 반환 유형으로 사용할 수 있습니다.

```lang-csharp
async Awaitable<List<Achievement>> GetAchievementsAsync()
{
    var apiResult = await SomeMethodReturningATask(); // or any await-compatible type
    List<Achievement> achievements = JsonConvert.DeserializeObject<List<Achievement>>(apiResult);
    return achievements;
}

async Awaitable ShowAchievementsView()
{
    ShowLoadingOverlay();
    List<Achievement> achievements = await GetAchievementsAsync();
    HideLoadingOverlay();
    ShowAchivementsList(achievements);
}
```

## Awaitable과 .NET Task 비교##awaitable-compared-to-.net-task

`Awaitable`는 Unity 프로젝트의 비동기 코드에 대해서 .NET [`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task?view=net-8.0)보다 효율적인 대안을 제공하도록 설계되었습니다. `Awaitable`의 효율성은 `Task`에 비해 몇 가지 중요한 한계가 있습니다.

가장 중요한 제한 사항은 할당을 제한하기 위해 `Awaitable` 인스턴스를 풀링한다는 것입니다. 다음 예시를 참고하십시오.

```lang-csharp
class SomeMonoBehaviorWithAwaitable : MonoBehavior
{
    public async void Start()
    {
        while(true)
        {
            // do some work on each frame
            await Awaitable.NextFrameAsync();
        }
    }
}
```

풀링이 없다면 이 예시에서 [`MonoBehavior`](/engine/6000.0/manual/scripting/get-started/fundamental-unity-types/class-mono-behaviour.md)의 각 인스턴스는 매 프레임마다 `Awaitable` 오브젝트를 할당하게 되므로 [가비지 컬렉터 작업 부하](/engine/6000.0/manual/analysis/performance-memory-overview/managed-memory/garbage-collector.md)가 증가하고 성능이 저하됩니다. Unity는 이를 완화하기 위해 `Awaitable` 오브젝트가 대기 처리된 후 내부 `Awaitable` 풀로 반환합니다.

**중요:** `Awaitable` 인스턴스를 풀링한다는 것은 `Awaitable` 인스턴스에서 두 번 이상 `await`를 수행하는 것은 안전하지 않다는 의미입니다. 그렇게 하면 예외나 교착 상태와 같이 정의되지 않은 행동이 발생할 수 있습니다.

## Awaitable과 .NET ValueTask 비교##awaitable-compared-to-.net-valuetask

.NET [`ValueTask<TResult>`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=net-8.0)는 `Awaitable`의 몇 가지 주요 이점과 한계를 제공합니다. `ValueTask`는 일반적으로 대부분의 경우 동기식으로 완료될 것으로 예상되는 비동기식 워크로드에 사용하는 것이 좋습니다. 자세한 내용은 [ValueTask의 이유, 내용, 시점 이해하기](https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/)를 참고하십시오.

## Awaitable, Task, ValueTask 요약##awaitable-task-and-valuetask-summary

아래의 표는 Unity의 `Awaitable` 클래스와 .NET `Task` 및 `ValueTask` 간의 기능을 비교한 내용을 요약한 것입니다.

| **기능**                               | `Task`                                                                                                                                           | `ValueTask`                                                                                                                           | `UnityEngine.Awaitable`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **필수 할당**                            | **많음**.모든 호출에 `Task` 반환 메서드를 할당하여 메모리 사용량과 가비지 컬렉터 작업량을 증가시킵니다.                                                                                  | **필요한 만큼**.풀링으로 최적화할 수 있습니다.                                                                                                          | **필요에 따라 최소한**.기본적으로 `Awaitable` 인스턴스가 풀링되기 때문에 `Awaitable` 반환 메서드를 호출하는 것은 일반적으로 메모리를 할당하지 않습니다.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| **여러 번 안전하게 대기 가능**                  | **예**.                                                                                                                                           | **아니오**.`ValueTask.AsTask`를 사용해 `Task`로 전환해야 합니다.                                                                                     | **아니오**.커스텀 `AsTask` 확장 메서드가 있는 `Task`로 전환해야 합니다. 코드 예시 레퍼런스의 [동일한 메서드에서 여러 번 대기](/engine/6000.0/manual/scripting/optimization/async-await-support/async-awaitable-examples.md#await-multiple-times)를 참조하십시오.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| **비동기식으로 실행되는 지속**                   | **예**.기본적으로 동기화 컨텍스트를 사용하며, 그렇지 않으면 `ThreadPool`을 사용합니다. 이는 코드가 다음 프레임 `Update`를 재개할 때까지 기다려야 하기 때문에 Unity의 메인 스레드를 완료할 때 지연 시간이 증가합니다.          | **예**.대기 중인 작업들이 동기식으로 완료되는 경우에 최적화되었습니다. 작업들이 비동기식으로 완료될 경우 지속 동작은 `Task`와 동일합니다.                                                    | **아니오**.지속은 완료가 트리거되면 동기식으로 실행됩니다. 즉, 완료가 트리거된 해당 프레임에서 코드가 즉시 재개됩니다. 자세한 내용은 [Awaitable 완료 및 지속](/engine/6000.0/manual/scripting/optimization/async-await-support/async-awaitable-continuations.md)에서 확인하십시오.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| **코드로 완료를 트리거할 수 있음**                | **예**.[`TaskCompletionSource`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource?view=net-8.0)를 사용하면 됩니다. | 대부분 동기식으로 완료되는 작업의 일반적인 사용 사례에는 적용되지 않습니다.                                                                                            | **예**.[`AwaitableCompletionSource`](/engine/6000.0/script-reference/unity-engine/awaitable-completion-source.md)를 사용하면 됩니다.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| **값 반환 가능**                          | **예**.[`Task<TResult>`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1?view=net-8.0)를 사용하면 됩니다.                      | **예**.[`ValueTask<TResult>`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask-1?view=net-8.0)를 사용하면 됩니다. | **예**.[`UnityEngine.Awaitable<T>`](/engine/6000.0/script-reference/unity-engine/awaitable-1.md)를 사용하면 됩니다.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| **`WaitAll` 및 `WaitAny`에 대한 빌트인 지원** | **예**.                                                                                                                                           | **아니오**.`ValueTask.AsTask`를 통해 `Task`로 전환해야 합니다.                                                                                      | **아니오**.커스텀 `AsTask` 확장 메서드를 통해 `Task`로 전환해야 합니다. 코드 예시 레퍼런스에서 [.NET 작업에서 Awatiable 래핑](/engine/6000.0/manual/scripting/optimization/async-await-support/async-awaitable-examples.md#awaitable-as-task)을 참조하십시오.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| **Unity 스레드 및 업데이트 루프 인식 실행 작업 예약**  | **아니오**.                                                                                                                                         | **아니오**.                                                                                                                              | **예**.[`Awaitable.BackgroundThreadAsync`](/engine/6000.0/script-reference/unity-engine/awaitable/background-thread-async.md) 및 [`Awaitable.MainThreadAsync`](/engine/6000.0/script-reference/unity-engine/awaitable/main-thread-async.md)를 통해 `Awaitable`가 재개되는 스레드를 지정할 수 있습니다. [`Awaitable.NextFrameAsync`](/engine/6000.0/script-reference/unity-engine/awaitable/next-frame-async.md) 및 [`Awaitable.FixedUpdateAsync`](/engine/6000.0/script-reference/unity-engine/awaitable/fixed-update-async.md)를 통해 `Update` 또는 `FixedUpdate` 루프를 기준으로 작업을 예약할 수도 있습니다. 자세한 내용은 [Awaitable 완료 및 지속](/engine/6000.0/manual/scripting/optimization/async-await-support/async-awaitable-continuations.md)을 참조하십시오. |

## Task 또는 ValueTask가 아닌 Awaitable을 사용해야 하는 경우##when-to-use-awaitable-over-task-or-valuetask

API 선택 기준은 비동기 코드의 성능 프로파일에 따라 달라지지만 일반적으로는 다음과 같습니다.

* 여러 번 또는 여러 소비자로에서 동시에 대기해야 할 경우에는 `Task`만 사용할 수 있습니다.

* 대부분의 경우 동기적으로 완료되는 높은 처리량의 비동기식 코드가 있을 경우 `ValueTask`를 사용하는 것이 좋습니다.

* `Awaitable`은 다음과 같은 경우에 적합합니다.

  * 메서드를 여러 번 기다릴 필요가 없으며 대부분 비동기식으로 완료될 것으로 예상되는 경우
  * 비동기 작업이 메인 스레드와 [Update](/engine/6000.0/manual/scripting/object-oriented-development/managing-time-and-frame-rate/time-per-frame-updates.md) 및 [FixedUpdate](/engine/6000.0/manual/scripting/object-oriented-development/managing-time-and-frame-rate/fixed-updates.md) 루프와 같은 Unity 관련 개념을 기본적으로 지원하기를 원합니다.

## Awaitable과 반복자 기반 코루틴 비교##awaitable-compared-to-iterator-based-coroutines

`Awaitable` 코루틴은 일반적으로 반복자 기반 코루틴보다 더 효율적이며, 특히 [`WaitForFixedUpdate`](/engine/6000.0/script-reference/unity-engine/wait-for-fixed-update.md)와 같이 반복자가 null이 아닌 값을 반환하는 경우에 더욱 효율적입니다.

그러나 여러 개의 `Awaitable` 코루틴을 동시에 실행하면 성능상의 이점이 줄어듭니다. 예를 들어 이전의 코드 예시에서 `while` 루프에서 [`Awaitable.NextFrameAsync`](/engine/6000.0/script-reference/unity-engine/awaitable/next-frame-async.md)를 기다리는 MonoBehavior는 대규모 프로젝트에서 모든 게임 오브젝트에 연결될 경우 성능 문제를 일으킬 가능성이 높습니다.

## 추가 리소스##additional-resources

* [Awaitable 작업 완료 및 지속](/engine/6000.0/manual/scripting/optimization/async-await-support/async-awaitable-continuations.md)
* [Awaitable 코드 예시 레퍼런스](/engine/6000.0/manual/scripting/optimization/async-await-support/async-awaitable-examples.md)
