티스토리 뷰
▶자바스크립트 프로미스(Promise)
지난 글에서 자바스크립트의 비동기 처리 방식의 문제점을 콜백 함수로 해결할 수 있지만
잘못 쓰게 되면 콜백 지옥에 빠질 수도 있으며 이 콜백 지옥을 Promise나 Async를 통해 해결할 수 있다고 했었습니다.
🔎 Promise ?
프로미스는 자바스크립트 비동기 처리에 사용되는 객체입니다.
💡프로미스는 객체이다!
여기서 자바스크립트의 비동기 처리는
'특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트의 특성'
을 의미합니다.
🔎 Promise가 필요한 이유
프로미스는 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용합니다.
일반적으로 웹 애플리케이션을 구현할 때 서버에서 데이터를 요청하고 받아오기 위해 아래와 같은 API를 사용합니다.
router.get('url 주소/products/1', (req, res) => {
// ...
})
위 API가 실행되면 서버에다가 '데이터 하나 보내주세요' 라는 요청을 보내죠/
그런데 여기서 데이터를 받아오기도 전에 마치 데이터를 다 받아온 것 마냥 화면에 데이터를 표시하려고 하면 오류가 발생하거나 빈 화면이 뜹니다.
이와 같은 문제점을 해결하기 위한 방법 중 하나가 프로미스입니다.
💡 "데이터가 도착하면 이걸 실행해!" 하고 싶을 때 promise를 사용하고는 한다!
🔎 프로미스 코드 - 기초!
그럼 프로미스가 어떻게 동작하는지 이해하기 위해 코드로 살펴보겠습니다.
function getData(callbackFunc) {
router.get('url 주소/products/1', (req, res) => {
callbackFunc(res); // 서버에서 받은 데이터 res를 callbackFunc() 함수에 넘겨줌
});
}
getData(tableData => console.log(tableData)); // router.get()의 res 값이 tableData에 전달됨
위 코드는 지정된 url에서 1번 상품 데이터를 받아 오는 코드입니다.
비동기 처리를 위해 프로미스 대신 콜백 함수를 사용했습니다.
위 코드에 프로미스를 적용하면 아래와 같은 코드가 됩니다.
function getData(callback) {
// new Promise() 추가
return new Promise((resolve, reject) => {
router.get('url 주소/products/1', (req, res) => {
// 데이터를 받으면 resolve() 호출
resolve(res);
});
});
}
// getData()의 실행이 끝나면 호출되는 then()
// resolve()의 결과값이 전달됨
// router.get()의 res 값이 tableData에 전달됨
getData().then(tableData => console.log(tableData));
콜백 함수로 처리하던 구조에서 new Promise(), resolve(), then()와 같은 프로미스 API를 사용한 구조로 바뀌었습니다.
여기서 new Promise()는 좀 이해가 가겠는데 resolve(), then()은 차차 알아보도록 합시다!
🔎 프로미스의 3가지 상태(states)
프로미스의 상태는 프로미스의 처리 과정을 의미합니다.
new Promise()로 프로미스를 생성하고 종료될 때까지 3가지 상태를 갖습니다.
- Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
- Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
- Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
1️⃣ Pending (대기)
먼저 아래와 같이 new Promise() 메서드를 호출하면 대기(Pending) 상태가 됩니다.
new Promise();
new Promise() 메서드를 호출할 때 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve, reject 입니다.
new Promise((resolve, reject) => {
// ...
})
2️⃣ Fulfilled (이행)
여기서 콜백 함수의 인자 resolve를 아래와 같이 실행하면 이행(Fulfilled) 상태가 됩니다.
new Promise((resolve, reject) => {
resolve();
})
그리고 이행 상태가 되면 아래와 같이 then()을 이용하여 처리 결과 값을 받을 수 있습니다.
function getData() {
return new Promise((resolve, reject) => {
var data = 100;
resolve(data);
});
}
// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(resolvedData => console.log(resolvedData)); // 100
3️⃣ Rejected (실패)
new Promise() 로 프로미스 객체를 생성하면 콜백 함수 인자로 resolve와 reject를 사용할 수 있다고 했었는데요.
여기서 reject를 아래와 같이 호출하면 실패(Rejected) 상태가 됩니다.
new Promise((resolve, reject) => {
reject();
})
그리고, 실패 상태가 되면 실패한 이유(실패 처리의 결과값)를 catch()로 받을 수 있습니다.
function getData() {
return new Promise((resolve, reject) => {
reject(new Error('Request is failed'));
});
}
getData().then().catch(err => console.log(err));
🔎 프로미스 코드 - 종합!
그럼 위에서 알아본 내용들을 종합하여 간단한 프로미스 코드를 살펴봐 봅시다.
function getData() {
return new Promise((resolve, reject) => {
router.get('url 주소/products/1', (req, res) => {
if (res) {
resolve(res);
} else {
reject(new Error('Request is failed!'));
}
});
});
}
// 위 router.get() 호출 결과에 따라 'res' 또는 'Error' 출력
getData()
.then(data => console.log(data))
.catch(err => console.error(err));
위 코드는 서버에서 제대로 응답을 받아오면 resolve() 메서드를 호출하고,
응답이 없으면 reject() 메서드를 호출하는 코드입니다.
호출된 메서드에 따라 then()이나 catch()로 분기하여 응답 결과 또는 오류를 출력합니다.
🔎 Promise Chaining - 여러 개의 프로미스를 연결해보자
프로미스의 또 다른 특징은 여러 개의 프로미스를 연결하여 사용할 수 있다는 점입니다.
앞 코드에서 then() 메소드를 호출하고 나면 새로운 프로미스 객체가 반환됩니다.
아래와 같이 코딩이 가능합니다.
function getData() {
return new Promise({
// ...
});
}
// then() 으로 여러 개의 프로미스를 연결한 형식
getData()
.then(function(data) {
// ...
})
.then(function() {
// ...
})
.then(function() {
// ...
});
그러면 위의 형식을 참고하여 실제로 돌려볼 수 있는 코드를 살펴보겠습니다.
new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 2000);
})
.then(result => {
console.log(result); // 1
return result + 10;
})
.then(result => {
console.log(result); // 11
return result + 20;
})
.then(result => {
console.log(result); // 31
});
위 코드는 프로미스 객체를 하나 생성하고 setTimeout()을 이용해 2초 후에 resolve()를 호출하는 예제입니다.
resolve()가 호출되면 프로미스가 대기 상태에서 이행 상태로 넘어가기 때문에 첫 번째 .then()의 로직으로 넘어갑니다.
첫 번째 .then()에서는 이행된 결과값 1을 받아서 10을 더한 후 그 다음 .then()으로 넘겨줍니다.
두 번째 .then()에서도 마찬가지로 바로 이어진 프로미스의 결과값 11을 받아서 20을 더하고 다음 .then()으로 넘겨줍니다.
마지막 .then()에서 최종 결과값 31을 출력합니다.
🔎 실무에서 있을 법한 프로미스 연결 사례
실제 웹 서비스에서 있을 법한 사용자 로그인 인증 로직에 프로미스를 여러 개 연결해보겠습니다.
getData(userInfo)
.then(parseValue)
.then(auth)
.then(display);
위 코드는 페이지에 입력된 사용자 정보를 받아와 파싱, 인증 등의 작업을 거치는 코드를 나타내었습니다.
여기서 userInfo는 사용자 정보가 담긴 객체를 의미하고, parseValue, auth, display는 각각 프로미스를 반환해주는 함수라고 가정했습니다. 아래와 같이 말입니다!
var userInfo = {
id: 'test@abc.com',
pw: '****'
};
function parseValue() {
return new Promise({
// ...
});
}
function auth() {
return new Promise({
// ...
});
}
function display() {
return new Promise({
// ...
});
}
이처럼 여러 개의 프로미스를 .then()으로 연결하여 처리할 수 있습니다.
🔎 프로미스의 에러 처리 방법
앞에서 살펴본 프로미스 코드들은 항상 정상적으로 동작한다고 가정하고 작성한 코드인데요.
실제 서비스를 구현하다 보면 네트워크 연결, 서버 문제 등으로 인해 오류가 발생할 수도 있겠죠?
따라서 프로미스의 에러 처리 방법에 대해서도 알고 있어야 합니다!
방법 1️⃣ .then()의 두 번재 인자로 에러를 처리하는 방법
getData().then(
handleSuccess,
handleError
);
방법 2️⃣ catch()를 이용하는 방법
getData().then().catch();
위 2가지 방법 모두 프로미스의 reject() 메서드가 호출되어 실패 상태가 된 경우에 실행됩니다.
간단하게 말해서 프로미스의 로직이 정상적으로 돌아가지 않는 경우 호출되는 겁니다.
function getData() {
return new Promise((resolve, reject) => {
reject('failed');
});
}
// 1. then()의 두 번째 인자로 에러를 처리하는 코드
getData().then(() => {
// ...
}, err => console.log(err));
// 2. catch()로 에러를 처리하는 코드
getData().then().catch(err => console.log(err));
💡 프로미스 에러 처리는 가급적 catch()를 사용하자
앞에서 프로미스 에러 처리 방법 2가지를 알아보았는데요.
가급적이면 catch()로 에러를 처리하는 게 더 효율적입니다!
// then()의 두 번째 인자로는 감지하는 못하는 오류
function getData() {
return new Promise((resolve, reject) => {
resolve('hi');
});
}
getData().then(result => {
console.log(result);
throw new Error('Error in then()'); // Uncaught (in promise) Error: Error in then()
}, err => console.log('then error: ', err));
getData() 함수의 프로미스에서 resolve() 메서드를 호출하여 정상적으로 로직을 처리했지만,
then()의 첫 번째 콜백 함수 내부에서 오류가 나는 경우 오류를 제대로 잡아내지 못합니다.
따라서 아래와 같은 오류가 나는데요.
하지만 똑같은 오류를 catch()로 처리하면 다른 결과나 나옵니다.
function getData() {
return new Promise((resolve, reject) => {
resolve('hi');
});
}
getData().then(result => {
console.log(result);
throw new Error('Error in then()');
}).catch(err => console.log('then error: ', err)); // then error : Error: Error in then()
위 코드의 처리 결과는 다음과 같습니다.
따라서, 더 많은 예외 처리 상황을 위해 프로미스의 끝에 가급적 catch()붙이는 것이 좋습니다!
'프론트엔드 > Javascript' 카테고리의 다른 글
[Javascript] 자바스크립트 async와 await (2) | 2023.01.12 |
---|---|
[Javascript] 자바스크립트 비구조화 할당(destructuring assignment) (2) | 2023.01.11 |
[Javascript] 자바스크립트 비동기 처리와 콜백 함수 (0) | 2023.01.03 |
[Javascript] ⑬ 자바스크립트 var, let, const (1) | 2022.10.03 |
[Javascript] ⑫ 자바스크립트 모듈 (0) | 2022.07.25 |
- Total
- Today
- Yesterday
- 리액트
- 프론트엔드 공부
- CSS
- JSP
- react-query
- 자바
- 머신러닝
- 데이터분석
- 자바스크립트
- frontend
- 프로젝트 회고
- 프론트엔드
- next.js
- 디프만
- 리액트 훅
- 자바스크립트 기초
- 스타일 컴포넌트 styled-components
- TypeScript
- 인프런
- react
- 프론트엔드 기초
- styled-components
- testing
- 타입스크립트
- 파이썬
- HTML
- jest
- 딥러닝
- Python
- rtl
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |