JavaScript 비동기 처리 완벽 가이드
개요
이 문서에서는 JavaScript의 핵심 비동기 처리 메커니즘인 Promise와 async/await를 깊이 있게 다룹니다. 비동기 처리의 기본 개념부터 실전 활용법까지, 실행 가능한 코드 예제와 함께 학습할 수 있습니다.
이 가이드를 완료하면 다음을 할 수 있습니다:
- Promise의 동작 원리를 이해하고 직접 생성할 수 있습니다
- async/await를 사용하여 가독성 높은 비동기 코드를 작성할 수 있습니다
- 복잡한 비동기 흐름을 효과적으로 제어할 수 있습니다
- 실무에서 자주 발생하는 비동기 관련 문제를 해결할 수 있습니다
배경: 왜 비동기 처리가 필요한가?
JavaScript의 특성
JavaScript는 싱글 스레드 언어입니다. 하나의 호출 스택(Call Stack)에서 코드를 순차적으로 실행합니다. 만약 모든 작업을 동기적으로 처리한다면 어떻게 될까요?
// ❌ 동기 방식의 문제점
console.log('작업 시작');
const start = Date.now();
while (Date.now() - start < 5000) {
// 아무것도 하지 않고 대기
}
console.log('작업 완료');위 코드는 5초 동안 전체 애플리케이션이 멈춥니다. 이 시간 동안 사용자 클릭도, 화면 업데이트도 불가능합니다.
해결책: 비동기 처리
비동기 처리를 사용하면 시간이 오래 걸리는 작업을 백그라운드에서 실행하고, 메인 스레드는 다른 작업을 계속할 수 있습니다.
// ✅ 비동기 방식
console.log('작업 시작');
setTimeout(() => {
console.log('5초 후 실행');
}, 5000);
console.log('작업 완료');
// 출력 순서:
// 작업 시작
// 작업 완료
// (5초 후)
// 5초 후 실행진화의 역사
1세대: 콜백 (Callback)
초기 JavaScript는 콜백 함수로 비동기를 처리했습니다.
// 콜백 방식
function fetchUser(userId, callback) {
setTimeout(() => {
callback({ id: userId, name: 'John' });
}, 1000);
}
fetchUser(1, (user) => {
console.log(user.name); // 'John'
});문제점: 콜백 지옥 (Callback Hell)
// ❌ 콜백 지옥의 예
fetchUser(1, (user) => {
fetchPosts(user.id, (posts) => {
fetchComments(posts[0].id, (comments) => {
fetchReplies(comments[0].id, (replies) => {
console.log(replies); // 4단계 중첩!
});
});
});
});2세대: Promise (ES6/2015)
Promise는 콜백 지옥 문제를 해결하기 위해 등장했습니다.
// ✅ Promise 체이닝
fetchUser(1)
.then(user => fetchPosts(user.id))
.then(posts => fetchComments(posts[0].id))
.then(comments => fetchReplies(comments[0].id))
.then(replies => console.log(replies));3세대: async/await (ES8/2017)
async/await는 Promise를 더 직관적으로 사용할 수 있게 해줍니다.
// ✅ async/await - 동기 코드처럼 읽힘
async function loadData() {
const user = await fetchUser(1);
const posts = await fetchPosts(user.id);
const comments = await fetchComments(posts[0].id);
const replies = await fetchReplies(comments[0].id);
console.log(replies);
}Promise 완전 정복
Promise란?
Promise는 미래에 완료될 작업을 나타내는 객체입니다. 3가지 상태를 가집니다:
┌─────────────┐
│ Pending │ 초기 상태
│ (대기중) │
└──────┬──────┘
│
┌───┴────┐
┌──────┐ ┌──────┐
│Fulfilled│ │Rejected│
│ (이행) │ │ (거부) │
└──────┘ └──────┘Promise 생성하기
// 기본 구조
const promise = new Promise((resolve, reject) => {
// 비동기 작업 수행
const success = true;
if (success) {
resolve('성공 데이터'); // 성공 시
} else {
reject('실패 이유'); // 실패 시
}
});실전 예제 1: API 요청
function fetchUserData(userId) {
return new Promise((resolve, reject) => {
// 1초 후 데이터 반환 (실제로는 fetch API 사용)
setTimeout(() => {
if (userId > 0) {
resolve({
id: userId,
name: 'John Doe',
email: 'john@example.com'
});
} else {
reject(new Error('유효하지 않은 사용자 ID'));
}
}, 1000);
});
}
// 사용
fetchUserData(1)
.then(user => {
console.log('사용자 정보:', user);
})
.catch(error => {
console.error('오류 발생:', error.message);
});Promise 체이닝
여러 비동기 작업을 순차적으로 실행할 수 있습니다.
// 순차적 비동기 처리
fetchUserData(1)
.then(user => {
console.log('1단계: 사용자 조회 완료');
return fetchUserPosts(user.id); // 다음 Promise 반환
})
.then(posts => {
console.log('2단계: 게시글 조회 완료');
return fetchPostComments(posts[0].id);
})
.then(comments => {
console.log('3단계: 댓글 조회 완료');
console.log('전체 댓글:', comments);
})
.catch(error => {
// 체인 중 어디서든 발생한 오류를 여기서 처리
console.error('오류:', error);
})
.finally(() => {
// 성공/실패 여부와 관계없이 항상 실행
console.log('작업 종료');
});Promise 병렬 처리
Promise.all() - 모두 성공해야 함
// 여러 요청을 동시에 실행
const promise1 = fetchUserData(1);
const promise2 = fetchUserData(2);
const promise3 = fetchUserData(3);
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log('모든 사용자 조회 완료');
console.log('사용자 1:', results[0]);
console.log('사용자 2:', results[1]);
console.log('사용자 3:', results[2]);
})
.catch(error => {
// 하나라도 실패하면 전체 실패
console.error('오류:', error);
});사용 사례: 여러 API를 동시에 호출하고 모든 결과가 필요할 때
// 실제 사용 예시
async function loadDashboard() {
try {
const [userData, postsData, statsData] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/stats').then(r => r.json())
]);
return {
user: userData,
posts: postsData,
stats: statsData
};
} catch (error) {
console.error('대시보드 로딩 실패:', error);
}
}Promise.allSettled() - 결과와 관계없이 모두 대기
// 일부가 실패해도 전체 결과를 확인
const promises = [
fetchUserData(1), // 성공
fetchUserData(-1), // 실패
fetchUserData(2) // 성공
];
Promise.allSettled(promises)
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`${index}번째 성공:`, result.value);
} else {
console.log(`${index}번째 실패:`, result.reason.message);
}
});
});
// 출력:
// 0번째 성공: { id: 1, name: 'John Doe', ... }
// 1번째 실패: 유효하지 않은 사용자 ID
// 2번째 성공: { id: 2, name: 'Jane Doe', ... }사용 사례: 여러 작업의 성공/실패를 모두 로깅하고 싶을 때
Promise.race() - 가장 빨리 완료된 것만
// 가장 빠른 응답만 사용
const promise1 = new Promise(resolve => setTimeout(() => resolve('느린 서버'), 3000));
const promise2 = new Promise(resolve => setTimeout(() => resolve('빠른 서버'), 1000));
Promise.race([promise1, promise2])
.then(result => {
console.log('결과:', result); // '빠른 서버'
});사용 사례: 타임아웃 구현
function fetchWithTimeout(url, timeout = 5000) {
const fetchPromise = fetch(url);
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('요청 시간 초과')), timeout);
});
return Promise.race([fetchPromise, timeoutPromise]);
}
// 사용
fetchWithTimeout('/api/data', 3000)
.then(response => response.json())
.catch(error => {
if (error.message === '요청 시간 초과') {
console.error('서버 응답이 너무 느립니다');
}
});Promise.any() - 하나만 성공하면 됨
// 여러 소스 중 하나만 성공하면 됨
const promises = [
fetch('https://api1.example.com/data'),
fetch('https://api2.example.com/data'),
fetch('https://api3.example.com/data')
];
Promise.any(promises)
.then(response => response.json())
.then(data => {
console.log('데이터 가져오기 성공:', data);
})
.catch(error => {
// 모두 실패한 경우에만 실행
console.error('모든 소스에서 실패:', error);
});async/await 마스터하기
기본 문법
// async 함수 선언
async function myFunction() {
// await는 Promise가 완료될 때까지 기다림
const result = await someAsyncOperation();
return result;
}
// async 함수는 항상 Promise를 반환
myFunction().then(result => console.log(result));실전 예제 2: 사용자 데이터 로딩
async function loadUserProfile(userId) {
try {
// 1. 사용자 기본 정보 조회
const user = await fetchUserData(userId);
console.log('사용자 조회 완료:', user.name);
// 2. 사용자의 게시글 조회
const posts = await fetchUserPosts(userId);
console.log('게시글 수:', posts.length);
// 3. 최근 게시글의 댓글 조회
const latestPost = posts[0];
const comments = await fetchPostComments(latestPost.id);
console.log('댓글 수:', comments.length);
// 4. 모든 데이터를 합쳐서 반환
return {
user,
posts,
latestComments: comments
};
} catch (error) {
console.error('프로필 로딩 실패:', error);
throw error; // 상위로 에러 전파
}
}
// 사용
loadUserProfile(1)
.then(profile => {
console.log('프로필 로딩 완료:', profile);
})
.catch(error => {
console.error('최종 에러 처리:', error);
});병렬 처리 최적화
❌ 비효율적: 순차 처리
// 나쁜 예: 6초 소요 (각 2초씩)
async function loadData() {
const user = await fetchUser();
const posts = await fetchPosts();
const comments = await fetchComments();
return { user, posts, comments };
}✅ 효율적: 병렬 처리
// 좋은 예: 2초 소요 (동시 실행)
async function loadData() {
const [user, posts, comments] = await Promise.all([
fetchUser(), // 동시 실행
fetchPosts(), // 동시 실행
fetchComments() // 동시 실행
]);
return { user, posts, comments };
}에러 처리 패턴
패턴 1: try-catch (권장)
async function fetchData() {
try {
const response = await fetch('/api/data');
// HTTP 에러 체크
if (!response.ok) {
throw new Error(`HTTP 에러! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
// 네트워크 에러, 파싱 에러 등 모두 여기서 처리
console.error('데이터 가져오기 실패:', error);
// 사용자에게 친화적인 메시지
if (error.message.includes('Failed to fetch')) {
alert('네트워크 연결을 확인해주세요');
} else {
alert('데이터를 불러오는데 실패했습니다');
}
return null;
}
}패턴 2: 여러 에러 타입 처리
async function processUserData(userId) {
try {
const user = await fetchUserData(userId);
if (!user.verified) {
throw new Error('인증되지 않은 사용자');
}
const data = await processData(user);
return data;
} catch (error) {
if (error.message === '유효하지 않은 사용자 ID') {
console.error('사용자를 찾을 수 없습니다');
return null;
} else if (error.message === '인증되지 않은 사용자') {
console.error('인증이 필요합니다');
throw error; // 상위로 전파
} else {
console.error('예상치 못한 오류:', error);
throw error;
}
}
}패턴 3: finally를 활용한 정리 작업
async function uploadFile(file) {
// 로딩 상태 표시
showLoadingSpinner();
try {
// 파일 업로드
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error('업로드 실패');
}
const result = await response.json();
console.log('업로드 성공:', result);
return result;
} catch (error) {
console.error('업로드 에러:', error);
alert('파일 업로드에 실패했습니다');
return null;
} finally {
// 성공/실패 여부와 관계없이 항상 실행
hideLoadingSpinner();
}
}실전 활용 패턴
패턴 1: 재시도 로직
async function fetchWithRetry(url, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
console.log(`시도 ${i + 1}/${maxRetries}`);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
lastError = error;
console.error(`시도 ${i + 1} 실패:`, error.message);
// 마지막 시도가 아니면 대기 후 재시도
if (i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // 지수 백오프: 1s, 2s, 4s
console.log(`${delay}ms 후 재시도...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// 모든 시도 실패
throw new Error(`${maxRetries}번 시도 후 실패: ${lastError.message}`);
}
// 사용
fetchWithRetry('/api/data', 3)
.then(data => console.log('성공:', data))
.catch(error => console.error('최종 실패:', error));패턴 2: 순차 처리 (배열)
// 여러 항목을 순차적으로 처리
async function processItemsSequentially(items) {
const results = [];
for (const item of items) {
try {
console.log(`처리 중: ${item.id}`);
const result = await processItem(item);
results.push(result);
console.log(`완료: ${item.id}`);
} catch (error) {
console.error(`실패: ${item.id}`, error);
results.push(null);
}
}
return results;
}
// 사용
const items = [{ id: 1 }, { id: 2 }, { id: 3 }];
const results = await processItemsSequentially(items);
console.log('전체 결과:', results);패턴 3: 병렬 처리 (배열) with 동시성 제어
// 한 번에 너무 많은 요청을 보내지 않도록 제어
async function processItemsWithLimit(items, limit = 3) {
const results = [];
// 청크로 나누기
for (let i = 0; i < items.length; i += limit) {
const chunk = items.slice(i, i + limit);
console.log(`청크 처리: ${i + 1}~${i + chunk.length}`);
// 청크 내에서는 병렬 처리
const chunkResults = await Promise.all(
chunk.map(item => processItem(item).catch(err => null))
);
results.push(...chunkResults);
}
return results;
}
// 사용
const items = Array.from({ length: 10 }, (_, i) => ({ id: i + 1 }));
const results = await processItemsWithLimit(items, 3);
// 3개씩 병렬로 처리: [1,2,3] → [4,5,6] → [7,8,9] → [10]패턴 4: 데이터 캐싱
// 간단한 캐시 구현
class DataCache {
constructor(ttl = 60000) { // 기본 1분
this.cache = new Map();
this.ttl = ttl;
}
async get(key, fetchFunction) {
// 캐시 확인
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.ttl) {
console.log('캐시 히트:', key);
return cached.data;
}
// 캐시 미스 - 데이터 가져오기
console.log('캐시 미스:', key);
const data = await fetchFunction();
// 캐시에 저장
this.cache.set(key, {
data,
timestamp: Date.now()
});
return data;
}
clear() {
this.cache.clear();
}
}
// 사용
const cache = new DataCache(30000); // 30초 TTL
async function getUserData(userId) {
return cache.get(`user-${userId}`, async () => {
console.log(`API 호출: user ${userId}`);
const response = await fetch(`/api/users/${userId}`);
return response.json();
});
}
// 첫 호출: API 요청
const user1 = await getUserData(1); // "API 호출: user 1"
// 30초 이내 재호출: 캐시 사용
const user1Again = await getUserData(1); // "캐시 히트: user-1"패턴 5: 폴링 (Polling)
// 주기적으로 데이터 확인
async function pollUntilComplete(checkFunction, interval = 1000, timeout = 30000) {
const startTime = Date.now();
while (true) {
// 타임아웃 체크
if (Date.now() - startTime > timeout) {
throw new Error('타임아웃: 작업이 완료되지 않았습니다');
}
// 상태 확인
const result = await checkFunction();
if (result.completed) {
console.log('작업 완료!');
return result.data;
}
console.log('대기 중... 진행률:', result.progress);
// 대기 후 재시도
await new Promise(resolve => setTimeout(resolve, interval));
}
}
// 사용 예시: 파일 처리 상태 확인
async function waitForFileProcessing(fileId) {
return pollUntilComplete(
async () => {
const response = await fetch(`/api/files/${fileId}/status`);
const status = await response.json();
return {
completed: status.state === 'completed',
progress: status.progress,
data: status.result
};
},
2000, // 2초마다 체크
60000 // 최대 60초
);
}
// 실행
try {
const result = await waitForFileProcessing('file-123');
console.log('처리 결과:', result);
} catch (error) {
console.error('처리 실패:', error);
}자주 하는 실수와 해결법
실수 1: await 누락
// ❌ 잘못된 코드
async function getData() {
const data = fetchData(); // await 누락!
console.log(data); // Promise 객체가 출력됨
return data;
}
// ✅ 올바른 코드
async function getData() {
const data = await fetchData();
console.log(data); // 실제 데이터 출력
return data;
}실수 2: 불필요한 순차 처리
// ❌ 비효율적 (6초)
async function loadData() {
const user = await fetchUser(); // 2초
const posts = await fetchPosts(); // 2초
const stats = await fetchStats(); // 2초
return { user, posts, stats };
}
// ✅ 효율적 (2초)
async function loadData() {
const [user, posts, stats] = await Promise.all([
fetchUser(), // 동시 실행
fetchPosts(), // 동시 실행
fetchStats() // 동시 실행
]);
return { user, posts, stats };
}실수 3: 에러 처리 누락
// ❌ 에러 처리 없음
async function processData() {
const data = await fetchData();
return data.process(); // fetchData나 process가 실패하면?
}
// ✅ 적절한 에러 처리
async function processData() {
try {
const data = await fetchData();
if (!data) {
throw new Error('데이터가 없습니다');
}
return data.process();
} catch (error) {
console.error('처리 중 오류:', error);
return null; // 또는 기본값 반환
}
}실수 4: forEach에서 await 사용
// ❌ 작동하지 않음
async function processItems(items) {
items.forEach(async (item) => {
await processItem(item); // 제대로 기다리지 않음!
});
console.log('완료'); // 실제로는 아직 진행 중
}
// ✅ 올바른 방법 1: for...of
async function processItems(items) {
for (const item of items) {
await processItem(item);
}
console.log('완료');
}
// ✅ 올바른 방법 2: Promise.all (병렬)
async function processItems(items) {
await Promise.all(
items.map(item => processItem(item))
);
console.log('완료');
}실수 5: Promise 체인에서 에러 전파 누락
// ❌ 에러가 숨겨짐
fetchData()
.then(data => {
processData(data); // 에러 발생 시 무시됨
});
// ✅ 에러 처리
fetchData()
.then(data => {
return processData(data); // return 필수!
})
.catch(error => {
console.error('오류:', error);
});성능 최적화 팁
1. 필요한 것만 await
// ❌ 불필요한 await
async function example() {
await console.log('Hello'); // console.log은 동기 함수!
const result = await 42; // 일반 값에 await 불필요
}
// ✅ 필요한 곳에만
async function example() {
console.log('Hello');
const data = await fetchData(); // 비동기 함수만 await
return data;
}2. Promise.all로 병렬화
// ❌ 순차 처리 (느림)
const user = await fetchUser(1);
const settings = await fetchSettings(1);
const stats = await fetchStats(1);
// ✅ 병렬 처리 (빠름)
const [user, settings, stats] = await Promise.all([
fetchUser(1),
fetchSettings(1),
fetchStats(1)
]);3. 조기 반환 (Early Return)
// ❌ 불필요한 중첩
async function getData(id) {
try {
const data = await fetchData(id);
if (data) {
return data;
} else {
return null;
}
} catch (error) {
return null;
}
}
// ✅ 조기 반환
async function getData(id) {
try {
const data = await fetchData(id);
if (!data) return null;
return data;
} catch (error) {
return null;
}
}실전 종합 예제
전자상거래 장바구니 시스템
class ShoppingCart {
constructor() {
this.items = [];
this.cache = new Map();
}
// 상품 정보 조회 (캐싱 포함)
async getProductInfo(productId) {
// 캐시 확인
if (this.cache.has(productId)) {
console.log(`캐시 사용: ${productId}`);
return this.cache.get(productId);
}
// API 호출
try {
const response = await fetch(`/api/products/${productId}`);
if (!response.ok) {
throw new Error(`상품 조회 실패: ${response.status}`);
}
const product = await response.json();
// 캐시에 저장
this.cache.set(productId, product);
return product;
} catch (error) {
console.error('상품 조회 오류:', error);
throw error;
}
}
// 장바구니에 상품 추가
async addItem(productId, quantity = 1) {
try {
// 상품 정보 확인
const product = await this.getProductInfo(productId);
// 재고 확인
if (product.stock < quantity) {
throw new Error('재고가 부족합니다');
}
// 기존 항목 찾기
const existingItem = this.items.find(item => item.productId === productId);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({
productId,
name: product.name,
price: product.price,
quantity
});
}
console.log(`추가됨: ${product.name} x ${quantity}`);
return true;
} catch (error) {
console.error('추가 실패:', error.message);
return false;
}
}
// 전체 가격 계산 (병렬 처리)
async calculateTotal() {
if (this.items.length === 0) {
return 0;
}
// 모든 상품의 최신 가격을 동시에 조회
const prices = await Promise.all(
this.items.map(async (item) => {
const product = await this.getProductInfo(item.productId);
return product.price * item.quantity;
})
);
// 총합 계산
return prices.reduce((sum, price) => sum + price, 0);
}
// 주문 처리
async checkout(userId) {
try {
console.log('결제 시작...');
// 1. 총액 계산
const total = await this.calculateTotal();
console.log(`총액: ${total}원`);
// 2. 재고 확인 (병렬)
const stockChecks = await Promise.all(
this.items.map(async (item) => {
const product = await this.getProductInfo(item.productId);
return {
productId: item.productId,
available: product.stock >= item.quantity
};
})
);
// 재고 부족 확인
const unavailable = stockChecks.filter(check => !check.available);
if (unavailable.length > 0) {
throw new Error('일부 상품의 재고가 부족합니다');
}
// 3. 결제 처리
const paymentResult = await this.processPayment(userId, total);
if (!paymentResult.success) {
throw new Error('결제 실패');
}
// 4. 주문 생성
const order = await this.createOrder(userId, this.items, total);
// 5. 장바구니 비우기
this.items = [];
console.log('주문 완료:', order.id);
return order;
} catch (error) {
console.error('결제 실패:', error.message);
throw error;
}
}
// 결제 처리 (재시도 로직 포함)
async processPayment(userId, amount, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
console.log(`결제 시도 ${attempt}/${maxRetries}`);
const response = await fetch('/api/payments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, amount })
});
if (!response.ok) {
throw new Error(`결제 API 오류: ${response.status}`);
}
const result = await response.json();
console.log('결제 성공');
return result;
} catch (error) {
lastError = error;
console.error(`시도 ${attempt} 실패:`, error.message);
if (attempt < maxRetries) {
// 재시도 전 대기 (지수 백오프)
const delay = Math.pow(2, attempt - 1) * 1000;
console.log(`${delay}ms 후 재시도...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw new Error(`결제 실패: ${lastError.message}`);
}
// 주문 생성
async createOrder(userId, items, total) {
const response = await fetch('/api/orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
userId,
items,
total,
timestamp: new Date().toISOString()
})
});
if (!response.ok) {
throw new Error('주문 생성 실패');
}
return response.json();
}
}
// 사용 예시
async function example() {
const cart = new ShoppingCart();
// 상품 추가
await cart.addItem('product-1', 2);
await cart.addItem('product-2', 1);
await cart.addItem('product-3', 3);
// 총액 계산
const total = await cart.calculateTotal();
console.log('장바구니 총액:', total);
// 결제
try {
const order = await cart.checkout('user-123');
console.log('주문 번호:', order.id);
} catch (error) {
console.error('결제 실패:', error.message);
}
}디버깅 팁
1. async 함수는 항상 Promise를 반환
async function test() {
return 42;
}
console.log(test()); // Promise { 42 }
test().then(value => console.log(value)); // 422. console.log로 Promise 상태 확인
async function debug() {
const promise = fetchData();
console.log('Promise 객체:', promise); // Promise { <pending> }
const data = await promise;
console.log('실제 데이터:', data); // 실제 값
}3. 에러 스택 추적
async function trackError() {
try {
await problematicFunction();
} catch (error) {
console.error('에러 메시지:', error.message);
console.error('스택 트레이스:', error.stack);
}
}브라우저 호환성
| 기능 | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Promise | 32+ | 29+ | 8+ | 12+ |
| async/await | 55+ | 52+ | 11+ | 15+ |
| Promise.allSettled | 76+ | 71+ | 13+ | 79+ |
| Promise.any | 85+ | 79+ | 14+ | 85+ |
참고: 구형 브라우저를 지원해야 한다면 Babel 을 사용하세요.
다음 단계
이제 Promise와 async/await의 기초를 마스터했습니다! 다음 주제로 넘어가세요:
- Web Workers와 병렬 처리 - CPU 집약적 작업 최적화
- RxJS로 리액티브 프로그래밍 - 복잡한 비동기 스트림 관리
- 서비스 워커와 오프라인 처리 - PWA 구축
참고 자료
공식 문서
추가 학습
도구
작성일: 2025-11-30
버전: 1.0.0
작성자: Claude with technical-writing skill