Promise와 async/await 실전 활용법 – 초보자도 쉽게 따라하는 완벽 가이드
1. 도입 – 학습 목표 및 필요성
🔗 관련 에러 해결 가이드
현대 JavaScript 개발에서 비동기 프로그래밍은 필수적인 기술입니다. Promise와 async/await 실전 활용법을 제대로 이해하고 활용하면, API 호출, 파일 처리, 데이터베이스 작업 등 실무에서 마주치는 다양한 비동기 상황을 효율적으로 처리할 수 있습니다. 이 튜토리얼에서는 콜백 지옥(Callback Hell)에서 벗어나 가독성 높고 유지보수하기 쉬운 코드를 작성하는 방법을 단계별로 학습합니다. 초보자도 쉽게 따라할 수 있도록 기본 개념부터 실전 예제까지 체계적으로 구성했으며, 실무에서 바로 적용할 수 있는 패턴과 베스트 프랙티스를 제공합니다.
2. 기본 개념 설명
Promise란?
Promise는 JavaScript에서 비동기 작업의 최종 완료 또는 실패를 나타내는 객체입니다. Promise는 세 가지 상태를 가집니다:
- Pending(대기): 초기 상태, 아직 완료되지 않은 상태
- Fulfilled(이행): 작업이 성공적으로 완료된 상태
- Rejected(거부): 작업이 실패한 상태
Promise는 .then(), .catch(), .finally() 메서드를 통해 결과를 처리합니다.
async/await란?
async/await는 Promise를 더 쉽게 사용할 수 있게 해주는 문법적 설탕(Syntactic Sugar)입니다. async 키워드를 함수 앞에 붙이면 해당 함수는 항상 Promise를 반환하며, await 키워드를 사용하면 Promise가 처리될 때까지 함수 실행을 일시 중단합니다. 이를 통해 비동기 코드를 동기 코드처럼 직관적으로 작성할 수 있어, 코드 가독성과 유지보수성이 크게 향상됩니다.
3. 단계별 구현 가이드
Step 1: 기본 Promise 생성하기
먼저 Promise를 생성하는 기본 방법을 알아봅시다. Promise 생성자는 executor 함수를 인자로 받으며, 이 함수는 resolve와 reject 두 개의 콜백을 매개변수로 받습니다.
// 기본 Promise 생성
const basicPromise = new Promise((resolve, reject) => {
const success = true;
if (success) {
resolve('작업이 성공했습니다!');
} else {
reject('작업이 실패했습니다.');
}
});
// Promise 사용
basicPromise
.then(result => console.log(result))
.catch(error => console.error(error));
Step 2: Promise 체이닝 활용하기
여러 비동기 작업을 순차적으로 실행해야 할 때 Promise 체이닝을 사용합니다. 각 .then()은 새로운 Promise를 반환하므로 연속적으로 연결할 수 있습니다.
// 사용자 정보를 가져온 후 게시글을 가져오는 예제
function getUser(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: userId, name: '홍길동' });
}, 1000);
});
}
function getPosts(userId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([{ id: 1, title: '첫 번째 게시글' }, { id: 2, title: '두 번째 게시글' }]);
}, 1000);
});
}
// Promise 체이닝
getUser(1)
.then(user => {
console.log('사용자:', user);
return getPosts(user.id);
})
.then(posts => {
console.log('게시글:', posts);
})
.catch(error => {
console.error('에러 발생:', error);
});
Step 3: async/await로 전환하기
위의 Promise 체이닝 코드를 async/await로 변환하면 훨씬 간결하고 읽기 쉬워집니다.
async function getUserAndPosts(userId) {
try {
const user = await getUser(userId);
console.log('사용자:', user);
const posts = await getPosts(user.id);
console.log('게시글:', posts);
return { user, posts };
} catch (error) {
console.error('에러 발생:', error);
throw error;
}
}
// 함수 호출
getUserAndPosts(1);
Step 4: 병렬 처리하기
여러 비동기 작업을 동시에 실행하고 모든 결과를 기다려야 할 때는 Promise.all()을 사용합니다.
async function fetchMultipleData() {
try {
// 세 개의 API를 동시에 호출
const [users, products, orders] = await Promise.all([
fetch('/api/users').then(res => res.json()),
fetch('/api/products').then(res => res.json()),
fetch('/api/orders').then(res => res.json())
]);
return { users, products, orders };
} catch (error) {
console.error('데이터 로딩 실패:', error);
}
}
Step 5: 에러 처리 패턴
async/await에서 에러 처리는 try-catch 블록을 사용합니다. 각 작업마다 세밀한 에러 처리가 필요할 경우 여러 try-catch 블록을 사용할 수 있습니다.
async function robustDataFetching(userId) {
let user = null;
let posts = [];
// 사용자 정보 가져오기
try {
user = await getUser(userId);
} catch (error) {
console.error('사용자 정보를 가져올 수 없습니다:', error);
return null;
}
// 게시글 가져오기 (실패해도 계속 진행)
try {
posts = await getPosts(user.id);
} catch (error) {
console.warn('게시글을 가져올 수 없습니다:', error);
posts = []; // 기본값 사용
}
return { user, posts };
}
4. 실제 코드 예제와 설명
실전 예제 1: API 데이터 페칭 및 처리
실무에서 가장 많이 사용하는 패턴인 API 호출과 데이터 처리를 구현해봅시다.
// API 유틸리티 함수
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`HTTP 에러: ${response.status}`);
}
return await response.json();
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('요청 시간 초과');
}
throw error;
}
}
// 실제 사용 예제
async function loadUserDashboard(userId) {
const loadingStart = Date.now();
try {
// 1. 사용자 기본 정보 가져오기
const user = await fetchWithTimeout(`/api/users/${userId}`);
console.log('✓ 사용자 정보 로드 완료');
// 2. 병렬로 여러 데이터 가져오기
const [profile, statistics, notifications] = await Promise.all([
fetchWithTimeout(`/api/users/${userId}/profile`),
fetchWithTimeout(`/api/users/${userId}/stats`),
fetchWithTimeout(`/api/users/${userId}/notifications`)
]);
console.log('✓ 대시보드 데이터 로드 완료');
const loadingTime = Date.now() - loadingStart;
console.log(`총 로딩 시간: ${loadingTime}ms`);
return {
user,
profile,
statistics,
notifications,
loadingTime
};
} catch (error) {
console.error('대시보드 로드 실패:', error.message);
throw error;
}
}
// 사용
loadUserDashboard(123)
.then(dashboard => {
console.log('대시보드:', dashboard);
})
.catch(error => {
console.error('치명적 에러:', error);
});
실전 예제 2: 재시도 로직 구현
async function retryOperation(operation, maxRetries = 3, delay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (error) {
if (i === maxRetries - 1) throw error;
console.log(`재시도 ${i + 1}/${maxRetries}...`);
await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));
}
}
}
// 사용 예제
const unreliableApi = () => fetchWithTimeout('/api/unstable-endpoint');
const result = await retryOperation(unreliableApi, 3, 1000);
5. 고급 활용 방법
Promise.race()로 타임아웃 구현
Promise와 async/await 실전 활용법의 고급 패턴으로, 여러 Promise 중 가장 먼저 완료되는 것을 사용할 수 있습니다.
async function fetchWithRaceTimeout(url, timeout = 3000) {
const fetchPromise = fetch(url).then(res => res.json());
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('타임아웃')), timeout)
);
return Promise.race([fetchPromise, timeoutPromise]);
}
Promise.allSettled()로 모든 결과 처리
일부 요청이 실패해도 모든 결과를 받아야 할 때 유용합니다.
async function fetchAllData(urls) {
const promises = urls.map(url => fetch(url).then(res => res.json()));
const results = await Promise.allSettled(promises);
const successful = results.filter(r => r.status === 'fulfilled').map(r => r.value);
const failed = results.filter(r => r.status === 'rejected').map(r => r.reason);
return { successful, failed };
}
순차 처리 패턴
async function processSequentially(items, asyncFunction) {
const results = [];
for (const item of items) {
const result = await asyncFunction(item);
results.push(result);
}
return results;
}
// 대용량 데이터 배치 처리
async function batchProcess(items, batchSize = 5) {
const results = [];
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize);
const batchResults = await Promise.all(batch.map(item => processItem(item)));
results.push(...batchResults);
}
return results;
}
6. 마무리 및 추가 학습 자료
이 튜토리얼에서 다룬 Promise와 async/await 실전 활용법을 통해 현대 JavaScript의 비동기 프로그래밍을 마스터할 수 있습니다. 실무에서는 에러 처리, 타임아웃, 재시도 로직, 병렬 처리 등을 조합하여 안정적인 애플리케이션을 구축합니다. 다음 단계로는 Promise 기반 라이브러리(axios, node-fetch 등)를 학습하고, 실제 프로젝트에 적용해보세요.
추가 학습 자료
- MDN Web Docs – Promise 및 async/await 공식 문서
- JavaScript.info – 비동기 프로그래밍 심화 가이드
- 실전 프로젝트: REST API 클라이언트 라이브러리 구축하기
- 성능 최적화: 비동기 작업의 병렬화 및 캐싱 전략
지금 바로 실습을 시작하여 Promise와 async/await 실전 활용법을 체득하고, 더 나은 JavaScript 개발자로 성장하세요!
📚 함께 읽으면 좋은 글
Promise와 async/await 실전 활용법 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 10. 9.
🎯 Promise와 async/await 실전 활용법
Promise와 async/await 실전 활용법 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 10. 8.
🎯 Promise와 async/await 실전 활용법
JavaScript 비동기 프로그래밍 마스터하기 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 10. 10.
🎯 JavaScript 비동기 프로그래밍 마스터하기
JavaScript 클로저 이해하고 활용하기 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 10. 9.
🎯 JavaScript 클로저 이해하고 활용하기
JavaScript 비동기 프로그래밍 마스터하기 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 10. 7.
🎯 JavaScript 비동기 프로그래밍 마스터하기
💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!
📢 이 글이 도움되셨나요? 공유해주세요!
여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨
🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏
💬 여러분의 소중한 의견을 들려주세요!
여러분은 Promise와 async/await 실전 활용법에 대해 어떻게 생각하시나요?
⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨
🔔 블로그 구독하고 최신 글을 받아보세요!
🌟 JavaScript 튜토리얼부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨
📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!