JavaScript는 기본적으로 단일 스레드에서 동작하기 때문에, 비동기 프로그래밍이 매우 중요합니다. 비동기 프로그래밍을 통해 시간 소요가 큰 작업(예: 네트워크 요청, 파일 읽기 등)을 처리하면서도 애플리케이션이 멈추지 않고 동작할 수 있습니다. 이번 글에서는 JavaScript의 비동기 프로그래밍 방법인 콜백(callback), 프로미스(Promise), async/await에 대해 살펴보고, 이들의 사용법과 차이점을 설명하겠습니다.
콜백 (Callback)
콜백 함수란?
콜백 함수는 다른 함수의 인자로 전달되어, 특정 작업이 완료된 후 호출되는 함수입니다. 비동기 작업을 처리하기 위해 널리 사용되었지만, 가독성이 떨어지고 에러 처리가 복잡해지는 단점이 있습니다.
콜백 함수 예제
function fetchData(callback) {
setTimeout(() => {
const data = { name: "John", age: 30 };
callback(data);
}, 1000);
}
function handleData(data) {
console.log("Data received:", data);
}
fetchData(handleData);
위 예제에서 fetchData
함수는 1초 후에 데이터를 가져와 콜백 함수 handleData
를 호출합니다.
콜백 지옥 (Callback Hell)
여러 비동기 작업을 중첩해서 처리할 때 콜백을 사용하면 코드가 복잡해지고 가독성이 떨어지는 문제를 초래합니다. 이를 콜백 지옥이라고 합니다.
function fetchData(callback) {
setTimeout(() => {
const data = { name: "John", age: 30 };
callback(data);
}, 1000);
}
function fetchAdditionalData(data, callback) {
setTimeout(() => {
data.additionalInfo = "Some more info";
callback(data);
}, 1000);
}
fetchData((data) => {
fetchAdditionalData(data, (updatedData) => {
console.log("Updated Data:", updatedData);
});
});
프로미스 (Promise)
프로미스란?
프로미스는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체입니다. 콜백 패턴보다 가독성이 좋고, 에러 처리가 간단합니다. 프로미스는 pending
(대기), fulfilled
(이행), rejected
(거부) 상태를 가집니다.
프로미스 사용법
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { name: "John", age: 30 };
resolve(data);
}, 1000);
});
}
fetchData()
.then((data) => {
console.log("Data received:", data);
})
.catch((error) => {
console.error("Error:", error);
});
위 예제에서 fetchData
함수는 프로미스를 반환하고, then
메서드를 사용하여 데이터를 처리합니다. catch
메서드를 사용하여 에러를 처리할 수 있습니다.
프로미스 체이닝
여러 비동기 작업을 순차적으로 처리할 때 프로미스 체이닝을 사용할 수 있습니다.
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { name: "John", age: 30 };
resolve(data);
}, 1000);
});
}
function fetchAdditionalData(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
data.additionalInfo = "Some more info";
resolve(data);
}, 1000);
});
}
fetchData()
.then(fetchAdditionalData)
.then((updatedData) => {
console.log("Updated Data:", updatedData);
})
.catch((error) => {
console.error("Error:", error);
});
async/await
async/await란?
async
와 await
는 ES2017(ES8)에서 도입된 비동기 프로그래밍을 위한 새로운 문법입니다. 프로미스를 기반으로 하며, 비동기 코드를 동기 코드처럼 작성할 수 있게 해줍니다.
async/await 사용법
async function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { name: "John", age: 30 };
resolve(data);
}, 1000);
});
}
async function fetchAndHandleData() {
try {
const data = await fetchData();
console.log("Data received:", data);
} catch (error) {
console.error("Error:", error);
}
}
fetchAndHandleData();
async/await의 장점
- 가독성: 코드가 동기 코드처럼 보이기 때문에 가독성이 높습니다.
- 에러 처리:
try/catch
블록을 사용하여 에러를 쉽게 처리할 수 있습니다.
async/await 체이닝
여러 비동기 작업을 순차적으로 처리할 때도 async/await
를 사용할 수 있습니다.
async function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { name: "John", age: 30 };
resolve(data);
}, 1000);
});
}
async function fetchAdditionalData(data) {
return new Promise((resolve, reject) => {
setTimeout(() => {
data.additionalInfo = "Some more info";
resolve(data);
}, 1000);
});
}
async function fetchAndHandleData() {
try {
const data = await fetchData();
const updatedData = await fetchAdditionalData(data);
console.log("Updated Data:", updatedData);
} catch (error) {
console.error("Error:", error);
}
}
fetchAndHandleData();
결론
JavaScript에서 비동기 프로그래밍을 처리하는 방법에는 콜백, 프로미스, async/await가 있습니다. 각각의 방법은 장단점이 있으며, 상황에 맞게 선택하여 사용해야 합니다. 콜백은 간단하지만 가독성이 떨어지고, 프로미스는 가독성이 좋고 에러 처리가 간단합니다. async/await는 프로미스를 기반으로 하여, 비동기 코드를 동기 코드처럼 작성할 수 있어 가독성이 매우 높습니다. 이 글을 통해 각 방법의 사용법과 차이점을 이해하고, 비동기 프로그래밍을 효과적으로 처리할 수 있기를 바랍니다.