비동기 프로그래밍은 현대 웹 개발에서 없어서는 안 될 중요한 개념입니다. 특히, 서버 요청, 데이터베이스 접근, 파일 처리 등의 작업을 수행하는 동안 프로그램이 멈추지 않고 원활하게 작동할 수 있도록 합니다. 이번 포스트에서는 자바스크립트에서 사용되는 비동기 프로그래밍의 핵심 개념인 콜백, 프로미스, 그리고 async/await
을 깊이 있게 살펴보겠습니다.
🔹 비동기 프로그래밍이란?
비동기 프로그래밍(Asynchronous Programming)은 코드 실행이 블로킹되지 않고, 특정 작업이 완료될 때까지 기다리지 않도록 설계하는 방식입니다. 이 개념을 이해하려면 동기(Synchronous)와 비동기(Asynchronous)의 차이를 살펴볼 필요가 있습니다.
✅ 동기(Synchronous) vs. 비동기(Asynchronous)
동기 처리 | 비동기 처리 |
---|---|
하나의 작업이 완료될 때까지 다음 작업이 대기함 | 여러 개의 작업이 동시에 진행될 수 있음 |
코드가 순차적으로 실행됨 | 긴 작업이 진행되는 동안 다른 코드도 실행됨 |
네트워크 요청이나 파일 읽기 같은 작업이 느려질 수 있음 | 긴 작업을 별도로 처리하며, UI나 다른 코드가 계속 실행됨 |
예제: 동기 코드
console.log("1. 시작");
console.log("2. 데이터 가져오는 중...");
console.log("3. 데이터 가져오기 완료");
출력 결과:
1. 시작
2. 데이터 가져오는 중...
3. 데이터 가져오기 완료
위 코드는 순차적으로 실행됩니다. 하지만 데이터베이스 조회나 서버 요청처럼 시간이 오래 걸리는 작업이 있다면, 이러한 방식은 프로그램을 멈추게 할 수 있습니다.
예제: 비동기 코드 (setTimeout 사용)
console.log("1. 시작");
setTimeout(() => {
console.log("2. 데이터 가져오기 완료");
}, 2000);
console.log("3. 다음 작업 실행");
출력 결과:
1. 시작
3. 다음 작업 실행
2. 데이터 가져오기 완료 (2초 후)
이처럼 비동기 처리를 하면, 데이터가 로딩되는 동안 다른 작업을 계속할 수 있습니다.
🔹 콜백 함수(Callback)란?
초기 자바스크립트에서는 비동기 작업을 위해 콜백 함수를 사용했습니다. 콜백 함수는 특정 작업이 완료된 후 실행되는 함수입니다.
콜백 함수 예제
function fetchData(callback) {
setTimeout(() => {
callback("데이터를 가져왔습니다!");
}, 2000);
}
fetchData((data) => {
console.log(data);
});
출력 결과:
(2초 후) 데이터를 가져왔습니다!
콜백 방식은 간단하지만, 콜백이 중첩되면 코드가 복잡해지는 콜백 지옥(Callback Hell) 문제가 발생할 수 있습니다.
콜백 지옥 예제
fetchData((data) => {
console.log(data);
fetchData((data2) => {
console.log(data2);
fetchData((data3) => {
console.log(data3);
});
});
});
위 코드는 중첩 구조가 많아질수록 유지보수가 어려워집니다.
🔹 프로미스(Promise)의 등장
콜백 지옥을 해결하기 위해 프로미스(Promise) 개념이 등장했습니다. 프로미스는 비동기 작업이 성공하거나 실패한 후 실행할 동작을 정의할 수 있는 객체입니다.
✅ 프로미스 기본 문법
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("데이터를 성공적으로 가져왔습니다!");
}, 2000);
});
};
fetchData()
.then(data => console.log(data))
.catch(error => console.error("오류 발생:", error));
출력 결과:
(2초 후) 데이터를 성공적으로 가져왔습니다!
Promise의 주요 메서드
.then()
→ 프로미스가 성공(resolve)했을 때 실행.catch()
→ 프로미스가 실패(reject)했을 때 실행.finally()
→ 성공, 실패 여부와 관계없이 실행
✅ 프로미스 체이닝(Promise Chaining)
fetchData()
.then(data => {
console.log(data);
return fetchData();
})
.then(data2 => {
console.log(data2);
})
.catch(error => console.error("오류 발생:", error));
위처럼 .then()
을 여러 개 연결하여, 비동기 작업을 체계적으로 구성할 수 있습니다.
🔹 async/await: 현대적인 비동기 처리
async/await
은 ES2017(ES8)에서 도입된 문법으로, 프로미스를 더 쉽게 다룰 수 있도록 도와줍니다.
✅ async/await 기본 문법
async function getData() {
const data = await fetchData();
console.log(data);
}
getData();
await 키워드
await
을 사용하면 프로미스가 해결될 때까지 기다립니다.await
키워드는async
함수 내부에서만 사용할 수 있습니다.
✅ async/await vs. Promise 체이닝 비교
Promise 체이닝
fetchData()
.then(data => {
console.log(data);
return fetchData();
})
.then(data2 => console.log(data2))
.catch(error => console.error("오류 발생:", error));
async/await 방식
async function getData() {
try {
const data1 = await fetchData();
console.log(data1);
const data2 = await fetchData();
console.log(data2);
} catch (error) {
console.error("오류 발생:", error);
}
}
getData();
async/await
방식은 동기 코드처럼 읽히기 때문에 가독성이 뛰어나며, 디버깅도 쉽습니다.
🔹 async/await의 에러 처리
비동기 코드에서 에러 처리는 필수적입니다. async/await
에서는 try/catch
구문을 사용하여 쉽게 예외 처리를 할 수 있습니다.
async function getDataWithErrorHandling() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error("오류 발생:", error);
}
}
만약 여러 개의 비동기 작업이 있다면, 각각의 await
에 대해 try/catch
를 사용하는 것이 좋습니다.
🔹 결론
- 콜백 함수는 비동기 처리의 기본 개념이지만, 중첩될 경우 유지보수가 어려움
- Promise를 사용하면 비동기 처리를 더 체계적으로 할 수 있지만,
.then()
체이닝이 길어질 경우 복잡해질 수 있음 - async/await는 프로미스를 더욱 직관적이고 깔끔하게 다룰 수 있도록 해줌
- try/catch를 활용하면 안정적인 에러 처리가 가능함
'프로그래밍 > Javascript' 카테고리의 다른 글
CommonJS 모듈 시스템 완벽 가이드: 개념부터 활용까지 (1) | 2025.02.14 |
---|---|
클로저와 렉시컬 스코프: 자바스크립트의 강력한 개념과 실전 활용법 (0) | 2025.02.14 |
자바스크립트 비동기 프로그래밍 완벽 가이드: 프로미스(Promise)부터 async/await까지 (0) | 2025.02.14 |
자바스크립트 비동기 프로그래밍 완벽 가이드: 콜백부터 async/await까지 (0) | 2025.02.14 |
자바스크립트 고급 데이터 타입: WeakMap과 WeakSet의 개념과 실전 활용 (0) | 2025.02.14 |