카테고리 없음

🌟 C# async / await 비동기 프로그래밍😋 🌟

minsoungdev 2026. 8. 17. 10:54

안녕하세요. 박민성입니다. ☺️

 

오늘은 C#의 async / await 비동기 프로그래밍에 대해서 글을 쓰고자 합니다. 🤔

학교에서 배우는 네트워크 시간이나 최근 엔진 수업때 사용하여서 공부하게 되었습니다.

워낙 주제가 방대하고 알아야 할 것들이 많아서 시간이 오래 걸릴 것 같아서 사실 살짝 미룬 감이 있긴 합니다.

 

1. async / await란?

async / await는 오래 걸리는 작업이 끝날 때까지 프로그램 전체를 멈춰 세우지 않고 기다리는 동안 다른 작업을 할 수 있도록 비동기 코드를 작성하는 C# 기능입니다.

private async Task TestAsync()
{
    Debug.Log("시작");

    await Task.Delay(3000);

    Debug.Log("3초 후");
}

여기서 await Task.Delay(3000); 을 만났다고 해서 프로그램 전체를 3초 동안 멈추게 하는 것이 아닙니다.

 

이 메서드의 실행을 잠시 중단하고 호출한 쪽으로 제어권을 돌려준 다음에 기다리던 작업이 완료되면 그다음 코드부터 이어서 실행합니다. Microsoft에서 await이 비동기 작업이 끝날 때까지 현재 async 메서드의 실행을 일시 중단한다고 설명하고 있습니다.

 

간단하게 기다려야 하는 작업 때문에 현재 흐름 전체를 붙잡아 두지 않는 것이라고 이해하면 될 것 같습니다.

 

2. 동기와 비동기의 차이

먼저 이걸 이해해야지 async / await가 왜 필요한지 알게 됩니다.

동기 방식

DoA();
DoB();
DoC();

기본적으로 A, B, C 순서대로 A실행되고 A완료되면 B실행되고 C까지 이렇게 작동됩니다.

B가 5초 걸리면 C는 5초 동안 기다려야 하는 한 타임에 한 동작들 실행하는 것이 동기 방식입니다.

비동기 방식

어떤 작업이 기다림을 필요로 한다면 

A 실행 -> 비동기 작업 시작 -> 기다리는 동안 제어권 반환 -> 작업 완료 -> 이어서 실행처럼 작동할 수 있습니다.

A가 실행 중에 B와 C가 동시에 실행할 수 있게 합니다.

 

특히 네트워크 통신, 파일 읽기, 서버 요청, 일정 시간 기다리기처럼 결과가 올 때까지 기다리는 작업에서 유용합니다.

Microsoft의 TAP(Task-based Asynchronous Pattern)도 이런 비동기 작업을 Task를 중심으로 표현하고 있습니다.

 

 

3. Task란?

async / await를 이해하려면 Task를 먼저 알아야 합니다.

Task는 간단하게 현재 진행 중이거나 앞으로 완료될 작업을 나타내는 객체를 나타냅니다.

Task task = Task.Delay(3000);

task에는 3초라는 값이 저장되는 게 아닙니다.

개념적으로 task는 "3초를 기다리는 작업"을 담고 있는 것입니다.

await task;

이 코드는 이 작업이 끝날 때까지 현재 async 메서드를 잠시 중단하고 완료되면 이어서 실행하라는 것을 나타냅니다.

 

4. async의 역할

메서드에 async를 붙이면 해당 메서드 안에서 await를 사용할 수가 있습니다.

없다면 await을 사용할 수 없습니다.

 

async의 핵심 역할은 메서드를 비동기 메서드로 선언하고 그 안에서 await를 사용할 수 있게 하는 것입니다. 
async 메서드는 첫 번째 완료되지 않은 await를 만날 때까지 동기적으로 실행되는 것입니다.

 

5. await의 역할

이 글의 핵심이 되는 내용인 await입니다.

await SomeTask();

await은 작업이 끝날 때까지 현재 async 메서드의 진행을 잠시 멈추고 완료되면 그다음부터 다시 실행하게 됩니다.

private async Task TestAsync()
{
    Debug.Log("1");

    await Task.Delay(2000);

    Debug.Log("2");
}

이 코드는 1을 출력 시키고 ->  2초 작업을 기다립니다. -> TestAsync의 실행을 중단 -> 다른 코드 실행 가능 -> 2초가 완료되면 ->

TestAsync 다시 이어집니다. -> 2를 출력시킵니다.

 

6. 기본 사용법

private async Task WaitAsync()
{
    await Task.Delay(1000);

    Debug.Log("완료");
}

호출하는 쪽도 async이라면

await WaitAsync();

