C# 윈도우폼 비동기 이벤트 및 메서드 사용하기

동키·2024년 11월 13일

C#

목록 보기
3/12

REST API 서버로부터 데이터을 비동기 방식으로 응답받아 화면 출력을 할때 메서드를 구현하면서 async, await, Task 등을 많이 사용했는데 일단 구현은 했어도 이해가 안되는 부분이 많았어서 정리

1. async

async는 비동기 메서드를 정의할 때 사용됩니다. 메서드에 async 키워드를 붙이면, 그 메서드는 비동기 방식으로 실행될 수 있습니다.

  • 비동기 메서드는 시간이 오래 걸릴 수 있는 작업(예: 데이터베이스 쿼리, 파일 읽기, HTTP 요청 등)을 처리할 때 메인 쓰레드가 그 작업이 끝날 때까지 멈추지 않도록 합니다.
  • async를 붙이면 메서드가 비동기 실행을 준비하게 되고, 그 메서드는 await를 사용할 수 있게 됩니다.
public async Task MyAsyncMethod()
{
    // 비동기 작업을 수행할 수 있는 메서드
}

2. await

await는 비동기 작업의 완료를 기다리는 역할을 합니다. await가 붙은 작업이 완료될 때까지 기다리지만, 중요한 점은 이 대기 동안 다른 작업들을 할 수 있도록 메인 쓰레드를 블로킹(blocking)하지 않는다는 것입니다.

  • 예를 들어, HTTP 요청을 보내고 응답을 기다릴 때, await는 그 작업이 끝나길 기다리지만 프로그램이 멈추지 않도록 해 줍니다. 이로 인해 사용자 인터페이스(UI)가 멈추지 않고 사용자가 다른 작업을 할 수 있게 됩니다.
HttpResponseMessage response = await client.GetAsync("http://example.com");

위 코드에서 await는 client.GetAsync() 메서드의 완료를 기다리면서 다른 작업을 할 수 있게 합니다.

3. Task

Task는 비동기 작업을 표현하는 객체입니다. 비동기 메서드에서 어떤 작업을 수행하고 그 결과를 반환할 때 사용합니다.

  • 비동기 작업이 완료될 때까지의 약속(Promise)을 나타낸다고 생각할 수 있습니다.

  • 비동기 메서드에서 반환 값이 없다면 Task를, 반환 값이 있다면 Task<T>를 사용합니다. 예를 들어, Task<int>는 비동기 메서드가 정수(int) 값을 반환할 것이라는 의미입니다.

private async Task LoadBoardList()
{
    HttpResponseMessage response = await client.GetAsync("http://example.com/api/boards");
    if (response.IsSuccessStatusCode)
    {
        string result = await response.Content.ReadAsStringAsync();
        var boards = JsonConvert.DeserializeObject<List<BoardDto>>(result);
        dataGridViewBoard.DataSource = boards;
    }
    else
    {
        MessageBox.Show("게시글 목록을 불러오는 데 실패했습니다.");
    }
}
  • async 키워드로 메서드를 선언
  • await client.GetAsync(...) : 서버에 요청을 보낸 뒤 응답 대기. 이 동안 다른 작업 수행할 수 있으며, 응답이 올 때까지 프로그랩은 멈추지 않습니다.
  • Task 반환 : 메서드의 반환 타입을 Task로 정의하여 호출하는 측에서 작업이 완료될 때까지 기다릴 수 있게 합니다.

이렇게 async, await, Task를 사용하면 비동기 작업을 할 때 프로그램이 멈추치 않고 자연스럽게 동작하게 되어 사용자 경험이 좋아지고, 성능이 개선되는 효과를 볼 수 있습니다.

4. 비동기 메서드와 Task의 사용

메서드의 반환 형태에 따른 구분

  • 비동기 작업을 수행하면서 아무 값도 반환하지 않는 메서드는 반환 타입이 Task가 아니라 void가 될 수 있습니다.
  • 비동기 작업을 수행하고 작업이 끝났음을 알리는 약속을 반환할 때는 Task를 반환해야 합니다.

즉, 비동기 메서드에 async를 붙이는 것은 해당 메서드를 비동기로 실행할 수 있다는 의미입니다. void, Task, Task<T>를 선택하는 것은 메서드가 반환하는 정보가 무엇인지를 나타냅니다.

예를 들어 LoadBoardList()메서드를 async Task로 구현하여 버튼을 클릭시 게시글 목록을 조회하는 기능을 구현한다면

private async Task LoadBoardList()
{
    HttpResponseMessage response = await client.GetAsync("http://example.com/api/boards");
    if (response.IsSuccessStatusCode)
    {
        string result = await response.Content.ReadAsStringAsync();
        var boards = JsonConvert.DeserializeObject<List<BoardDto>>(result);
        dataGridViewBoard.DataSource = boards;
    }
    else
    {
        MessageBox.Show("게시글 목록을 불러오는 데 실패했습니다.");
    }
}

private async void btnSearch_Click(object sender, EventArgs e)
{
    await LoadBoardList();
}

LoadBoardList()메서드는 반환타입이 Task입니다. Task를 반환하는 이유는 호출자에게 이 메서드가 비동기 작업을 진행하고 있고, 완료될 때까지 기다릴 수 있다는 약속을 제공하기 위해서입니다.

LoadBoardList() 메서드를 호출하는 쪽에서는 이 메서드가 완료되기를 기다려야 할 수 있습니다. 이때 Task를 반환하면 호출자는 await를 사용하여 해당 메서드가 끝날 때까지 대기할 수 있습니다.

  • btnSearch_Click 이벤트 핸들러가 LoadBoardList()를 호출할 때 await을 사용할 수 있는 이유는 LoadBoardList()가 Task를 반환하기 때문입니다.
  • 만약 LoadBoardList()가 void를 반환한다면, 호출자는 해당 작업이 끝났는지 여부를 알 방법이 없으며 await으로 기다릴 수 없습니다.
  • async void는 btnSearch_Click과 같은 이벤트 핸들러에서 주로 사용됩니다. 이벤트 핸들러는 일반적으로 반환 값을 필요로 하지 않으며, 이벤트가 발생할 때마다 실행되기 때문에 void로 선언해도 사용하는데 문제가 없습니다.
  • 하지만 일반적인 메서드에서는 void대신 Task를 사용해야, 호출자에게 작업이 완료될 때까지 기다릴 수 있는 방법을 제공할 수 있습니다.

요약

  • 비동기 메서드는 async 키워드를 붙이며 반환 타입은 실행 결과에 따른 코드를 구현하기 위해서는 Task를 사용 필요 없다면 void
  • void: 이벤트 핸들러처럼 반환 값을 필요로 하지 않는 경우 사용
  • Task: 호출자가 비동기 작업의 완료를 기다려야 하는 경우 사용
  • Task<T>: 비동기 작업이 완료된 후 값을 반환해야 할 때 사용

추가

일반적인 비동기 메서드에서는 void 대신 Task사용이 권장됩니다. Task를 반환하지 않고 void로 처리할 경우, 예외 처리가 더 어려워질 수 있고 호출자가 작업의 완료를 알 수 있는 방법이 없기 때문에 비동기 작업에 대한 흐름 관리가 불가능해질 수 있습니다.

profile
오키동키

0개의 댓글