C

자바스크립트 async await 사용법: 비동기 코드를 쉽게 작성하는 법

2026-05-02 · JavaScript · 비동기 · async await · Promise

자바스크립트 async await 사용법, 왜 필요할까?

자바스크립트 async await 사용법을 제대로 익히면 서버 요청, 파일 읽기, 타이머처럼 시간이 걸리는 비동기 작업을 마치 동기 코드처럼 위에서 아래로 읽기 쉽게 쓸 수 있습니다. asyncawait는 ES2017(ES8)에 도입된 문법으로, 내부적으로는 Promise 위에서 동작합니다. 즉 새로운 마법이 아니라 Promise를 더 깔끔하게 쓰게 해주는 "문법적 설탕(syntactic sugar)"입니다.

먼저 Promise를 짧게 짚고 넘어가기

비동기 작업의 결과는 미래에 도착합니다. Promise는 그 "미래의 값"을 담는 상자이며 대기(pending), 이행(fulfilled), 거부(rejected) 세 가지 상태를 가집니다. 기존에는 .then() 체이닝으로 결과를 꺼냈는데, 중첩이 깊어지면 읽기 어려워집니다.

fetch('/api/user')
  .then(res => res.json())
  .then(user => fetch('/api/posts/' + user.id))
  .then(res => res.json())
  .then(posts => console.log(posts))
  .catch(err => console.error(err));

async await 기본 문법

함수 앞에 async를 붙이면 그 함수는 항상 Promise를 반환합니다. 함수 내부에서 await를 쓰면 Promise가 처리될 때까지 기다렸다가 그 결과값을 꺼내 줍니다.

async function getPosts() {
  const res = await fetch('/api/user');
  const user = await res.json();
  const postRes = await fetch('/api/posts/' + user.id);
  const posts = await postRes.json();
  return posts; // Promise<posts> 로 감싸져 반환됨
}

getPosts().then(posts => console.log(posts));

핵심 규칙 두 가지입니다.

  • awaitasync 함수 안에서만 쓸 수 있습니다(단, 최신 모듈의 최상위에서는 top-level await 가능).
  • await는 Promise가 이행될 때까지 그 함수의 실행만 잠시 멈춥니다. 전체 프로그램이 멈추는 것이 아니라 다른 작업은 계속 진행됩니다.

화살표 함수에서도 사용 가능

const getUser = async (id) => {
  const res = await fetch('/api/user/' + id);
  return res.json();
};

실전 예제: 에러 처리는 try/catch로

비동기 작업은 실패할 수 있습니다. async/await에서는 동기 코드와 똑같이 try...catch로 에러를 잡습니다. 이것이 .catch()보다 직관적이라는 점이 큰 장점입니다.

async function loadUser(id) {
  try {
    const res = await fetch('/api/user/' + id);
    if (!res.ok) {
      throw new Error('HTTP 오류: ' + res.status);
    }
    const user = await res.json();
    return user;
  } catch (err) {
    console.error('사용자 불러오기 실패:', err.message);
    return null; // 기본값으로 복구
  }
}

병렬 실행: 순차 await의 함정 피하기

서로 의존하지 않는 작업을 각각 await하면 불필요하게 순서대로 기다리게 됩니다. 동시에 시작하려면 Promise.all을 사용하세요.

// 느림: A가 끝나야 B를 시작 (총합 시간)
const a = await fetchA();
const b = await fetchB();

// 빠름: A, B를 동시에 시작 (더 긴 쪽 시간)
const [a2, b2] = await Promise.all([fetchA(), fetchB()]);

async await vs Promise.then 비교

구분async / await.then() 체이닝
가독성위→아래 동기 코드처럼 읽힘중첩 시 가독성 저하
에러 처리try / catch.catch()
중간 변수 사용쉬움 (지역 변수로 보관)스코프 공유가 번거로움
반복문과 결합for...of 안에서 자연스러움까다로움

흔한 실수

  • await 빠뜨리기: const data = res.json();처럼 await를 안 쓰면 data에 실제 값이 아니라 Promise 객체가 들어갑니다.
  • forEach 안에서 await: arr.forEach(async ...)는 완료를 기다리지 않습니다. 순차 처리는 for...of, 병렬은 Promise.all(arr.map(...))을 쓰세요.
  • 불필요한 순차 await: 독립적인 요청은 Promise.all로 병렬화하세요.
  • 최상위 await 오해: 일반 스크립트나 함수 밖에서 await를 쓰면 문법 오류가 납니다.

자주 묻는 질문

Q1. async 함수는 항상 Promise를 반환하나요?

네. return 1;처럼 일반 값을 반환해도 자동으로 Promise.resolve(1)로 감싸집니다. 그래서 호출한 쪽에서 다시 await하거나 .then()으로 값을 꺼내야 합니다.

Q2. await가 전체 프로그램을 멈추나요?

아니요. await는 해당 async 함수의 실행만 일시 중단하고, 그 사이 자바스크립트 엔진은 다른 코드(이벤트, 다른 콜백 등)를 계속 처리합니다. 브라우저가 멈추지 않는 이유입니다.

Q3. try/catch 없이 에러를 처리하면 어떻게 되나요?

처리하지 않은 거부(rejection)는 "Unhandled Promise Rejection" 경고를 발생시킵니다. try/catch를 쓰거나, 호출부에서 .catch()를 붙여 반드시 에러를 처리하세요.

자바스크립트 async await 사용법: 비동기 코드를 쉽게 작성하는 법 | CodeMaster 블로그 | CodeMaster