정의
Async/await는 비동기 프로그래밍을 보다 직관적으로 표현하기 위해 고안된 구문 설계이다. async 키워드가 붙은 함수(또는 메서드)는 비동기적으로 실행된다는 뜻을 명시하고, 그 내부에서 await 키워드를 사용하면 비동기 작업이 완료될 때까지 현재 실행 흐름을 일시 중단(pause)한다. 이때 실제 스레드가 차단되지 않으며, 작업이 완료되면 이어서 실행된다.
주요 특징
- 비동기 함수 선언 –
async키워드가 함수 선언 앞에 붙으며, 함수는 일반적으로Promise(또는 해당 언어의 비동기 결과 객체)를 반환한다. - 대기 연산 –
await는Promise와 같은 비동기 연산을 기다리는 문법이며, 해당 연산이 해결(resolve)되거나 거부(reject)될 때까지 함수 내부 실행을 일시 중단한다. - 에러 처리 –
await구문은try…catch블록 안에서 사용되어 비동기 연산에서 발생한 예외를 동기식 코드와 동일하게 처리한다. - 스레드 차단 없음 –
await는 내부적으로 콜백 큐와 이벤트 루프를 활용해 비동기 작업을 예약하므로, 호출 스레드가 블록되지 않는다.
역사·배경
- JavaScript: ES2017(ECMAScript 2017, 2018년 발표)에서 공식적으로 도입되었다. 이전에는
Promise체이닝이나 콜백 함수가 일반적이었다. - C#: .NET Framework 4.5 (2012년)에서
async와await키워드가 도입되어 비동기 메서드 구현을 간소화했다. - Python: 3.5 버전(2015년)에서
async def와await구문이 추가되어asyncio라이브러리와 연계된 비동기 프로그래밍을 지원한다. - 그 외 언어: Kotlin, Swift, Rust, Dart 등에서도 유사한 형태의 async/await 구문이 제공된다.
사용 예시 (JavaScript)
// 비동기 함수 정의
async function fetchData(url) {
try {
const response = await fetch(url); // fetch는 Promise 반환
const data = await response.json(); // JSON 파싱도 비동기
return data;
} catch (error) {
console.error('오류 발생:', error);
throw error;
}
}
// 호출 측
(async () => {
const result = await fetchData('https://api.example.com/items');
console.log(result);
})();
지원 언어 및 플랫폼
| 언어 | async/await 지원 버전 | 주요 구현 메커니즘 |
|---|---|---|
| JavaScript (ECMAScript) | ES2017 (ES8) | Promise 기반, 이벤트 루프 |
| C# | .NET 4.5 / C# 5.0 | Task 기반, 스레드 풀 |
| Python | 3.5+ | asyncio, awaitable 객체 |
| Kotlin | 1.3+ (코루틴) | suspend 함수와 코루틴 디스패처 |
| Swift | 5.5+ | Concurrency 프레임워크, Task |
| Rust | 1.39+ | Future 트레이트와 async/.await 매크로 |
| Dart | 2.0+ | Future와 async/await 구문 |
| Go | (비공식) | go 런타임에서 await 키워드 자체는 없으나, 비동기 흐름을 관리하는 서드파티 라이브러리 존재 |
장점
- 가독성 향상: 복잡한 콜백 지옥(callback hell)이나 긴
Promise체이닝 없이 순차적인 코드 형태를 유지한다. - 에러 처리 통합:
try…catch구문을 활용해 동기식 코드와 동일한 방식으로 예외를 관리한다. - 디버깅 편리: 스택 트레이스가 비동기 경계에서도 연속적으로 유지되어 디버거가 중단점을 삽입하기 쉽다.
제한점·주의사항
- 실행 컨텍스트 의존:
await구문은 반드시async함수 내부에서만 사용 가능하다. 일반 함수나 전역 스코프에서는 문법 오류가 발생한다. - 동시성 제한:
await는 해당 지점에서 현재 함수 흐름을 일시 중단하므로, 여러 비동기 작업을 동시에 진행하려면Promise.all(JavaScript) 등 별도의 병렬 실행 메커니즘을 사용해야 한다. - 성능 오버헤드: 매우 빈번한
await호출이 많을 경우, 내부 상태 머신 전환 및 스케줄링 비용이 누적될 수 있다. 고성능이 요구되는 환경에서는 프로파일링이 필요하다.
관련 개념
- Promise / Future / Task : async/await가 반환하거나 기다리는 비동기 결과 객체.
- Event Loop / Scheduler : 비동기 작업을 큐에 등록하고 실행 시점을 조정하는 런타임 메커니즘.
- 코루틴 (Coroutine) : Kotlin, Python 등에서 async/await가 구현되는 근본 원리.
결론
Async/await는 현대 프로그래밍 언어에서 비동기 흐름을 동기식 코드와 유사하게 표현하도록 설계된 구문이다. 이를 통해 개발자는 복잡한 콜백 구조를 피하고 일관된 에러 처리와 가독성을 확보할 수 있다. 다만, 비동기 작업의 병렬성 제어와 성능 특성을 이해하고 적절히 활용하는 것이 중요하다.