자바스크립트 비동기 완벽 가이드: Promise와 async/await 제대로 쓰기
자바스크립트는 싱글 스레드지만, 네트워크 요청·타이머·파일 읽기 같은 작업을 멈추지 않고 처리합니다. 이를 가능하게 하는 것이 비동기(asynchronous) 처리입니다. 과거 콜백 함수가 중첩되며 생기던 '콜백 지옥'은 Promise와 async/await가 등장하며 해결됐습니다. 가장 흔한 실수는 비동기 함수의 결과를 동기처럼 다루려다 undefined나 Promise {<pending>}를 만나는 것입니다.
Promise의 3가지 상태
Promise는 비동기 작업의 최종 결과를 담는 객체로, 세 가지 상태를 가집니다.
| 상태 | 의미 | 전이 |
|---|---|---|
| pending | 대기 중 | 초기 상태 |
| fulfilled | 성공 | .then()으로 결과 처리 |
| rejected | 실패 | .catch()로 에러 처리 |
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) resolve({ id, name: '영희' });
else reject(new Error('잘못된 ID'));
}, 500);
});
}
fetchUser(1)
.then(user => console.log(user.name)) // '영희'
.catch(err => console.error(err.message));
async/await: 동기 코드처럼 읽기
async/await는 Promise를 더 읽기 쉽게 만든 문법 설탕입니다. await는 Promise가 처리될 때까지 기다린 뒤 결과값을 반환합니다.
async function loadProfile(id) {
try {
const res = await fetch(`/api/users/${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;
}
}
주의: async 함수는 항상 Promise를 반환합니다. 따라서 호출하는 쪽에서도 await 또는 .then()을 써야 실제 값을 얻습니다.
병렬 처리: Promise.all로 속도 올리기
서로 의존하지 않는 비동기 작업을 await로 순차 실행하면 시간이 낭비됩니다. 독립적인 요청은 병렬로 처리하세요.
// 느림: 순차 실행 (총 ~2초)
const a = await fetchUser(1);
const b = await fetchUser(2);
// 빠름: 병렬 실행 (총 ~1초)
const [user1, user2] = await Promise.all([
fetchUser(1),
fetchUser(2),
]);
- Promise.all: 하나라도 실패하면 전체 reject
- Promise.allSettled: 실패해도 모든 결과를 받고 싶을 때
- Promise.race: 가장 먼저 끝나는 하나만 (타임아웃 구현에 유용)
마무리 체크리스트
- 비동기 함수 호출 시
await빠뜨리지 않기 (가장 흔한 버그) try/catch로 에러 처리 필수, fetch는res.ok도 체크- 독립적인 요청은
Promise.all로 병렬화 - 반복문 안에서
await를 남발하면 느려짐 →map+Promise.all활용 forEach는 await를 기다리지 않으므로 비동기 반복엔for...of사용
함께 보면 좋은 글
== vs ===, 그리고 NaN: 자바스크립트 동등 비교의 함정
느슨한 비교(==)의 암묵적 형변환이 만드는 버그, ===를 기본으로 써야 하는 이유, NaN·null·undefined 비교의 특이점을 예제로 정리합니다.
CSS Flexbox vs Grid 차이와 선택 기준: 1차원 vs 2차원 레이아웃
Flexbox와 Grid는 언제 무엇을 써야 할까요? 1차원과 2차원이라는 핵심 차이부터 실전 코드, 선택 기준까지 한 번에 정리합니다. 더 이상 헷갈리지 마세요.
React useEffect, useState 완벽 정리: 의존성 배열부터 클린업까지
React 함수형 컴포넌트의 핵심인 useState와 useEffect를 실무 관점에서 정리합니다. 의존성 배열, 무한 루프 방지, 클린업 함수까지 흔한 실수와 해결법을 코드로 짚어드립니다.