[JavaScript] 26. 비동기 작업 처리하기 / Promise

서회정's avatar
Feb 04, 2026
[JavaScript] 26. 비동기 작업 처리하기 / Promise

1. Promise란

💡
비동기 작업을 효율적으로 처리할 수 있도록 도와주는 자바스크립트 내장 객체
notion image
 

2. Promise의 세 가지 상태

1. 대기 (Pending)

  • 아직 작업이 완료되지 않은 상태

2. 성공 (Fulfilled)

  • 비동기 작업이 성공적으로 마무리 된 상태
  • Pending 상태에서 Resolve(해결)된 상태

3. 실패 (Rejected)

  • 비동기 작업이 실패한 상태
  • Pending 상태에서 Reject(거부)된 상태
 

3. Promise 사용

💡
Promise 객채의 인수로 전달되는 콜백함수는 비동기 작업을 실행하는 함수로, executor라고 부른다.
const promise = new Promise((resolve, reject) => { // 비동기 작업 실행하는 함수 // executor setTimeout(() => { console.log("안녕"); }, 1000); }); console.log(promise);
notion image
 
Promise 객체를 생성하게 되면 그 안에 있는 콜백함수는 별도의 실행 코드 없이 실행된다.
따라서 위 코드에서 1초 뒤에 “안녕” 이라는 문자열이 출력되는 것을 확인할 수 있는데, 만일 이 객체를 console에 출력하게 되면 즉시 Promise 객체가 출력되고 1초 뒤에 Promise 내부에 있는 함수가 실행되며 “안녕”이 출력된다.
 
여기서 콘솔을 확인하면 Promise객체의 상태와 결과값을 확인할 수 있는데 상태는 Pending, 즉 대기 상태이며 결과값은 아직 아무것도 들어오지 않은 undefined 인 것을 확인할 수 있다.
 

4. Promise 객체의 구조

new Promise((resolve, reject) => {})
notion image
 
Promise 객체에는 resolvereject라는 각각의 매개변수가 들어간다.
resolve에는 비동기 작업을 성공, 즉 fulfilled상태로 바꾸는 함수를 가지고 있으며, reject에는 비동기 작업을 실패, rejected상태로 바꾸는 함수를 가지고 있다.
 
따라서 Promise가 관리하는 비동기 작업의 상태를 fulfilled가 되는 것을 확인하고자 한다면, 비동기 작업의 상태를 성공 상태로 바꿔주는 resolve() 함수를 호출하여 다음과 같이 확인할 수 있다.
 
const promise = new Promise((resolve, reject) => { // 비동기 작업 실행하는 함수 // executor setTimeout(() => { console.log("안녕"); resolve(); }, 1000); }); setTimeout(() => { console.log(promise); }, 2000);
notion image
 
상태가 fulfilled로 변경된 것은 확인할 수 있지만 여전히 Promise의 결과값은 undefined인것을 확인할 수 있는데, 이는 executor함수 내부에서 resolve() 함수를 호출하면서 인수로 결과값을 전달하지 않았기 때문이다. 따라서 결과값을 콘솔에서 확인하고자 한다면 다음과 같이 인수로 전달하고자 하는 결과값을 넘겨주면 된다.
 
const promise = new Promise((resolve, reject) => { // 비동기 작업 실행하는 함수 // executor setTimeout(() => { console.log("안녕"); resolve(); }, 1000); }); setTimeout(() => { console.log(promise); }, 2000);
notion image
 
실패한 경우에는 reject() 함수를 호출하여 상태를 변경할 수 있다.
 
const promise = new Promise((resolve, reject) => { // 비동기 작업 실행하는 함수 // executor setTimeout(() => { console.log("안녕"); reject("실패한 이유 ..."); }, 1000); }); setTimeout(() => { console.log(promise); }, 2000);
notion image
 

5. Promise 객체 활용

executor함수 내부에서 if문을 통해 Promise 상태와 결과값을 반환해보자
const promise = new Promise((resolve, reject) => { setTimeout(() => { const num = 10; if (typeof num === "number") { resolve(num + 10); } else { reject(`${num}이 숫자가 아닙니다`); } }, 1000); }); setTimeout(() => { console.log(promise); }, 2000);
notion image
num의 타입이 숫자가 맞기때문에 true 조건의 결과에 따라 fulfilled 상태와 그 결과값인 20이 반환되는 것을 확인할 수 있다.
 
여기서 반환되는 결과값을 활용하고자 한다면 then 메서드가 필요하다.
then 메서드는 그 후에 라는 단어의 뜻에서 할 수 있듯 Promise의 결과값을 활용할 수 있도록 돕는 메서드인데 결과값을 매개변수로 받아서 콜백함수 내에서 그 값을 활용할 수 있도록 만들어준다.
const promise = new Promise((resolve, reject) => { setTimeout(() => { const num = 10; if (typeof num === "number") { resolve(num + 10); } else { reject(`${num}이 숫자가 아닙니다`); } }, 1000); }); // 결과값 활용 // then 메서드 // 그 후에 promise.then((value) => { console.log(value); });
notion image
 
여기서 num의 값을 숫자값인 10이 아니라 null로 변경하게 된다면 then 메서드가 실행되지 않아서 아무것도 출력되지 않는 것을 확인할 수 있는데, reject 상태의 결과값을 활용하고자 할 때는 then이 아닌 catch 메서드를 사용하여 결과값을 반환할 수 있다.
 
const promise = new Promise((resolve, reject) => { setTimeout(() => { const num = null; if (typeof num === "number") { resolve(num + 10); } else { reject(`${num}이 숫자가 아닙니다`); } }, 1000); }); // 결과값 활용 // then 메서드 // 그 후에 promise.then((value) => { console.log(value); }); promise.catch((error) => { console.log(error); });
notion image
 
then은 Promise를 한 번 더 반환하는데, 이러한 특성때문에 catch 메서드를 따로 작성하지 않고 다음과 같은 형태로 사용할 수도 있다.
 
promise.then((value) => { console.log(value); }).catch((error) => { console.log(error); });
 
⇒ 이러한 문법은 Promise Chaining이라고 부른다. 비동기 작업을 콜백함수만 사용하여 처리하게 되면 조건 분기가 늘어날 때마다 코드가 깊어져 콜백 지옥이 되기 쉽상이다. 이는 가독성도 떨어질 뿐더러 유지보수를 어렵게 만든다. 이 때 Promise Chaining 문법을 사용하여 보다 깔끔한 코드를 유지할 수 있다.
 
function add10(num) { const promise = new Promise((resolve, reject) => { setTimeout(() => { if (typeof num === "number") { resolve(num + 10); } else { reject(`${num}이 숫자가 아닙니다`); } }, 1000); }); return promise; } add10(0) .then((result) => { console.log(result); return add10(result); }) .then((result) => { console.log(result); return add10(result); }) .then((result) => { console.log(result); return add10(result); }) .catch((error) => { console.log(error); });
 
기존에 있던 코드에서 promise 객체를 add10이라는 함수 안에 넣고, 해당하는 함수의 매개변수로 num을 받는다. 따라서 이 add10이라는 함수를 호출할 때 num의 값을 동적으로 넣을 수 있게 된다. 또 해당하는 함수에서 생성된 promise객체를 반환할 수 있도록 return해준다.
 
그리고 이 add10 함수를 호출할 때 0이라는 숫자를 매개변수로 넣어주게 되면 첫 번째 결과값으로 10이 출력될 수 있도록 콘솔 출력 코드를 넣고, result라는 매개변수를 가진 add10 함수를 리턴하여 다음 then 메서드가 그 결과값을 활용할 수 있게 한다.
 
이러한 방법은 콜백지옥 구조에서 벗어나 보다 가독성 좋은 코드를 만들어주는데, 마지막에 혹여 모를 에러 발생 가능성을 고려해 catch 메서드를 사용하는 것이 일반적이다. 이렇게 catch 메서드를 마지막에 넣어주면 Promise Chaining 도중 reject가 발생했을 때, 그 이후에 연결된 then 메서드들은 실행되지 않고, 제어는 가장 가까운 catch 메서드로 이동한다.
 
 
Share article

clubnerdy