자바스크립트 비동기 처리: 콜백, 프로미스, 그리고 async/await
비동기 처리란 무엇인가?
**비동기 처리(Asynchronous Processing)**란, 코드가 실행되는 동안 다른 작업을 기다리지 않고 동시에 처리할 수 있도록 하는 방식입니다.
자바스크립트(JavaScript)는 기본적으로 싱글 스레드(Single Thread) 환경이기 때문에, 비동기 로직이 필수적입니다.
예를 들어, 사용자의 버튼 클릭, 서버에서 데이터 받아오기(AJAX), 파일 읽기, 타이머 설정 등은 시간이 오래 걸릴 수 있으므로 비동기 방식으로 처리해야 UI가 멈추지 않고 부드럽게 동작합니다.
콜백 함수 (Callback Function)
콜백 함수란?
콜백 함수는 어떤 함수에 인수로 전달되어 특정 시점에 호출되는 함수입니다. 비동기 작업이 완료되었을 때 실행되도록 예약할 수 있습니다.
예제:
function fetchData(callback) {
setTimeout(() => {
console.log("데이터를 가져왔습니다!");
callback();
}, 1000);
}
fetchData(() => {
console.log("콜백 실행 완료!");
});
단점: 콜백 지옥
콜백을 중첩해서 사용하다 보면 다음과 같이 **“콜백 지옥(Callback Hell)”**에 빠지기 쉽습니다.
login(user, () => {
getProfile(user, () => {
getPosts(user, () => {
showFeed();
});
});
});
코드가 가독성이 떨어지고 유지보수가 어려워지는 문제가 발생합니다.
프로미스(Promise)
프로미스란?
**프로미스(Promise)**는 비동기 작업의 성공(resolve) 또는 실패(reject)를 처리하는 객체입니다. 콜백보다 구조화된 에러 처리와 연결된 체이닝이 가능해 보다 직관적인 코드 작성이 가능합니다.
기본 구조:
const promise = new Promise((resolve, reject) => {
// 비동기 작업
if (성공) {
resolve(결과값);
} else {
reject(에러);
}
});
예제:
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("데이터를 성공적으로 불러왔습니다!");
}, 1000);
});
}
fetchData()
.then(data => {
console.log(data);
})
.catch(err => {
console.error(err);
});
장점:
- 가독성 향상
- 체이닝 가능 (.then().then())
- 에러를 .catch()로 관리 가능
async/await
async/await란?
async/await는 프로미스를 기반으로 만들어진 비동기 처리 문법의 최신 방식입니다.
async 키워드가 붙은 함수 내부에서 await를 사용하면, 해당 작업이 완료될 때까지 기다렸다가 다음 줄을 실행합니다.
사용 예:
async function loadData() {
try {
const data = await fetchData(); // Promise가 반환되어야 함
console.log(data);
} catch (err) {
console.error("에러 발생:", err);
}
}
특징:
- 마치 동기식 코드처럼 읽기 쉽고 직관적
- try/catch로 에러를 처리할 수 있음
- await는 반드시 async 함수 안에서만 사용 가능
세 가지 방식 비교
항목 | 콜백 함수 | 프로미스(Promise) | async/await |
가독성 | 낮음 (콜백 지옥 발생) | 중간 (체이닝 가능) | 높음 (동기식처럼 작성) |
에러 처리 | try-catch 어려움 | .catch() 사용 가능 | try-catch 사용 가능 |
디버깅 편의성 | 어려움 | 중간 | 좋음 |
코드 길이 | 길어짐 | 줄어듦 | 가장 짧음 |
실전 예제: API 호출 시 async/await 활용하기
async function getUserInfo() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
const data = await response.json();
console.log("유저 정보:", data);
} catch (error) {
console.error("에러 발생:", error);
}
}
getUserInfo();
이렇게 작성하면 비동기 요청이 마치 동기 방식처럼 순차적이고 가독성 있게 처리됩니다.
결론: 어떤 방식이 가장 좋을까?
- 콜백 함수는 단순한 작업에는 빠르지만 복잡해지면 지옥을 경험할 수 있음
- 프로미스는 체이닝을 통한 작업 순서 제어가 가능함
- async/await는 가장 최신 방식으로, 가독성과 유지보수성이 가장 뛰어남
현재는 대부분의 프로젝트에서 async/await을 표준으로 채택하고 있습니다. 하지만 모든 방식의 구조를 이해하고 있어야 다양한 상황에 유연하게 대응할 수 있습니다.