# 프로미스, 비동기 처리를 더 깔끔하게
## 미션
프로미스(Promise)를 활용하여, 콜백지옥을 개선하시오.
## 개념
콜백을 무분별하게 중첩할 경우, 문제가 생길 수 있다.
이를 해결하기 위한 객체가 바로 프로미스다.
### 중첩된 콜백의 문제점
콜백은 중첩되면 될 수록 더럽다. 스파게티 코드가 된다.
다음 코드를 보라. 대표적인 스파게티 코드라 하겠다.
이러한 코드를 콜백 지옥(callback hell)이라 한다.
```
getData(function(data) {
getMoreData(data, function(moreData) {
getMoreDataAgain(moreData, function(evenMoreData) {
displayData(evenMoreData);
});
});
});
```
### 프로미스의 등장
더러운 콜백지옥을 뿌시자. 프로미스를 사용해서!
프로미스를 사용하면, 콜백지옥을 다음과 같이 개선할 수 있다.
```
getData()
.then(function(data) {
return getMoreData(data);
})
.then(function(moreData) {
return getMoreDataAgain(moreData);
})
.then(function(evenMoreData) {
displayData(evenMoreData);
})
.catch(function(error) {
console.error(error);
});
```
### 프로미스 객체와 상태
프로미스는 객체다. 비동기 작업을 수행할 객체.
프로미스가 비동기 처리에 성공하면, resolve 콜백이 수행된다.
실패하는 경우, reject 콜백이 호출된다.
이러한 동작을 위해, 프로미스는 상태값을 갖는다.
- 대기(pending): 아직 비동기 처리가 수행되지 않음
- 이행(fulfilled): 비동기 처리에 성공
- 거부(rejected): 비동기 처리에 실패
### 프로미스 예제 코드
다음은 paymentAPI() 함수는, 결제를 위해 프로미스를 활용한 예다.
반반 확률로 성공 또는 실패하는 프로미스를 반환한다.
```
function paymentAPI(amount) {
return new Promise((resolve, reject) => {
// 50% 확률로 결제 성공
const isSuccess = Math.random() < 0.5;
// 비동기 처리 수행
setTimeout(() => {
if (isSuccess) {
resolve(`[결제 완료] ${amount}원`);
} else {
reject('[결제 실패] 잔액이 부족합니다');
}
}, 1000); // 1초 후에 결제 결과 반환
});
};
```
### 프로미스 호출 예
실제 호출 코드는 다음과 같이 작성했다.
9900입력으로 함수를 호출하면, 프로미스 객체가 반환된다.
그러면 1초뒤에 성공 또는 실패한다.
```
// 사용 예
paymentAPI(9900)
.then((result) => {
console.log(result); // [결제 완료] 9900원
})
.catch((error) => {
console.log(error); // [결제 실패] 잔액이 부족합니다
});
```
성공했다면 resolve() 콜백이 호출되는데,
해당 콜백은 then()절 내부에 작성되어있다.
따라서 "[결제 완료] 9900"원이 출력된다.
실패했다면 reject() 콜백에 의해,
catch()절 내부에 작성된 콜백이 수행된다.
따라서 "[결제 실패] 잔액이 부족합니다"가 출력된다.
## 실습
### 기본파일 작성
promise.html
```
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Promise</title>
<!-- custom -->
<link rel="stylesheet" href="promise.css">
<!-- bootstrap 5.2 -->
<link href="https://cdn.jsdelivr.net/npm/
[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<Script defer src="https://cdn.jsdelivr.net/npm/
[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body class="container">
<h1 id="heading">프로미스</h1>
<article id="practice-1">
<h2>배달앱 주문부터 식사까지</h2>
<p>야심한 밤, 배가고픈 홍팍. 그는 치킨이 먹고 싶었다..</p>
<ul class="list-group">
<li class="list-group-item">1. 배달앱 주문</li>
<li class="list-group-item">2. 결제하기</li>
<li class="list-group-item">3. 배달도착</li>
<li class="list-group-item">4. 리뷰작성</li>
</ul>
</article>
</body>
</html>
```
promise.css
```
/*
* CSS 관련 구글링
* 👉 site:developer.mozilla.org {연관_키워드}
*/
body {
padding-top: 1rem;
}
article {
padding-top: 1rem;
padding-bottom: 1rem;
}
```
promise.js
```
// JavaScript 관련 구글링
// 👉 site:developer.mozilla.org {연관_키워드}
'use strict';
```
### 콜백지옥이란
```
// 1. 콜백 지옥(callback hell)
// - 콜백 함수가 또 다른 콜백을 부르는 상황
// - 비동기 처리흐름 파악이 어려움
// 주문하기
function orderAPI(doNext) {
setTimeout(() => {
console.log("[주문] 완료!");
doNext();
}, 1000);
}
// 결제하기
function paymentAPI(doNext) {
setTimeout(() => {
console.log("[결제] 완료!");
doNext();
}, 1000);
}
// 배달하기
function deliveryAPI(doNext) {
setTimeout(() => {
console.log("[배달] 완료!");
doNext();
}, 1000);
}
// 리뷰작성
function reviewAPI(doNext) {
setTimeout(() => {
console.log("[리뷰] 완료!");
doNext();
}, 1000);
}
// 콜백을 활용한 비동기처리: 콜백 지옥 발생..
// 코드 파악이 어려움: 코드 흐름이 눈에 잘 들어오지 않음
orderAPI(() => {
paymentAPI(() => {
deliveryAPI(() => {
reviewAPI(() => {
console.log("== END ==");
});
});
});
});
```
### 프로미스 개요
```
// 2. 프로미스(Promise)
// - 콜백 지옥을 개선하는 객체
// - 비동기 처리 흐름을 파악하기 좋음
// 👉 https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise
// 프로미스 만들기
const promise1 = new Promise((resolve, reject) => {
// 비동기 처리 작업..
setTimeout(() => {
if (Math.random() < 0.5) {
resolve("성공");
} else {
reject("실패");
}
});
});
console.log(promise1);
// 프로미스 호출
promise.then((result) => { console.log(result) })
.catch((err) => { console.log(err) })
.finally(() => { console.log("== 끝 ==") });
```
### 프로미스로, 콜백지옥 개선하기
```
// 3. 기존 콜백을 프로미스로 개선하기
// - 콜백 지옥은 기존 콜백을 직접호출해서 생김
// - 프로미스로 감싸면, 콜백 지옥을 없앨 수 있음
// 주문하기
function orderAPI() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.8) {
console.log("[주문] 완료!");
resolve();
} else {
reject("[주문] 실패..");
}
}, 1000);
});
}
// 결제하기
function paymentAPI() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.8) {
console.log("[결제] 완료!");
resolve();
} else {
reject("[결제] 실패..");
}
}, 1000);
});
}
// 배달하기
function deliveryAPI() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.8) {
console.log("[배달] 완료!");
resolve();
} else {
reject("[배달] 실패..");
}
}, 1000);
});
}
// 리뷰작성
function reviewAPI() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.8) {
console.log("[리뷰] 완료!");
resolve();
} else {
reject("[리뷰] 실패..");
}
}, 1000);
});
}
// "주문-결제-배달-리뷰"를 프로미스로 개선하여 호출
orderAPI().then(() => { return paymentAPI() })
.then(() => { return deliveryAPI() })
.then(() => { return reviewAPI() })
.catch((err) => { console.log(err) })
.finally(() => { console.log("== 끝 ==")});
```
## 구글링 학습 🔥
- 자바스크립트 콜백지옥
- 자바스크립트 프로미스란
- 자바스크립트 프로미스 상태값
- 자바스크립트 프로미스 resolve와 reject
- 자바스크립트 프로미스 예외처리