C#에서 async와 await 키워드는 비동기(Asynchronous) 프로그래밍을 구현할 때 사용하는 핵심 요소입니다. 이 두 키워드를 활용하면 시간이 오래 걸리는 작업을 수행하면서도 애플리케이션이 응답성을 유지하도록 만들 수 있습니다.
동기 방식의 문제점
GUI(그래픽 사용자 인터페이스)가 있는 애플리케이션은 내부적으로 메시지 큐를 확인하고, 처리되지 않은 작업이 있으면 이를 꺼내어 먼저 실행하는 방식으로 동작합니다. 코드가 동기식(synchronously)으로 실행되면, 현재 진행 중인 작업이 완료될 때까지 다른 모든 작업이 대기하게 됩니다.
만약 작업 처리에 예상보다 많은 시간이 걸리면, 애플리케이션은 메시지에 반응하지 못하고 '응답 없음(Not Responding)' 상태로 보일 수 있습니다. 이는 사용자 경험을 크게 저해하는 심각한 문제입니다.
동기 방식 예제 코드
private void OnRequestDownload(object sender, RoutedEventArgs e) {
var req = HttpWebRequest.Create(_requestedUri);
var res = req.GetResponse();
}위 코드에서 GetResponse() 호출은 네트워크 응답이 올 때까지 현재 스레드를 차단(blocking)합니다. 그동안 UI 스레드도 멈추기 때문에 화면이 얼어붙는 현상이 발생합니다.
async와 await로 해결하기
이러한 문제를 해결하려면 async와 await 키워드를 사용하면 됩니다. 메서드 선언부에 async 한정자를 붙이고, 시간이 걸리는 작업 호출 앞에 await 키워드를 추가합니다.
비동기 방식 예제 코드
private async void OnRequestDownload(object sender, RoutedEventArgs e) {
var req = HttpWebRequest.Create(_requestedUri);
var res = await req.GetResponseAsync();
}동작 원리
await 키워드를 만나면 컴파일러는 해당 지점에서 메서드의 실행을 일시 중단하고, UI 스레드를 즉시 반환하여 다른 메시지(클릭, 입력 등)를 계속 처리할 수 있게 합니다. 백그라운드에서 네트워크 요청이 완료되면, 중단된 지점부터 나머지 코드가 자동으로 이어서 실행됩니다.
주목할 점은 HTTP 요청에 대해 GetResponse() 대신 비동기 버전인 GetResponseAsync()를 사용했다는 것입니다. .NET에서는 대부분의 I/O 관련 메서드에 'Async' 접미사가 붙은 비동기 버전이 제공되므로, 이를 함께 활용하는 것이 좋습니다.
핵심 정리
- async: 메서드 내부에서 await를 사용할 수 있도록 하는 한정자입니다.
- await: 비동기 작업이 완료될 때까지 기다리되, 스레드를 차단하지 않고 제어권을 반환합니다.
- UI 애플리케이션에서 화면 멈춤 현상을 방지하고 응답성을 높이는 데 필수적입니다.