프론트엔드 개발을 하다 보면 비동기 작업을 관리하는 경우가 많습니다. 비동기 작업은 사용자 경험을 향상시키고, 대기 시간을 줄이는 데 필수적입니다. 그러나 비동기 작업을 관리하는 방법은 다양하고, 각 작업의 특성에 맞게 적절한 패턴을 사용하는 것이 중요합니다. 이번 글에서는 비동기 작업을 효과적으로 관리하는 데 도움이 되는 7가지 패턴을 살펴보겠습니다.
1. 콜백 패턴
가장 기본적인 비동기 작업 처리 방법인 콜백 패턴은 함수의 인자로 다른 함수를 전달하여 비동기 작업이 완료된 후 호출되는 방식입니다. 하지만 콜백 지옥(callback hell) 문제로 가독성이 떨어질 수 있습니다.
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: 'Alice' };
callback(data);
}, 1000);
}
fetchData((data) => {
console.log(data); // { id: 1, name: 'Alice' }
});
2. 프로미스(Promise) 패턴
프로미스는 비동기 작업의 결과값을 나타내는 객체로, 성공(resolve) 또는 실패(reject) 상태를 가집니다. 비동기 작업이 완료된 후 .then()과 .catch() 메서드를 사용하여 결과를 처리할 수 있습니다.
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { id: 1, name: 'Bob' };
resolve(data);
}, 1000);
});
}
fetchData()
.then((data) => {
console.log(data); // { id: 1, name: 'Bob' }
})
.catch((error) => {
console.error(error);
});
3. async/await 패턴
ES2017에서 도입된 async/await 패턴은 프로미스를 더 간결하고 가독성 좋게 사용할 수 있도록 도와줍니다. async 함수 내에서 await 키워드를 사용하여 비동기 작업을 처리할 수 있습니다.
async function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: 1, name: 'Charlie' });
}, 1000);
});
}
async function displayData() {
const data = await fetchData();
console.log(data); // { id: 1, name: 'Charlie' }
}
displayData();
4. 병렬 처리 패턴
Promise.all()을 사용하면 여러 개의 프로미스를 병렬로 실행할 수 있습니다. 모든 프로미스가 완료되면 한 번에 결과를 받을 수 있습니다.
function fetchUserData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: 1, name: 'David' });
}, 1000);
});
}
function fetchOrderData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ orderId: 123, product: 'Laptop' });
}, 1500);
});
}
async function fetchAllData() {
const [userData, orderData] = await Promise.all([fetchUserData(), fetchOrderData()]);
console.log(userData, orderData);
}
fetchAllData();
5. 에러 핸들링 패턴
비동기 작업에서 발생할 수 있는 오류를 처리하는 것은 매우 중요합니다. try...catch 문을 사용하여 async/await 패턴에서 에러를 관리할 수 있습니다.
async function fetchDataWithError() {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error('데이터를 가져오는 데 실패했습니다.'));
}, 1000);
});
}
async function displayData() {
try {
const data = await fetchDataWithError();
console.log(data);
} catch (error) {
console.error(error.message); // 데이터 가져오기 실패
}
}
displayData();
6. 지연 처리 패턴
비동기 작업의 실행을 지연시키고 싶을 때는 setTimeout을 이용한 지연 처리 패턴을 사용할 수 있습니다. 이를 통해 특정 시간 후에 비동기 작업을 실행할 수 있습니다.
function delayedFetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: 1, name: 'Eve' });
}, 2000);
});
}
async function fetchDataWithDelay() {
console.log('데이터를 가져옵니다...');
const data = await delayedFetchData();
console.log(data); // { id: 1, name: 'Eve' }
}
fetchDataWithDelay();
7. 취소 가능 비동기 작업 패턴
비동기 작업을 취소할 수 있는 기능도 필요할 때가 있습니다. AbortController를 사용하면 비동기 작업을 취소할 수 있습니다.
const controller = new AbortController();
const signal = controller.signal;
async function fetchDataWithAbort() {
return new Promise((resolve, reject) => {
signal.addEventListener('abort', () => {
reject(new Error('요청이 취소되었습니다.'));
});
setTimeout(() => {
resolve({ id: 1, name: 'Frank' });
}, 2000);
});
}
async function displayData() {
try {
const data = await fetchDataWithAbort();
console.log(data);
} catch (error) {
console.error(error.message); // 요청이 취소되었습니다.
}
}
displayData();
controller.abort(); // 데이터 요청 취소
마무리
비동기 작업 관리는 프론트엔드 개발에서 중요한 부분입니다. 다양한 패턴을 이해하고 적절하게 활용하면, 개발자와 사용자 모두에게 더 나은 경험을 제공할 수 있습니다. 위에서 소개한 7가지 패턴을 바탕으로 비동기 작업을 효과적으로 관리해 보세요.