Promise와 async/await 실전 활용법 – 초보자도 쉽게 따라하는 완벽 가이드
1. 도입 – 학습 목표 및 필요성
🔗 관련 에러 해결 가이드
현대 JavaScript 개발에서 비동기 처리는 필수적인 기술입니다. 이 가이드에서는 Promise와 async/await 실전 활용법을 체계적으로 학습하여, 복잡한 비동기 작업을 효율적으로 처리하는 방법을 익히게 됩니다. API 호출, 파일 읽기, 데이터베이스 쿼리 등 실무에서 자주 마주치는 비동기 상황을 우아하게 해결할 수 있습니다. 콜백 지옥(Callback Hell)에서 벗어나 가독성 높고 유지보수가 쉬운 코드를 작성하는 것이 이 튜토리얼의 핵심 목표입니다. 초보자도 단계별로 따라하면서 실전 감각을 키울 수 있도록 구성했습니다.
2. 기본 개념 설명
Promise란?
Promise는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체입니다. 세 가지 상태를 가집니다:
- Pending(대기): 초기 상태, 아직 완료되지 않음
- Fulfilled(이행): 작업이 성공적으로 완료됨
- Rejected(거부): 작업이 실패함
Promise는 .then(), .catch(), .finally() 메서드를 통해 결과를 처리합니다.
async/await란?
async/await는 ES2017에서 도입된 문법으로, Promise를 더 간결하고 동기 코드처럼 작성할 수 있게 해줍니다. async 키워드는 함수가 항상 Promise를 반환하도록 만들고, await 키워드는 Promise가 처리될 때까지 함수 실행을 일시 중지합니다. 이를 통해 비동기 코드의 가독성이 크게 향상되며, 에러 처리도 try-catch 구문으로 직관적으로 할 수 있습니다. Promise와 async/await 실전 활용법을 마스터하면 복잡한 비동기 로직도 쉽게 구현할 수 있습니다.
3. 단계별 구현 가이드
Step 1: 기본 Promise 생성하기
먼저 간단한 Promise를 만들어봅시다. Promise 생성자는 executor 함수를 인자로 받으며, 이 함수는 resolve와 reject 두 개의 콜백을 매개변수로 받습니다.
// 기본 Promise 생성
const simplePromise = new Promise((resolve, reject) => {
const success = true;
setTimeout(() => {
if (success) {
resolve('작업이 성공했습니다!');
} else {
reject('작업이 실패했습니다.');
}
}, 1000);
});
// Promise 사용
simplePromise
.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 getOrders(user) {
return new Promise((resolve) => {
setTimeout(() => {
resolve([{ orderId: 1, product: '노트북' }, { orderId: 2, product: '마우스' }]);
}, 1000);
});
}
// Promise 체이닝으로 순차 실행
getUser(100)
.then(user => {
console.log('사용자:', user);
return getOrders(user);
})
.then(orders => {
console.log('주문 목록:', orders);
})
.catch(error => {
console.error('에러 발생:', error);
});
Step 3: async/await로 변환하기
위의 Promise 체이닝을 async/await로 변환하면 훨씬 읽기 쉬운 코드가 됩니다.
async function getUserAndOrders(userId) {
try {
const user = await getUser(userId);
console.log('사용자:', user);
const orders = await getOrders(user);
console.log('주문 목록:', orders);
return orders;
} catch (error) {
console.error('에러 발생:', error);
throw error;
}
}
// 함수 호출
getUserAndOrders(100);
Step 4: 병렬 처리하기
여러 독립적인 비동기 작업을 동시에 실행하려면 Promise.all()을 사용합니다.
async function fetchMultipleData() {
try {
// 세 개의 API를 동시에 호출
const [users, products, settings] = await Promise.all([
fetch('/api/users').then(res => res.json()),
fetch('/api/products').then(res => res.json()),
fetch('/api/settings').then(res => res.json())
]);
console.log('모든 데이터 로드 완료');
return { users, products, settings };
} catch (error) {
console.error('데이터 로드 실패:', error);
}
}
Step 5: 에러 처리 패턴
실전에서는 견고한 에러 처리가 중요합니다. 여러 패턴을 조합하여 사용할 수 있습니다.
async function robustDataFetch(url) {
let retries = 3;
while (retries > 0) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
retries--;
console.log(`재시도 남은 횟수: ${retries}`);
if (retries === 0) {
throw new Error(`최종 실패: ${error.message}`);
}
// 1초 대기 후 재시도
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
}
4. 실제 코드 예제와 설명
실무에서 자주 사용되는 실전 시나리오를 살펴보겠습니다. 다음은 여러 사용자의 데이터를 순차적으로 처리하면서 진행 상황을 추적하는 예제입니다.
// 실전 예제: 사용자 데이터 일괄 처리
class UserDataProcessor {
constructor() {
this.processedCount = 0;
}
async processUser(userId) {
// API에서 사용자 데이터 가져오기
const userData = await fetch(`/api/users/${userId}`).then(res => res.json());
// 데이터 검증 및 변환
const validatedData = this.validateUser(userData);
// 데이터베이스에 저장
await this.saveToDatabase(validatedData);
this.processedCount++;
return validatedData;
}
validateUser(user) {
if (!user.email || !user.name) {
throw new Error('필수 정보가 누락되었습니다.');
}
return {
...user,
processedAt: new Date().toISOString()
};
}
async saveToDatabase(data) {
// 데이터베이스 저장 시뮬레이션
return new Promise((resolve) => {
setTimeout(() => resolve(true), 500);
});
}
async processBatch(userIds) {
const results = [];
const errors = [];
for (const userId of userIds) {
try {
const result = await this.processUser(userId);
results.push(result);
console.log(`처리 완료: ${this.processedCount}/${userIds.length}`);
} catch (error) {
errors.push({ userId, error: error.message });
console.error(`사용자 ${userId} 처리 실패:`, error.message);
}
}
return { results, errors, total: userIds.length };
}
}
// 사용 예시
async function main() {
const processor = new UserDataProcessor();
const userIds = [1, 2, 3, 4, 5];
const summary = await processor.processBatch(userIds);
console.log('처리 결과:', summary);
}
main();
이 예제는 Promise와 async/await 실전 활용법의 핵심을 보여줍니다. 에러 처리, 진행 상황 추적, 일괄 처리 등 실무에서 필요한 모든 요소를 포함하고 있습니다.
5. 고급 활용 방법
Promise.race() 활용
타임아웃 구현이나 가장 빠른 응답 선택 시 유용합니다.
async function fetchWithTimeout(url, timeout = 5000) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('요청 시간 초과')), timeout);
});
const fetchPromise = fetch(url);
return Promise.race([fetchPromise, timeoutPromise]);
}
Promise.allSettled() 사용
모든 Promise의 성공/실패 여부와 관계없이 모든 결과를 받을 때 사용합니다.
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 제너레이터 활용
async function* dataStream(urls) {
for (const url of urls) {
const data = await fetch(url).then(res => res.json());
yield data;
}
}
// 사용
async function processStream() {
for await (const data of dataStream(['/api/1', '/api/2', '/api/3'])) {
console.log('데이터 처리:', data);
}
}
6. 마무리 및 추가 학습 자료
Promise와 async/await 실전 활용법을 통해 비동기 JavaScript의 핵심을 학습했습니다. 이제 여러분은 복잡한 비동기 작업도 자신있게 처리할 수 있습니다. 더 깊이 있는 학습을 위해 다음 자료를 추천합니다:
- MDN Web Docs의 Promise 및 async/await 공식 문서
- JavaScript.info의 비동기 프로그래밍 섹션
- 실제 프로젝트에서 API 호출 및 데이터 처리 구현 연습
- Node.js 환경에서의 비동기 파일 처리 학습
지속적인 연습과 실전 적용을 통해 Promise와 async/await 실전 활용법을 완벽히 마스터하시기 바랍니다. 질문이나 피드백이 있다면 커뮤니티에서 공유해주세요!
📚 함께 읽으면 좋은 글
JavaScript 비동기 프로그래밍 마스터하기 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 10. 7.
🎯 JavaScript 비동기 프로그래밍 마스터하기
JavaScript 클로저 이해하고 활용하기 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 10. 6.
🎯 JavaScript 클로저 이해하고 활용하기
JavaScript 클로저 이해하고 활용하기 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 10. 5.
🎯 JavaScript 클로저 이해하고 활용하기
ES6 화살표 함수 완벽 가이드 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 10. 5.
🎯 ES6 화살표 함수 완벽 가이드
JavaScript 모듈 시스템 완전 정복 – 초보자도 쉽게 따라하는 완벽 가이드
📅 2025. 10. 4.
🎯 JavaScript 모듈 시스템 완전 정복
💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!
📢 이 글이 도움되셨나요? 공유해주세요!
여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨
🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏
💬 여러분의 소중한 의견을 들려주세요!
여러분은 Promise와 async/await 실전 활용법에 대해 어떻게 생각하시나요?
⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨
🔔 블로그 구독하고 최신 글을 받아보세요!
🌟 JavaScript 튜토리얼부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨
📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!