제4.5장: Async/await vs Promise
학습 시간: 30분
1. 비동기성: "우주 통신"을 관리하는 두 가지 방법
지상 관제 센터(ЦУП)가 화성에 명령을 보낸다고 상상해 보세요. 응답은 몇 분 후에나 도착할 것입니다. 이 시간 동안 작업을 어떻게 구성해야 할까요?
방법 1: "콜백 프로토콜" (Promise와 .then()
)
명령을 보내고 지시합니다: "응답이 올 때마다 이 함수를 실행하세요." 이는 이벤트 체인과 유사합니다.
방법 2: "대기 모드" (Async/await) "이 명령에 대한 응답을 기다리겠지만, 다른 제어판을 차단하지는 않을 것입니다." 라고 말하는 것과 같습니다. 이는 이 특정 작업의 실행을 일시 중지하여 나머지 지상 관제 센터가 계속 작동하도록 허용하는 것과 같습니다.
두 가지 방법 모두 비동기 작업 관리라는 동일한 문제를 해결합니다. async/await
는 Promise 위에 구축된 더 현대적이고 읽기 쉬운 구문일 뿐입니다.
💡 우주 비유:
- Promise와
.then()
: 이것은 "탐사선이 사진을 보내면 분석 부서로 전달하라"고 스티커에 쓰는 것과 같습니다.- Async/await: 이것은 조수에게 "탐사선으로부터 사진을 기다려줘. 나는 그동안 새 로켓 발사를 위한 계산을 할게."라고 말하는 것과 같습니다.
2. Promise와 .then()
: 고전적인 명령 체인
이것은 JavaScript에서 비동기성을 다루는 기본적인 방법이며, 우리는 4.1장에서 이를 사용했습니다.
첫 번째 코드를 다시 살펴봅시다:
function getIssPositionWithPromises() {
console.log('"Promise" 프로토콜로 요청을 보내는 중...');
fetch('http://api.open-notify.org/iss-now.json')
.then(response => {
// 단계 1: 응답 수신
if (!response.ok) {
throw new Error(`HTTP 오류: ${response.status}`);
}
return response.json(); // 새 Promise 반환
})
.then(data => {
// 단계 2: 데이터 파싱됨
console.log('"Promise" 프로토콜로 데이터 수신됨:', data.iss_position);
})
.catch(error => {
// 단계 3 (오류): 어느 단계에서든 문제가 발생했습니다
console.error('"Promise" 프로토콜 통신 실패:', error);
});
console.log('...명령이 전송되었습니다, 지상 관제 센터는 계속 작동합니다...');
}
장점:
- 명시적인 작업 체인.
- 간단한 순차적 작업에 적합합니다.
단점:
- "콜백 헬" (Callback Hell): 중첩된 비동기 작업이 많을 경우 코드가 읽기 어려운
.then()
의 "사다리" 형태로 변할 수 있습니다. - 오류 처리가 덜 직관적일 수 있습니다.
3. Async/await: 현대적인 동기식 스타일
async/await
는 Promise 위에 있는 "문법적 설탕"으로, 비동기 코드를 마치 동기 코드처럼 작성할 수 있게 해줍니다.
사용 규칙:
- 키워드
await
는async
로 표시된 함수 내부에서만 사용할 수 있습니다. await
는 Promise를 반환하는 호출(예:fetch()
또는response.json()
) 앞에 놓입니다.await
는 Promise가 해결될 때까지async
함수의 실행을 "일시 중지"하고 그 결과를 반환합니다.
동일한 코드를 async/await
로 다시 작성:
async function getIssPositionWithAsyncAwait() {
console.log('"Async/await" 프로토콜로 요청을 보내는 중...');
try {
// 단계 1: 서버 응답 대기
const response = await fetch('http://api.open-notify.org/iss-now.json');
if (!response.ok) {
throw new Error(`HTTP 오류: ${response.status}`);
}
// 단계 2: 응답 본문이 JSON으로 변환될 때까지 대기
const data = await response.json();
console.log('"Async/await" 프로토콜로 데이터 수신됨:', data.iss_position);
} catch (error) {
// 단계 3 (오류): try 블록에서 발생하는 모든 오류 포착
console.error('"Async/await" 프로토콜 통신 실패:', error);
}
console.log('...명령이 전송되었습니다, 지상 관제 센터는 계속 작동합니다...');
}
장점:
- 가독성: 코드가 거의 일반 동기 코드처럼 보여 위에서 아래로 읽기 쉽습니다.
- 오류 처리: 표준적이고 익숙한
try...catch
블록이 사용됩니다. - 디버깅:
await
가 있는 각 줄에 중단점(breakpoints)을 설정할 수 있어 디버깅이 훨씬 쉽습니다.
단점:
await
또는async
를 잊기 쉬워 오류로 이어질 수 있습니다.
4. 어떤 프로토콜을 사용해야 할까요?
상황 | 권장되는 접근 방식 | 이유? |
---|---|---|
대부분의 경우 | async/await |
코드가 더 깔끔하고 읽고 디버깅하기 쉽습니다. 이것이 현대적인 표준입니다. |
간단한 1-2단계 체인 | Promise와 .then() |
충분히 적합하며 코드가 간결하게 유지됩니다. |
여러 요청의 병렬 실행 | Promise.all() |
이 메서드는 여러 Promise를 동시에 시작하고 모두 완료될 때까지 기다릴 수 있게 합니다. async/await 는 이와 잘 어울립니다. |
Promise.all()
예시:
async function getParallelData() {
try {
// 두 요청을 동시에 시작합니다.
const [shipsResponse, launchesResponse] = await Promise.all([
fetch('https://api.spacexdata.com/v4/rockets'),
fetch('https://api.spacexdata.com/v4/launches/latest')
]);
if (!shipsResponse.ok || !launchesResponse.ok) {
throw new Error('요청 중 하나가 실패했습니다!');
}
const rockets = await shipsResponse.json();
const latestLaunch = await launchesResponse.json();
console.log(`총 함대 로켓 수: ${rockets.length}`);
console.log(`최신 발사: ${latestLaunch.name}`);
} catch (error) {
console.error('병렬 데이터 가져오기 오류:', error);
}
}
개념 확인 퀴즈
🚀 장 요약:
비동기 작업을 관리하기 위한 두 가지 구문을 학습했으며, 대부분의 현대 프로젝트에서 async/await
가 왜 선호되는지 이해했습니다.
- 🔗
Promise
와.then()
에 대한 지식을 복습했습니다. - 🛠️
async/await
의 작동 방식과 그 장점을 깊이 이해했습니다. - ⚡ 병렬 요청을 실행하기 위한
Promise.all
에 대해 알게 되었습니다.
통신 프로토콜 학습 완료! 이 섹션의 마지막 장에서 우리는 모든 지식을 한데 모아 모든 CRUD 작업에 대한 완전한 인터페이스를 생성하여 우리의 "비행 제어 센터"를 완성할 것입니다.
📌 연습 문제:
- 당신의
app.js
파일에서 여전히.then()
을 사용하는 모든 함수를async/await
구문으로 다시 작성하세요.Promise.all()
에 다른 요청을 추가해 보세요(예:https://api.spacexdata.com/v4/starlink
로) 그리고 데이터를 출력하세요.⚠️ 오류 발생 시:
await is only valid in async functions
:await
를 사용하는 함수가async
로 표시되어 있는지 확인하세요.- 변수에
[object Promise]
가 포함됨: 프라미스를 반환하는 함수 앞에await
를 붙이는 것을 잊으셨습니다.