TypeScript객체와 함수

async, await, Promise 타입과 Awaited

`async` 함수의 반환 타입, `Promise<T>`, `Awaited<T>`를 어떻게 읽고 설계할지 비동기 타입 관점에서 정리합니다.

마지막 수정 2026년 3월 22일

기본 패턴

ts
async function fetchUser(id: string): Promise<User> {
  const res = await api.get(`/users/${id}`);
  return res.data;
}

type UserResult = Awaited<ReturnType<typeof fetchUser>>;

설명

  • async 함수는 값을 직접 반환하는 것처럼 보여도 타입 수준에서는 항상 Promise<...>를 반환합니다. 그래서 비동기 함수의 핵심 타입은 "최종 결과 타입"이 아니라 "Promise 안에 무엇이 들어 있나"입니다.
  • await는 Promise를 풀어 안쪽 값을 꺼내는 문법이고, TypeScript는 이 과정을 타입 수준에서도 추적합니다. 즉 await promiseOfUser를 하면 결과 타입은 User가 됩니다.
  • Awaited<T>는 이런 언랩 과정을 타입으로 표현하는 유틸리티입니다. 중첩 Promise나 PromiseLike가 섞인 타입을 다룰 때 특히 유용합니다.
  • 실무에서는 Promise<T>를 명시하는 것만큼, API helper와 wrapper 함수의 반환 타입을 중복 없이 재사용하는 일이 중요합니다. 이때 ReturnTypeAwaited 조합이 자주 등장합니다.
  • 결국 TypeScript의 비동기 타입 설계는 "지금 이 값이 Promise 자체인가, 이미 풀린 결과인가"를 명확히 구분하는 습관에서 시작합니다.

빠른 정리

표현의미
async function fn(): Promise<T>비동기 함수가 최종적으로 T를 돌려줌
await promisePromise 안쪽 값을 꺼냄
Promise<T>아직 도착하지 않은 T
Awaited<T>Promise 구조를 한 단계 이상 풀어 최종 값 타입 추출
실전 조합Awaited<ReturnType<typeof fn>>

주의할 점

Promise<User>User를 섞어 생각하면 타입이 바로 흔들립니다. 함수 시그니처를 볼 때는 "이 함수가 값을 반환하는가, Promise를 반환하는가"를 먼저 분리해서 읽는 습관이 중요합니다.

참고 링크

2 sources