← SEARCHABLE ARCHIVE
C# · 2026-08-07

C# 기초부터 마스터까지 — 8화

async와 await로 비동기 작업 구성하기

INTERACTIVE READING MODE →

본 블로그 작성 글들은, 강의를 목표로 하고 제작되었습니다. 또한 작성된 글의 정보도 틀렸을 수 있으니, 주의해주세요.

네트워크나 파일 작업을 기다리는 동안 스레드를 막으면 화면과 다른 작업도 멈출 수 있다. async와 await는 기다림이 있는 작업을 이어서 실행할 수 있게 표현한다.

Task 반환하기

async Task<PlayerData> LoadPlayerAsync(
    string url,
    CancellationToken cancellationToken)
{
    using HttpResponseMessage response =
        await httpClient.GetAsync(url, cancellationToken);

    response.EnsureSuccessStatusCode();

    PlayerData? data = await response.Content
        .ReadFromJsonAsync<PlayerData>(cancellationToken);

    return data ?? throw new InvalidDataException("Player data is empty");
}

await를 만나면 작업이 끝날 때까지 현재 스레드를 계속 붙잡고 있는 대신 나머지 코드를 나중에 이어갈 수 있다. async 메서드는 호출자가 완료와 실패를 관찰할 수 있도록 Task를 반환하는 것이 기본이다.

취소 전달하기

using var source = new CancellationTokenSource(TimeSpan.FromSeconds(5));

try
{
    PlayerData data = await LoadPlayerAsync(url, source.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Loading canceled");
}

취소는 스레드를 강제로 종료하는 기능이 아니다. 작업이 토큰을 확인하고 안전한 지점에서 중단하도록 요청하는 계약이다.

여러 작업 기다리기

Task<ItemData> itemTask = LoadItemsAsync(token);
Task<QuestData> questTask = LoadQuestsAsync(token);

await Task.WhenAll(itemTask, questTask);

ItemData items = await itemTask;
QuestData quests = await questTask;

서로 의존하지 않는 대기 작업은 먼저 시작한 뒤 함께 기다릴 수 있다. CPU 계산을 단순히 async로 표시한다고 자동으로 병렬화되지는 않는다.

피해야 할 흐름

Task의 .Result나 .Wait()로 동기 대기하면 스레드를 막거나 실행 환경에 따라 교착 상태를 만들 수 있다. 호출 경로를 가능한 끝까지 async로 이어가는 편이 좋다.

async void는 완료와 예외를 호출자가 기다릴 수 없으므로 이벤트 핸들러 외에는 피한다. 비동기 코드의 핵심은 키워드를 붙이는 것이 아니라 완료, 실패, 취소의 흐름을 모두 호출자에게 전달하는 것이다.