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, 비동기 에러 처리