실전 비동기 프로그래밍 패턴
2025. 3. 30. 22:45ㆍWeb Development/JavaScript
실전 비동기 프로그래밍 패턴
타임아웃 처리
Promise가 지정된 시간 내에 완료되지 않으면 거부되도록 타임아웃을 설정할 수 있습니다.
function fetchWithTimeout(url, options = {}, timeout = 5000) {
return new Promise((resolve, reject) => {
// 주 Promise
const fetchPromise = fetch(url, options)
.then(resolve)
.catch(reject);
// 타임아웃 Promise
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`요청이 ${timeout}ms 후에 시간 초과되었습니다.`));
}, timeout);
});
// 둘 중 먼저 완료되는 것 선택
Promise.race([fetchPromise, timeoutPromise]);
});
}
// 사용 예시
async function fetchData() {
try {
const response = await fetchWithTimeout('https://api.example.com/data', {}, 3000);
const data = await response.json();
console.log('데이터:', data);
} catch (error) {
console.error('오류:', error.message);
}
}
재시도 메커니즘
일시적인 오류가 발생할 경우 몇 번의 재시도를 수행하는 패턴입니다.
async function fetchWithRetry(url, options = {}, maxRetries = 3, retryDelay = 1000) {
let lastError;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP 오류! 상태: ${response.status}`);
}
return response; // 성공 시 즉시 반환
} catch (error) {
console.warn(`시도 ${attempt + 1}/${maxRetries} 실패:`, error.message);
lastError = error;
if (attempt < maxRetries - 1) {
// 다음 시도 전에 지연
await new Promise(resolve => setTimeout(resolve, retryDelay));
// 지수 백오프 (선택 사항)
retryDelay *= 2;
}
}
}
throw new Error(`${maxRetries}번 시도 후 실패: ${lastError.message}`);
}
// 사용 예시
async function getData() {
try {
const response = await fetchWithRetry('https://api.example.com/data', {}, 3, 1000);
const data = await response.json();
console.log('성공적으로 데이터를 가져왔습니다:', data);
} catch (error) {
console.error('모든 재시도 실패:', error.message);
}
}
비동기 반복 및 제어
배열의 요소를 비동기적으로 처리하는 다양한 방법입니다.
// 배열 요소 순차 처리
async function processSequentially(items) {
const results = [];
for (const item of items) {
// 각 항목을 순차적으로 처리
const result = await processItem(item);
results.push(result);
}
return results;
}
// 배열 요소 병렬 처리
async function processInParallel(items) {
// 모든 프로미스를 동시에 시작하고 결과 기다림
const results = await Promise.all(items.map(item => processItem(item)));
return results;
}
// 병렬 처리 (동시 실행 수 제한)
async function processWithConcurrencyLimit(items, concurrency = 3) {
const results = [];
const inProgress = new Set();
for (const item of items) {
// 처리를 시작하고 프로미스를 추적
const promise = (async () => {
const result = await processItem(item);
// 완료 시 진행 중인 작업에서 제거
inProgress.delete(promise);
return result;
})();
inProgress.add(promise);
results.push(promise);
// 동시 실행 수가 제한에 도달하면 기다림
if (inProgress.size >= concurrency) {
await Promise.race(inProgress);
}
}
// 모든 결과 반환
return Promise.all(results);
}
캐싱 및 메모이제이션
동일한 비동기 작업 결과를 캐싱하여 성능을 향상시킬 수 있습니다.
// 간단한 비동기 메모이제이션 함수
function memoizeAsync(fn) {
const cache = new Map();
return async function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log('캐시에서 결과 반환:', key);
return cache.get(key);
}
console.log('함수 실행 및 결과 캐싱:', key);
const result = await fn(...args);
cache.set(key, result);
return result;
};
}
// 사용 예시
const fetchUserDataCached = memoizeAsync(async (userId) => {
// 실제로는 API 호출 등 비용이 큰 작업
const response = await fetch(`https://api.example.com/users/${userId}`);
return response.json();
});
// 첫 번째 호출 - API 호출
await fetchUserDataCached(1);
// 두 번째 호출 - 캐시에서 반환
await fetchUserDataCached(1);
// 다른 ID로 호출 - 새 API 호출
await fetchUserDataCached(2);
에러 처리 및 복구
강건한 비동기 코드를 위한 에러 처리 및 복구 패턴입니다.
// 백오프 재시도 + 폴백(fallback) 패턴
async function fetchWithRetryAndFallback(url, options = {}, maxRetries = 3) {
let retryCount = 0;
let lastError;
// 재시도 로직
while (retryCount < maxRetries) {
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP 오류: ${response.status}`);
}
return await response.json();
} catch (error) {
lastError = error;
console.warn(`시도 ${retryCount + 1}/${maxRetries} 실패:`, error.message);
retryCount++;
if (retryCount < maxRetries) {
// 지수 백오프
const delay = Math.pow(2, retryCount) * 1000 + Math.random() * 1000;
console.log(`${delay}ms 후 재시도...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
// 모든 시도 실패 시 폴백 동작
console.error("모든 재시도 실패. 폴백 데이터 사용:", lastError);
return getFallbackData(url); // 캐시된 데이터 또는 기본값 반환
}
function getFallbackData(url) {
// URL에 따라 적절한 폴백 데이터 제공
if (url.includes('/users')) {
return { error: true, fallback: true, users: [] };
}
if (url.includes('/products')) {
return { error: true, fallback: true, products: [] };
}
return { error: true, fallback: true, data: null };
}
'Web Development > JavaScript' 카테고리의 다른 글
최신 JavaScript 문법 (0) | 2025.03.30 |
---|---|
모듈 시스템 (0) | 2025.03.30 |
async/await (0) | 2025.03.30 |
Promise (0) | 2025.03.30 |
콜백 함수 (0) | 2025.03.30 |