18강

async/await

4 조회 1

async/await 문법으로 비동기 코드를 동기처럼 읽기 좋게 작성하는 방법을 배운다. try/catch 에러 처리, 병렬 실행 최적화, 그리고 for...of와의 조합 등 실무 패턴을 익힌다.

async/await — Promise를 더 읽기 좋게

async/await는 Promise를 기반으로 동작하는 문법 설탕(Syntactic Sugar)입니다. Promise 체이닝을 동기 코드처럼 작성할 수 있게 해줍니다. ES2017에서 도입되었으며, 현재 비동기 코드의 표준 작성 방식입니다.

기본 문법

// async 함수는 항상 Promise를 반환
async function greet() {
  return "Hello!"; // Promise.resolve("Hello!")와 동일
}

greet().then(msg => console.log(msg)); // "Hello!"

// await — Promise가 완료될 때까지 기다림 (async 함수 내에서만 사용 가능)
async function fetchUser(id) {
  const response = await fetch(`https://api.example.com/users/${id}`);
  const user = await response.json();
  return user;
}

// Promise 체이닝 vs async/await 비교
// Promise 체이닝:
function loadDashboard() {
  return fetchUser(1)
    .then(user => fetchOrders(user.id))
    .then(orders => fetchOrderDetails(orders[0].id))
    .then(detail => renderDashboard(detail));
}

// async/await (훨씬 읽기 좋음):
async function loadDashboard() {
  const user = await fetchUser(1);
  const orders = await fetchOrders(user.id);
  const detail = await fetchOrderDetails(orders[0].id);
  return renderDashboard(detail);
}

에러 처리 — try/catch

async function loadUserData(userId) {
  try {
    const user = await fetchUser(userId);
    const orders = await fetchOrders(user.id);
    return { user, orders };
  } catch (error) {
    console.error("데이터 로드 실패:", error.message);
    throw error; // 필요하면 다시 던지기
  } finally {
    setLoading(false); // 항상 실행
  }
}

// 특정 단계만 에러 처리
async function safeLoadOrders(userId) {
  const user = await fetchUser(userId); // 실패하면 전체 함수 중단

  // 주문 실패해도 계속 진행
  let orders = [];
  try {
    orders = await fetchOrders(userId);
  } catch {
    console.warn("주문 로드 실패, 기본값 사용");
  }

  return { user, orders };
}

병렬 실행 — await의 함정

// 나쁜 예: 순서대로 기다림 (총 3초 소요)
async function loadSlow() {
  const users = await fetchUsers();       // 1초 대기
  const products = await fetchProducts(); // 1초 대기
  const orders = await fetchOrders();     // 1초 대기
  return { users, products, orders };
}

// 좋은 예: 동시에 시작 (총 1초 소요)
async function loadFast() {
  const [users, products, orders] = await Promise.all([
    fetchUsers(),
    fetchProducts(),
    fetchOrders()
  ]);
  return { users, products, orders };
}

// 의존성 있는 것만 순서대로, 나머지는 병렬로
async function loadOptimized() {
  const user = await fetchUser(1); // 먼저 필요

  // user.id가 필요한 것들을 병렬로
  const [orders, favorites] = await Promise.all([
    fetchOrders(user.id),
    fetchFavorites(user.id)
  ]);

  return { user, orders, favorites };
}

반복문에서 async/await

// for...of 사용 — 순서 보장, await 정상 작동
async function processSequentially(items) {
  const results = [];
  for (const item of items) {
    const result = await processItem(item); // 순서대로 처리
    results.push(result);
  }
  return results;
}

// map + Promise.all — 병렬 처리
async function processParallel(items) {
  const promises = items.map(item => processItem(item));
  return Promise.all(promises); // 모두 동시에 시작
}

// forEach에서 await는 동작 안 함! (주의)
async function wrongWay(items) {
  items.forEach(async item => {
    await processItem(item); // 기다리지 않음!
  });
  // 모든 작업이 완료되기 전에 함수 종료
}

실전 fetch 패턴

// API 호출 유틸리티 함수
async function apiRequest(url, options = {}) {
  const response = await fetch(url, {
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${getToken()}`,
      ...options.headers
    },
    ...options
  });

  if (!response.ok) {
    const error = await response.json().catch(() => ({}));
    throw new Error(error.message || `HTTP ${response.status}`);
  }

  return response.json();
}

// 사용 예
async function createPost(data) {
  try {
    const post = await apiRequest("/api/posts", {
      method: "POST",
      body: JSON.stringify(data)
    });
    return post;
  } catch (error) {
    if (error.message.includes("401")) {
      redirectToLogin();
    }
    throw error;
  }
}

async/await 패턴 모음

// 타임아웃 구현
function withTimeout(promise, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error("요청 시간 초과")), ms)
  );
  return Promise.race([promise, timeout]);
}

async function fetchWithTimeout(url) {
  return withTimeout(fetch(url), 5000);
}

// 재시도 구현
async function retry(fn, maxAttempts = 3, delay = 1000) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxAttempts) throw error;
      await new Promise(resolve => setTimeout(resolve, delay * attempt));
    }
  }
}

// 사용
const data = await retry(() => fetchData(), 3, 500);

관련 검색어  ·  async await 사용법, try catch 비동기, Promise.all 병렬 처리, for of await, fetch async await, 비동기 에러 처리

채용정보
공지사항
인사이트
MY