자바스크립트에서 비동기 프로그래밍은 웹 애플리케이션에서 중요한 역할을 합니다. 비동기 프로그래밍을 통해 여러 작업을 동시에 수행하면서도 응답성과 성능을 유지할 수 있습니다. 자바스크립트는 비동기 프로그래밍을 지원하기 위해 여러 가지 메커니즘과 패턴을 제공합니다. 여기서는 콜백, 프라미스, async/await와 같은 주요 비동기 프로그래밍 개념과 기법에 대해 상세히 설명하겠습니다.
1. 콜백 (Callback)
콜백 함수는 다른 함수에 인수로 전달되어 특정 작업이 완료된 후 호출되는 함수입니다. 비동기 작업에서 콜백은 결과를 처리하는 데 자주 사용됩니다.
1.1 기본 콜백 예제
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: 'John Doe' };
callback(data);
}, 1000);
}
function handleData(data) {
console.log('Data received:', data);
}
fetchData(handleData);
fetchData
함수는 1초 후에 콜백 함수를 호출하여 데이터를 전달합니다.handleData
함수는 데이터를 받아서 콘솔에 출력합니다.
1.2 콜백 지옥 (Callback Hell)
복잡한 비동기 작업을 중첩된 콜백으로 처리하면 코드가 복잡해지고 가독성이 떨어집니다. 이를 콜백 지옥이라고 합니다.
function fetchData(url, callback) {
setTimeout(() => {
const data = { id: 1, name: 'John Doe' };
callback(null, data);
}, 1000);
}
fetchData('api/data1', (err, data1) => {
if (err) {
console.error(err);
return;
}
fetchData('api/data2', (err, data2) => {
if (err) {
console.error(err);
return;
}
fetchData('api/data3', (err, data3) => {
if (err) {
console.error(err);
return;
}
console.log('All data received:', data1, data2, data3);
});
});
});
2. 프라미스 (Promise)
프라미스는 비동기 작업의 완료 또는 실패를 처리하기 위한 객체입니다. 프라미스는 콜백 지옥 문제를 해결하고 코드의 가독성을 높입니다.
2.1 프라미스 생성
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // 성공 여부를 나타내는 변수
if (success) {
resolve('Data received');
} else {
reject('Error occurred');
}
}, 1000);
});
myPromise
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
myPromise
는 1초 후에 성공하면resolve
를 호출하고, 실패하면reject
를 호출합니다.then
메서드는 프라미스가 성공할 때 호출되며,catch
메서드는 프라미스가 실패할 때 호출됩니다.
2.2 여러 프라미스 처리
여러 비동기 작업을 프라미스를 사용하여 순차적으로 처리하거나 병렬로 처리할 수 있습니다.
2.2.1 순차 처리
function fetchData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { url, data: 'Sample Data' };
resolve(data);
}, 1000);
});
}
fetchData('api/data1')
.then(data1 => {
console.log(data1);
return fetchData('api/data2');
})
.then(data2 => {
console.log(data2);
return fetchData('api/data3');
})
.then(data3 => {
console.log(data3);
})
.catch(error => {
console.error(error);
});
2.2.2 병렬 처리
Promise.all([fetchData('api/data1'), fetchData('api/data2'), fetchData('api/data3')])
.then(results => {
console.log('All data received:', results);
})
.catch(error => {
console.error(error);
});
Promise.all
메서드는 전달된 모든 프라미스가 완료되면 결과를 배열로 반환합니다.- 하나라도 실패하면
catch
블록이 실행됩니다.
3. Async/Await
Async/Await는 프라미스를 사용한 비동기 코드를 더 간단하고 동기 코드처럼 작성할 수 있게 해줍니다. async
키워드를 사용하여 함수를 비동기로 만들고, await
키워드를 사용하여 프라미스가 해결될 때까지 기다릴 수 있습니다.
3.1 기본 Async/Await 예제
async function fetchData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { url, data: 'Sample Data' };
resolve(data);
}, 1000);
});
}
async function handleData() {
try {
const data1 = await fetchData('api/data1');
console.log(data1);
const data2 = await fetchData('api/data2');
console.log(data2);
const data3 = await fetchData('api/data3');
console.log(data3);
} catch (error) {
console.error(error);
}
}
handleData();
handleData
함수는 비동기 함수로 정의되었으며,await
키워드를 사용하여 프라미스가 완료될 때까지 기다립니다.try...catch
블록을 사용하여 에러를 처리합니다.
3.2 병렬 처리
Async/Await를 사용하여 병렬로 프라미스를 처리할 수도 있습니다.
async function handleData() {
try {
const [data1, data2, data3] = await Promise.all([
fetchData('api/data1'),
fetchData('api/data2'),
fetchData('api/data3')
]);
console.log('All data received:', data1, data2, data3);
} catch (error) {
console.error(error);
}
}
handleData();
Promise.all
을 사용하여 병렬로 프라미스를 처리하고 결과를 배열로 반환받습니다.
4. 비동기 프로그래밍의 실제 사례
비동기 프로그래밍은 주로 네트워크 요청, 파일 I/O, 타이머 등을 처리할 때 사용됩니다.
4.1 네트워크 요청
async function fetchUserData(userId) {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error fetching user data:', error);
}
}
fetchUserData(1);
fetch
API를 사용하여 사용자 데이터를 비동기적으로 가져옵니다.await
를 사용하여 네트워크 요청과 JSON 변환이 완료될 때까지 기다립니다.
4.2 파일 I/O (Node.js)
const fs = require('fs').promises;
async function readFileContent(filePath) {
try {
const data = await fs.readFile(filePath, 'utf8');
console.log(data);
} catch (error) {
console.error('Error reading file:', error);
}
}
readFileContent('example.txt');
- Node.js의
fs
모듈을 사용하여 파일을 비동기적으로 읽습니다. await
를 사용하여 파일 읽기 작업이 완료될 때까지 기다립니다.
결론
자바스크립트의 비동기 프로그래밍은 웹 애플리케이션의 성능과 응답성을 향상시키는 데 필수적입니다. 콜백, 프라미스, async/await와 같은 다양한 방법을 사용하여 비동기 작업을 효율적으로 처리할 수 있습니다. 특히, async/await는 코드의 가독성을 높이고 에러 처리를 단순화하여 비동기 프로그래밍을 더 쉽게 만듭니다. 이러한 비동기 프로그래밍 기법을 잘 이해하고 활용하면 더 나은 자바스크립트 애플리케이션을 개발할 수 있습니다.