C#

async vs await – 비동기 코딩, 진짜 이해하고 쓰고 있나?

Juan_ 2025. 6. 7. 11:31
728x90

async vs await – 비동기 코딩, 진짜 이해하고 쓰고 있나?

처음 C#에서 비동기를 배울 때 이런 코드를 보게 된다.

public async Task<int> GetDataAsync()
{
    await Task.Delay(1000);
    return 42;
}

처음 보면 느낌이 좋다.

오호~ 뭔가 최신스럽고, 깔끔하고, 빨라 보인다.

근데 조금 지나면 이렇게 된다.

  • async void를 남발함
  • await을 안 쓰고 async만 붙임
  • Task를 안 기다리고 .Result를 씀
  • 데드락(deadlock)이 발생함
  • UI가 멈춘다

"async와 await이 뭐가 다른 건데?"

"비동기라고 해서 썼는데 더 느린데?"

이번 챕터에선 이 둘의 개념부터 실무 팁까지, 진짜로 정리해보자.


문제 상황: 비동기로 했는데 프로그램이 멈췄다?

public async Task<int> GetValueAsync()
{
    await Task.Delay(1000);
    return 123;
}

int result = GetValueAsync().Result; // ❌ 데드락 가능성 있음

분명 비동기로 했는데 Result를 호출하니까 프로그램이 멈췄다.

왜 그럴까?

async는 비동기 메서드를 정의하는 키워드이고,

await은 비동기 작업(Task)을 기다리는 키워드다.

하나는 선언이고, 하나는 실행이다.


async – "비동기 메서드야!"라고 선언하는 키워드

public async Task DoSomethingAsync()
{
    // 비동기 작업
}
  • async는 혼자선 아무 일도 하지 않음
  • 내부에 await가 없으면 경고 뜸
  • 반드시 Task, Task<T>, ValueTask 리턴해야 함
public async void DoSomething() {} // ❌ 비추천
public async Task DoSomething() {} // ✅

async void는 예외 처리가 어렵고, 테스트도 안 되므로 이벤트 핸들러 외엔 쓰지 말자.


await – "비동기 작업이 끝날 때까지 기다릴게요"

await Task.Delay(1000); // 1초 쉬기
  • await은 Task를 비동기적으로 기다린다
  • 기다리는 동안 다른 작업(예: UI 렌더링)을 계속함
  • await이 없으면 그냥 동기 함수처럼 실행됨
public async Task PrintLater()
{
    Console.WriteLine("1");
    await Task.Delay(1000);
    Console.WriteLine("2");
}

await이 있어야 진짜 비동기처럼 동작한다.

async만 붙이고 await 안 쓰면 아무 소용 없다.


실무 예시: 비동기 API 호출

public async Task<string> GetUserNameAsync()
{
    var response = await httpClient.GetAsync("https://example.com/user");
    var json = await response.Content.ReadAsStringAsync();
    return json;
}
  • await이 연속적으로 붙는 구조를 비동기 파이프라인이라 부름
  • ConfigureAwait(false)를 쓰면 스레드 컨텍스트를 안 따라가서 서버 환경에서 퍼포먼스 향상 가능

async/await 없이 비동기 호출하면?

var task = SomeAsyncMethod(); // 호출만 함
  • 그냥 Task 객체만 얻고 실행은 이미 시작됨
  • await하지 않으면 완료 여부, 결과, 예외 확인 불가
  • 무시하면 고스트 Task 발생 → 디버깅 지옥

언제 어떤 걸 써야 할까?

구분 async await
기능 비동기 메서드 선언 비동기 작업을 기다림
필수 조건 Task/Task 반환 Task 또는 awaitable 대상
단독 사용 가능? O (하지만 await 없으면 의미 없음) X (항상 async와 함께 써야 함)
주의할 점 async void 지양 Result, Wait() 사용 지양

마무리하며

비동기는 어렵지 않다. 다만 제대로 이해하지 않으면 오히려 더 위험하다.

  • async는 "비동기로 만들겠다"는 약속
  • await은 "비동기 작업이 끝날 때까지 기다리겠다"는 실행

"동기처럼 보이지만, 내부는 비동기"

이게 C# 비동기의 핵심이다.

그리고 절대 잊지 말자.

  • async void 쓰지 마라 (예외 처리 불가능)
  • .Result, .Wait() 쓰지 마라 (데드락 위험)
  • ConfigureAwait(false)는 라이브러리나 서버 사이드에선 필수 고려
728x90