비동기 자바스크립트 완벽 가이드: 프로미스와 Async/Await의 이해
2024-10-07 14:45:21비동기 자바스크립트의 이해
자바스크립트의 단일 스레드 특성과 비동기 처리
자바스크립트는 단일 스레드로 작동하기 때문에 한 번에 하나의 작업만 실행할 수 있습니다. 그러나 API 데이터 가져오기, 파일 읽기, 사용자 입력 처리와 같은 비동기 작업은 메인 스레드를 차단하지 않고 사용자 경험을 향상시킬 수 있습니다. 초기의 백그라운드 작업들은 콜백을 통해 처리되었는데, 이로 인해 '콜백 지옥'이라는 비극이 발생하기도 했습니다.
프로미스: 비동기 처리를 향한 진전
프로미스란 무엇인가?
프로미스(Promise)는 ES6에서 소개되었으며, 비동기 작업의 성공 또는 실패를 나타내는 객체입니다. 이는 콜백에 비해 구조화된 방식으로 비동기 작업을 관리할 수 있도록 돕습니다. 프로미스는 다음 세 가지 상태를 가질 수 있습니다.
- 대기(Pending): 작업이 아직 완료되지 않은 상태
- 이행(Fulfilled): 작업이 성공적으로 완료된 상태
- 거부(Rejected): 작업이 실패한 상태
프로미스 생성 방법
새로운 프로미스를 생성하려면 resolve와 reject 두 개의 인수를 받는 함수를 전달해야 합니다. 함수 내에서 게시는 비동기 작업을 수행하고, 그 결과에 따라 resolve(성공시) 또는 reject(실패시)를 호출합니다.
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { name: '이샨', age: 25 };
resolve(data);
}, 1000);
});
};
위의 예제에서는 fetchData 함수가 1초 후에 데이터를 반환하는 프로미스를 생성합니다.
프로미스 소비하기
프로미스의 결과를 처리하려면 .then() 메서드를 통해 성공적인 결과를, .catch() 메서드를 통해 오류를 처리할 수 있습니다.
fetchData()
.then(data => {
console.log(data); // { name: '이샨', age: 25 }
})
.catch(error => {
console.error('Error:', error);
});
프로미스 체이닝
프로미스의 강력한 기능 중 하나는 여러 비동기 작업을 연쇄적으로 구현할 수 있다는 점입니다. 한 .then()의 결과는 다음 .then()로 전달될 수 있습니다.
fetchData()
.then(data => {
return data.name.toUpperCase(); // 데이터를 수정
})
.then(upperName => {
console.log(upperName); // 'ISHAN'
})
.catch(error => {
console.error('Error:', error);
});
프로미스 조합기
프로미스는 여러 비동기 작업을 동시에 처리할 수 있는 내장된 조합기 메서드를 제공합니다.
Promise.all(): 모든 프로미스가 완료될 때까지 기다리고, 결과를 배열로 반환합니다. 하나의 프로미스라도 실패하면 전체 프로미스가 실패로 간주됩니다.
Promise.all([fetchData(), fetchData()])
.then(results => console.log(results))
.catch(error => console.error(error));
Promise.race(): 가장 먼저 완료되는 프로미스의 결과를 반환합니다.
Promise.race([fetchData(), fetchData()])
.then(result => console.log(result))
.catch(error => console.error(error));
Async/Await: 프로미스의 구문적 설탕
Async/Await의 사용법
Async/Await는 프로미스를 더 쉽게 사용할 수 있는 문법적 설탕(syntactic sugar)으로, 비동기 코드 작성이 마치 동기 코드처럼 읽히게 해줍니다. 이를 사용하려면 함수에 async 키워드를 추가하고, 프로미스 앞에 await 키워드를 사용합니다.
const fetchData = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ name: '이샨', age: 25 });
}, 1000);
});
};
const getData = async () => {
const data = await fetchData();
console.log(data); // { name: '이샨', age: 25 }
};
getData();
Async/Await의 오류 처리
Async/Await를 사용할 때는 try/catch 문을 활용하여 오류를 처리할 수 있으며, 이는 .catch() 보다 오류 관리를 수월하게 해줍니다.
const getData = async () => {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
};
getData();
프로미스 vs. Async/Await 언제 사용할까?
프로미스와 Async/Await 모두 비동기 작업 처리를 목표로 하지만, 다음과 같은 상황에서 특정 방식이 유리할 수 있습니다.
프로미스를 사용해야 할 때:
Promise.all()또는Promise.race()같은 프로미스 조합기를 사용할 때.- 작은 비동기 작업에서
.then()체인이 관리 가능할 때.
Async/Await를 사용해야 할 때:
- 더 깨끗하고 읽기 쉬운 코드를 원할 때.
- 여러 비동기 작업이 서로의 결과에 의존할 때.
- 오류 처리를 중앙집중식으로 관리해야 할 때.
Async/Await와 프로미스 조합기 혼합하기
Async/Await는 Promise.all() 같은 조합기와 함께 사용할 수 있어, 여러 비동기 작업을 병렬로 실행하면서도 쉽게 코드를 작성할 수 있습니다.
const fetchData1 = () => {
return new Promise(resolve => setTimeout(() => resolve('Data 1'), 1000));
};
const fetchData2 = () => {
return new Promise(resolve => setTimeout(() => resolve('Data 2'), 2000));
};
const getAllData = async () => {
try {
const [data1, data2] = await Promise.all([fetchData1(), fetchData2()]);
console.log(data1, data2); // 'Data 1', 'Data 2'
} catch (error) {
console.error('Error:', error);
}
};
getAllData();
결론
프로미스와 Async/Await는 자바스크립트 비동기 코드 관리를 위한 필수 도구입니다. 프로미스는 비동기 작업을 구조적으로 처리할 수 있게 해주며, Async/Await는 더 깔끔하고 읽기 쉬운 문법을 제공합니다. 이 두 가지 방식을 적재적소에 사용할 줄 아는 것이 더 효율적이고 효과적인 자바스크립트 개발자가 되는 길입니다.