예전의 자바스크립트에서 비동기 코드를 작성하는 일은 꽤나 번거로운 작업이었습니다. 비동기 작업을 처리하려면 여러 겹으로 중첩된 콜백 함수를 다뤄야 했고, 코드에 함수가 추가될수록 가독성은 급격히 나빠졌습니다.
이러한 문제를 해결하기 위해 ES6에서 Promise가 등장했습니다. Promise는 자바스크립트에서 비동기 코드를 효율적이고 깔끔하게 작성할 수 있도록 도와주는 기능입니다.
이 가이드에서는 Promise가 무엇인지, 어떻게 동작하는지 살펴보고, 실제 예제를 통해 코드에서 활용하는 방법까지 단계별로 알아보겠습니다.
Promise란 무엇인가?
Promise는 미래에 받고자 하는 결과값을 반환하는 객체입니다.
Promise를 이해하는 가장 좋은 방법은 현실 속 '약속'과 비교해 보는 것입니다. 사람이 약속을 할 때는 지금 당장 무언가를 하는 것이 아니라, 미래의 어느 시점에 그 일을 하겠다고 약속하는 것과 같습니다.
자바스크립트의 Promise는 다음 세 가지 상태 중 하나를 가집니다.
- Pending(대기): 아직 완료되지 않은 상태
- Fulfilled(이행): 성공적으로 완료된 상태
- Rejected(거부): 값을 반환하지 못하고 실패한 상태
이는 현실의 약속과 매우 유사합니다. 미래에 실행하겠다고 말해둔 약속은 '대기' 상태이고, 약속을 지키면 '이행', 지키지 못하면 '거부'되는 것입니다.
Promise가 생성되면 처음에는 대기(Pending) 상태가 됩니다. 이 상태는 응답이 수신되어 이행(Fulfilled) 또는 거부(Rejected)로 확정될 때까지 유지됩니다.
자바스크립트 Promise 생성 방법
먼저 사용자의 이름을 반환하는 Promise를 만들어 보겠습니다. 이 함수는 3초 후에 프로그램에 이름을 전달하며, 이를 통해 Promise가 어떻게 비동기 코드를 처리하는지 확인할 수 있습니다.
Promise를 생성하려면 Promise 객체를 만들어야 합니다.
new Promise((resolve, reject) => {
// 여기에 코드를 작성합니다
});
Promise는 두 개의 인자를 받습니다. 하나는 성공 시 호출되는 resolve 함수이고, 다른 하나는 실패 시 호출되는 reject 함수입니다. 즉, Promise는 실행 결과에 따라 서로 다른 값을 반환하게 됩니다.
일반적으로 Promise 내부에는 조건문을 넣어 성공 여부를 판단합니다. 조건이 충족되지 않으면 거부(Rejected) 상태의 Promise를 반환하도록 작성합니다.
new Promise((resolve, reject) => {
if (조건이 참이라면) {
resolve();
} else {
reject();
}
});
새로운 자바스크립트 파일을 열고 아래 코드를 입력해 보세요.
let returnName = new Promise((resolve, reject) => {
let name;
setTimeout(() => {
name = "Cinnamon";
if (name === "Cinnamon") {
resolve(name);
} else {
reject("This promise has failed.");
}
}, 3000);
});
이 코드는 Promise가 호출되면 3초(3,000밀리초) 후에 Cinnamon이라는 이름을 메인 프로그램에 반환합니다. 즉, Promise를 호출하면 3초간 대기 상태에 있다가, 처리가 완료되면 이행 또는 거부 상태의 결과가 반환됩니다.
name 변수가 "Cinnamon"과 같으면 Promise는 성공적으로 이행되고, 그렇지 않으면 거부됩니다. 이 예제에서는 name이 "Cinnamon"으로 설정되어 있으므로 Promise는 정상적으로 이행됩니다.
then()과 catch()로 Promise 결과 다루기
아직 위 코드는 실행되지 않았습니다. 생성한 Promise 객체를 직접 호출해야 합니다.
returnName.then(data => {
console.log(data);
});
then() 메서드를 사용하면 Promise의 응답값을 가져올 수 있습니다. 이 메서드는 Promise가 이행된 후에 호출되며, 위 예제에서는 Promise가 반환한 응답인 data의 내용을 콘솔에 출력합니다.
실행 결과는 다음과 같습니다.
Cinnamon
then()은 성공한 Promise만 처리합니다. 그렇다면 Promise가 거부되었을 때는 어떻게 될까요? 바로 이때 catch() 메서드가 필요합니다.
Promise 체이닝(Promise Chaining)
하나의 Promise에 여러 개의 then() 문을 연결할 수 있는데, 이를 프로미스 체이닝(promise chaining)이라고 합니다. 아래 예제를 통해 체이닝이 어떻게 동작하는지 살펴보겠습니다.
returnName.then(data => {
console.log(data);
}).then(() => {
console.log("This function is over!");
});
실행 결과:
Cinnamon
This function is over!
then() 문은 Promise 생성자 안에서 지정된 순서대로 순차적으로 실행됩니다.
catch()로 에러 처리하기
catch() 메서드는 then()처럼 Promise에 연결하여 사용합니다. 이름 예제에 거부된 Promise를 처리하는 핸들러를 추가해 보겠습니다.
returnName.then(data => {
console.log(data);
}).catch(error => {
console.log(error);
});
then() 문 뒤에 catch() 문을 붙였습니다. 주의할 점은, catch()를 함께 사용할 경우 then() 뒤에 세미콜론(;)을 붙이면 안 된다는 것입니다. 세미콜론을 붙이면 자바스크립트는 then()과 catch()를 별개의 문장으로 인식하여 오류가 발생합니다.
Promise가 거부되면 catch() 문 내부의 코드가 실행됩니다. 위 코드를 실행하면 다음과 같은 결과가 출력됩니다.
Cinnamon
Cinnamon이 출력된 이유는 Promise가 성공적으로 이행되었기 때문입니다. 만약 name 값이 "Cinnamon"이 아니었다면 다음과 같은 에러가 반환됩니다.
Uncaught (in promise) This promise has failed.
이 에러 메시지는 Promise 내부의 reject() 함수에 직접 지정한 문자열입니다.
finally()로 마무리 작업 처리하기
Promise의 성공 여부와 관계없이, 실행이 끝난 후 반드시 특정 코드를 실행해야 한다면 어떻게 해야 할까요?
이럴 때 finally() 문이 유용합니다. finally()는 Promise가 이행되었든 거부되었든 상관없이 항상 실행됩니다. 기존 코드에 finally 문을 추가해 보겠습니다.
returnName.then(data => {
console.log(data);
}).catch(error => {
console.log(error);
}).finally(() => {
console.log("The returnName promise has been executed.");
});
실행 결과:
Cinnamon
The returnName promise has been executed.
Promise가 성공했기 때문에 Cinnamon이 반환되었고, 이후 finally() 문의 내용이 실행되어 추가 메시지가 콘솔에 출력되었습니다.
마무리
자바스크립트 Promise를 활용하면 깔끔하고 읽기 쉬운 비동기 코드를 작성할 수 있습니다.
이번 가이드에서는 Promise의 기본 개념과 생성 방법, then(), catch(), finally()의 사용법을 살펴보았습니다. Promise에는 이 외에도 배울 것이 많지만, 가장 좋은 다음 단계는 직접 코드로 몇 가지 Promise를 작성해 보는 것입니다.
간단한 실습 과제를 드리겠습니다. 웹 요청을 보내고 그 응답을 반환하는 Promise를 작성해 보세요. 이때 fetch() API를 사용해 요청을 만들고, 해당 요청을 Promise로 감싸면 됩니다. 요청이 성공했을 때와 실패했을 때 모두를 처리하는 코드를 작성하는 것이 핵심입니다.
더 나아가고 싶다면 async/await 문법도 학습해 보세요. async 함수 역시 비동기 코드를 작성하는 데 사용되며, Promise와 함께 자주 활용됩니다.
이제 여러분도 전문가처럼 자바스크립트 Promise를 작성할 준비가 되었습니다!