메서드나 람다 선언부에 붙이는 키워드.
“이 메서드는 비동기 스타일로 작성되었고,Task또는Task<T>를 반환할 거야” 라는 표시.
메서드/람다 몸통 안에서 쓰는 키워드(연산자).
“이 비동기 작업(Task)이 끝날 때까지 비동기적으로 기다렸다가, 끝나면 그 다음 줄부터 이어서 실행해 줘.” 라는 뜻.
async Task DoWorkAsync()
{
// 여기 안에서 await 사용 가능
}
async Task<int> GetNumberAsync()
{
await Task.Delay(1000);
return 10;
}
async는 메서드 이름 앞에 붙는다.Task / Task<T> / (특수한 경우) void 중 하나다.
중요한 점 하나:
async를 붙였다고 자동으로 새 스레드가 만들어지는 게 아니다.
그냥 “비동기 로직을 담고 있고, 안에서 await 쓸 거야” 정도의 표지판에 가깝다.
async Task TestAsync()
{
await Task.Delay(1000); // OK
}
void Test()
{
await Task.Delay(1000); // ❌ 컴파일 에러: async 메서드가 아니라서
}
await는 반드시 async가 붙은 메서드/람다 안에서만 사용할 수 있다.await someTask는 “someTask가 끝날 때까지 비동기적으로 기다렸다가, 다시 여기로 돌아와서 아래 코드를 이어서 실행하자” 라는 의미다.Task<T>를 await 하면, T 결과값을 바로 꺼내 쓸 수 있다.int result = await GetNumberAsync(); // GetNumberAsync가 Task<int>를 반환한다고 가정
위 한 줄은 사실상:
GetNumberAsync()가 시작되고 Task<int> 하나가 만들어진다.int 값을 꺼내서 result에 넣는다.await를 쓸 수 있게 해준다.Task / Task<T> / void 중 하나로 맞추게 만든다.흔한 오해 중 하나가:
“async 붙이면 자동으로 다른 스레드에서 실행된다”
이건 아니다.
새 스레드를 만드는 건 Task.Run, Thread, ThreadPool 같은 애들이고,
async는 단지 비동기 흐름을 코드 구조로 표현하기 위한 키워드다.
Task 또는 Task<T>가 완료될 때까지 비동기적으로 대기한다.Task<T>를 await하면, 결과 T를 바로 받을 수 있다.async Task DemoAsync()
{
Console.WriteLine("1");
await Task.Delay(1000); // 1초 후, 다시 여기로 돌아와서
Console.WriteLine("2"); // 이 줄부터 이어서 실행
}
여기서 await Task.Delay(1000)를 Thread.Sleep(1000)으로 바꾸면
1초 동안 스레드가 진짜로 멈춰버린다.
await는 “기다린다”는 의미는 같지만, 스레드를 점유하지 않는다는 점이 결정적으로 다르다.
async Task FooAsync()
{
Console.WriteLine("Hello");
// await 없음
}
이런 코드는 사실상 동기 메서드와 거의 같다. 컴파일러가 내부적으로 Task를 감싸긴 하지만, 중간에 “끊기는 지점”이 없기 때문에 흐름은 그냥 위에서 아래로 쭉 실행된다.
그래서 진짜 비동기의 느낌이 나는 지점은 결국 await를 만나는 순간부터라고 보면 된다.
void Foo()
{
await Task.Delay(1000); // ❌ 컴파일 에러
}
이런 코드는 컴파일 자체가 안 된다. await는 반드시 async 메서드 안에서만 사용 가능하기 때문이다.
즉, 둘의 관계를 요약하면:
await는 async 없이 쓸 수 없다.async는 await 없이 쓸 수는 있지만, 의미가 거의 없다.async Task<int> GetNumberAsync()
{
await Task.Delay(1000); // 1초짜리 비동기 작업
return 10;
}
async Task ShowAsync()
{
Console.WriteLine("시작");
int n = await GetNumberAsync(); // 여기서 비동기 대기 + 결과 받기
Console.WriteLine("결과: " + n);
}
GetNumberAsync
→ “나중에 int를 줄 비동기 메서드야” (반환 타입: Task<int>)await Task.Delay(1000)
→ 1초 뒤에 다시 아래 줄부터 이어서 실행int n = await GetNumberAsync();
→ Task<int>가 끝날 때까지 기다렸다가, 안의 int 값(10)을 꺼내서 n에 대입Task, Task<T>, void(이벤트 핸들러) 중 하나.Task, Task<T>) 앞에 붙는 연산자.Task<T>라면 결과 T를 바로 꺼내서 변수에 넣어줌.정말 짧게 말하면:
async는 “이 메서드는 비동기 모드로 작성할게” 라고 선언하는 스위치이고,
await는 “이 비동기 작업이 끝날 때까지 잠깐 맡겨 두고, 끝나면 여기서부터 다시 시작하자” 라고 말하는 실행 지점이다.