도입 – 학습 목표 및 필요성
🔗 관련 에러 해결 가이드
현대 JavaScript 개발에서 비동기 프로그래밍은 필수적인 기술입니다. Promise와 async/await 실전 활용법을 마스터하면 API 호출, 파일 처리, 데이터베이스 연동 등 실무에서 마주치는 다양한 비동기 작업을 효율적으로 처리할 수 있습니다. 이 튜토리얼에서는 기본 개념부터 고급 패턴까지, 실전에서 바로 적용할 수 있는 Promise와 async/await의 모든 것을 배워보겠습니다. 콜백 지옥(Callback Hell)에서 벗어나 깔끔하고 유지보수하기 쉬운 코드를 작성하는 방법을 익혀보세요.
기본 개념 설명
Promise는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체입니다. Promise는 세 가지 상태를 가집니다: Pending(대기), Fulfilled(이행), Rejected(거부). Promise를 생성할 때는 executor 함수를 전달하며, 이 함수는 resolve와 reject 두 개의 콜백을 인자로 받습니다. 작업이 성공하면 resolve를 호출하고, 실패하면 reject를 호출합니다.
async/await는 Promise를 더 간결하게 사용할 수 있게 해주는 문법적 설탕(syntactic sugar)입니다. async 키워드를 함수 앞에 붙이면 해당 함수는 자동으로 Promise를 반환합니다. await 키워드는 async 함수 내부에서만 사용할 수 있으며, Promise가 처리될 때까지 함수 실행을 일시 중지합니다. 이를 통해 비동기 코드를 동기 코드처럼 작성할 수 있어 가독성이 크게 향상됩니다.
단계별 구현 가이드
1단계: Promise 기본 사용법
먼저 간단한 Promise를 만들어봅시다. Promise 생성자는 executor 함수를 받으며, 이 함수는 비동기 작업을 수행합니다.
const simplePromise = new Promise((resolve, reject) => {
const success = true;
setTimeout(() => {
if (success) {
resolve('작업 성공!');
} else {
reject('작업 실패!');
}
}, 1000);
});
simplePromise
.then(result => console.log(result))
.catch(error => console.error(error));
2단계: Promise 체이닝
여러 비동기 작업을 순차적으로 실행해야 할 때 Promise 체이닝을 사용합니다. then 메서드는 새로운 Promise를 반환하므로 계속해서 연결할 수 있습니다.
function fetchUser(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: userId, name: '홍길동' });
}, 1000);
});
}
function fetchPosts(user) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([{ title: '첫 번째 글' }, { title: '두 번째 글' }]);
}, 1000);
});
}
fetchUser(1)
.then(user => {
console.log('사용자:', user);
return fetchPosts(user);
})
.then(posts => {
console.log('게시글:', posts);
})
.catch(error => console.error('에러 발생:', error));
3단계: async/await로 전환
위의 Promise 체이닝을 async/await로 변환하면 훨씬 읽기 쉬운 코드가 됩니다.
async function getUserAndPosts(userId) {
try {
const user = await fetchUser(userId);
console.log('사용자:', user);
const posts = await fetchPosts(user);
console.log('게시글:', posts);
return posts;
} catch (error) {
console.error('에러 발생:', error);
throw error;
}
}
getUserAndPosts(1);
4단계: 병렬 처리
여러 비동기 작업이 서로 독립적이라면 Promise.all을 사용해 병렬로 실행할 수 있습니다.
async function fetchAllData() {
try {
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then(res => res.json()),
fetch('/api/posts').then(res => res.json()),
fetch('/api/comments').then(res => res.json())
]);
return { users, posts, comments };
} catch (error) {
console.error('데이터 로딩 실패:', error);
}
}
실제 코드 예제와 설명
실무에서 자주 사용하는 API 호출 및 에러 처리 패턴을 살펴보겠습니다.
// API 클라이언트 클래스
class APIClient {
constructor(baseURL) {
this.baseURL = baseURL;
}
async get(endpoint) {
try {
const response = await fetch(`${this.baseURL}${endpoint}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('API 호출 실패:', error);
throw error;
}
}
async post(endpoint, body) {
try {
const response = await fetch(`${this.baseURL}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('데이터 전송 실패:', error);
throw error;
}
}
}
// 사용 예제
const api = new APIClient('https://api.example.com');
async function loadUserData(userId) {
try {
const user = await api.get(`/users/${userId}`);
const posts = await api.get(`/users/${userId}/posts`);
return { user, posts };
} catch (error) {
// 에러 처리 로직
return null;
}
}
이 예제는 재사용 가능한 API 클라이언트를 보여줍니다. async/await를 사용해 에러 처리와 응답 파싱을 깔끔하게 처리하고 있습니다.
고급 활용 방법
Promise와 async/await 실전 활용법의 고급 패턴을 알아봅시다.
1. Promise.allSettled로 안전한 병렬 처리
async function fetchMultipleAPIs() {
const results = await Promise.allSettled([
fetch('/api/data1'),
fetch('/api/data2'),
fetch('/api/data3')
]);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`API ${index + 1} 성공:`, result.value);
} else {
console.log(`API ${index + 1} 실패:`, result.reason);
}
});
}
2. 재시도 로직 구현
async function retryOperation(operation, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
3. 타임아웃 처리
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('타임아웃')), ms)
);
return Promise.race([promise, timeout]);
}
async function fetchWithTimeout(url) {
return await withTimeout(fetch(url), 5000);
}
마무리 및 추가 학습 자료
이 튜토리얼에서 Promise와 async/await 실전 활용법의 핵심을 모두 다뤘습니다. 기본 문법부터 고급 패턴까지 익혔다면, 이제 실제 프로젝트에 적용해보세요. 추가 학습을 위해 MDN Web Docs의 Promise 문서와 JavaScript.info의 async/await 섹션을 추천합니다. 또한 실무에서는 에러 처리와 로깅을 항상 고려하고, Promise.all과 Promise.race를 상황에 맞게 활용하는 것이 중요합니다. 지속적인 연습을 통해 비동기 프로그래밍 마스터가 되어보세요!
📚 함께 읽으면 좋은 글
Promise와 async/await 실전 활용법 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 11. 15.
🎯 Promise와 async/await 실전 활용법
Promise와 async/await 실전 활용법 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 11. 15.
🎯 Promise와 async/await 실전 활용법
Promise와 async/await 실전 활용법 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 11. 9.
🎯 Promise와 async/await 실전 활용법
Promise와 async/await 실전 활용법 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 11. 2.
🎯 Promise와 async/await 실전 활용법
DOM 조작 베스트 프랙티스 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 11. 14.
🎯 DOM 조작 베스트 프랙티스
💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!
📢 이 글이 도움되셨나요? 공유해주세요!
여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨
🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏
💬 여러분의 소중한 의견을 들려주세요!
이 글을 읽고 새롭게 알게 된 정보가 있다면 공유해주세요!
⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨
🔔 블로그 구독하고 최신 글을 받아보세요!
🌟 JavaScript 튜토리얼부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨
📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!