WIPIVERSE

Async/await

정의

Async/await는 비동기 프로그래밍을 보다 직관적으로 표현하기 위해 고안된 구문 설계이다. async 키워드가 붙은 함수(또는 메서드)는 비동기적으로 실행된다는 뜻을 명시하고, 그 내부에서 await 키워드를 사용하면 비동기 작업이 완료될 때까지 현재 실행 흐름을 일시 중단(pause)한다. 이때 실제 스레드가 차단되지 않으며, 작업이 완료되면 이어서 실행된다.

주요 특징

  1. 비동기 함수 선언 – async 키워드가 함수 선언 앞에 붙으며, 함수는 일반적으로 Promise(또는 해당 언어의 비동기 결과 객체)를 반환한다.
  2. 대기 연산 – await는 Promise와 같은 비동기 연산을 기다리는 문법이며, 해당 연산이 해결(resolve)되거나 거부(reject)될 때까지 함수 내부 실행을 일시 중단한다.
  3. 에러 처리 – await 구문은 try…catch 블록 안에서 사용되어 비동기 연산에서 발생한 예외를 동기식 코드와 동일하게 처리한다.
  4. 스레드 차단 없음 – 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는 현대 프로그래밍 언어에서 비동기 흐름을 동기식 코드와 유사하게 표현하도록 설계된 구문이다. 이를 통해 개발자는 복잡한 콜백 구조를 피하고 일관된 에러 처리와 가독성을 확보할 수 있다. 다만, 비동기 작업의 병렬성 제어와 성능 특성을 이해하고 적절히 활용하는 것이 중요하다.

둘러보기

더 찾아볼 만한 주제

    전체 문서 보기