await을 사용하여 WaitAsync()이 끝날 때까지 기다릴 수 있습니다. 이것이 위에 설명했던 다른 코드를 실행 가능한 시점인 것입니다.

private async Task StartGameAsync()
{
    Debug.Log("로딩 시작");

    await LoadDataAsync();

    Debug.Log("게임 시작");
}
LoadDataAsync()가 완료된 이후에 "게임 시작"이 실행되는 구조입니다.
 
Task playerTask = LoadPlayerAsync();
Task itemTask = LoadItemsAsync();

await Task.WhenAll(playerTask, itemTask);

이렇게 여러 비동기 작업도 다룰 수 있습니다. 두 작업이 모두 완료될 때까지 기다릴 수 있숩니다.

이런 식으로 서로 독립적인 비동기 작업을 조합할 수 있다는 것도 Task 방식의 큰 장점이라고 Microsoft에서 설명하고 있습니다.

 

7. Task<T>로 결과값 받기

작업이 끝난 뒤 값을 반환하고 싶다면 Task<T>를 사용할 수 있습니다!

private int GetLevel()
{
    return 10;
}

이렇게 일반 메서드는 10을 반환할 때 메서드 반환형에 int를 적습니다.

private async Task<int> GetLevelAsync()
{
    await Task.Delay(1000);

    return 10;
}

int level = await GetLevelAsync(); // 사용

비동기 메서드는 Task에 있는 제네릭 T에 반환형을 적어서 그 자료형을 반환 시킵니다.

 

Task는 완료 여부를 가진 작업이라고 하면 Task<T>는 완료되면 T 결과까지 돌려주는 작업이라고 생각하시면 됩니다.

Task와 Task<TResult>는 C# async 메서드의 대표적인 반환형입니다.

 

8. async가 새로운 스레드를 만드는 걸까요? 🌟

아닙니다.

async Task TestAsync()

이렇게 했다고 해서 메인 스레드에서 새로운 스레드가 자동 생성되는 것은 아닙니다.

 

async / await의 핵심은 스레드 생성이 아니라 비동기적인 제어 흐름입니다.

 

CPU 작업을 실제 백그라운드 스레드로 옮기는 경우에는 Task.Run 같은 별도의 방법이 사용될 수 있습니다.

Microsoft도 I/O-bound와 CPU-bound 작업의 비동기 처리 방식이 다르다고 설명하고 있습니다.

 

async = 멀티스레딩이라고 외우시면 안됩니다.

 

9. Unity Coroutine과 뭐가 다를까요?

private IEnumerator Wait() // 유니티 코루틴
{
    yield return new WaitForSeconds(1f);

    Debug.Log("완료");
}

async Task WaitAsync() // async / await
{
    await Task.Delay(1000);

    Debug.Log("완료");
}

두 메서드는 작업 중간에 기다리고 나중에 이어서 실행이라는 작업이 얼핏 느낌이 비슷해 보입니다.

하지만 서로 다른 시스템으로서 코루틴은 유니티의 IEnumerator/yield 기반 실행 시스템이고

async / await는 C# 언어의 비동기 프로그래밍 모델이입니다.

 

10. 최신 Unity에 Awaitable

현재 Unity는 자체적으로

Awaitable

타입을 제공합니다.

private async Awaitable WaitAsync()
{
    await Awaitable.WaitForSecondsAsync(1);

    Debug.Log("완료");
}

Unity 공식 문서는 Awaitable을 C#의 async/await 모델에서 기다릴 수 있고 async 반환형으로도 쓸 수 있는 Unity 전용 타입으로 설명하며 Unity 비동기 코드에서는 .NET Task보다 Awaitable이 선호되는 경우가 있다고 안내합니다.

Coroutine, Task, Awaitable 적절하게 사용하는 것이 중요합니다.

 

이상으로 오늘은 C#에 async / await 비동기 프로그래밍에 대해서 알아보았습니다.

async / await에 관련해서 Unity까지 이어지면 내용이 제 기준으로 엄청 방대해지기 때문에 다 다루지는 못했습니다..

뭔가 어설프게 끝난 것도 제가 이해한 것까지만 다루어서 애매하게 끊긴 느낌도 있습니다..

 

오늘은 기숙사에 들어가는 날입니다.

내일은 개학입니다.

블로그를 매일 쓰는 것은 방학때 게으르게 공부하지 않으려고 했던 저와의 약속 같은 것입니다.

개학하고 나서는 매일 블로그를 쓰는 것은 불가능에 가깝겠지만 중요한 일이 일어나거나 전하고자 하는 내용이 있다면 틈틈이 글을 쓰고자 합니다.

 

지금까지 글을 봐주셔서 감사합니다. ☺️

 

오늘의 추천 노래는

015B - 이젠 안녕 입니다.