[javascript] 프로미스(Promise) 이해와 활용

Chan의 기술 블로그·2025년 10월 17일

javascript

목록 보기
5/8

이 글은 『자바스크립트 딥 다이브』를 공부하며 정리한 내용입니다.

이전 게시물에서 자바스크립트의 전통적인 비동기 처리 방식인 콜백 패턴에는 다음과 같은 한계가 있다고 했다.

  • 콜백 헬(callback hell)로 인해 가독성이 나쁘다.
  • 비동기 처리 중 발생한 에러 처리가 어렵다.
  • 여러 비동기 작업을 순차 또는 병렬로 제어하기가 복잡하다.

이러한 문제를 해결하기 위해 ES6에서 프로미스(Promise) 가 도입되었다.


프로미스의 생성

Promise 생성자 함수를 new 연산자와 함께 호출하면 프로미스 객체가 생성된다.
생성자 함수는 콜백 함수를 인수로 받으며, 이 콜백은 resolve와 reject 함수를 전달받는다.

const promise = new Promise((resolve, reject) => {
  if (/* 비동기 처리 성공 */) {
    resolve("result");
  } else {
    reject("failure reason");
  }
});

프로미스는 내부에서 비동기 작업을 수행하고,
성공 시 resolve, 실패 시 reject 함수를 호출한다.

프로미스의 상태

상태의미상태 변경 조건
pending비동기 처리가 아직 수행되지 않은 상태프로미스가 생성된 직후
fulfilled비동기 처리가 성공적으로 완료된 상태resolve() 호출
rejected비동기 처리가 실패한 상태reject() 호출
  • fulfilled 또는 rejected 상태를 settled 상태라고 한다.
  • 한 번 settled 상태가 되면 더 이상 다른 상태로 변하지 않는다.
// 성공
const fulfilled = new Promise(resolve => resolve(1));

// 실패
const rejected = new Promise((_, reject) => reject(new Error("에러 발생!")));

비동기 처리가 성공하면 프로미스는 fulfilled 상태가 되고, 결과값을 저장한다.
실패하면 rejected 상태가 되어 Error 객체를 값으로 가진다.

즉, 프로미스는 비동기 처리의 상태와 결과를 관리하는 객체다.


프로미스의 후속 처리 메서드

비동기 작업이 완료되면 후속 처리를 수행해야 한다.
이를 위해 프로미스는 다음 3가지 후속 처리 메서드를 제공한다.

메서드설명
then성공(fulfilled) 또는 실패(rejected) 시점의 콜백 등록
catch실패(rejected) 시점의 콜백 등록
finally성공/실패와 관계없이 항상 실행되는 콜백 등록

Promise.prototype.then

then 메서드는 두 개의 콜백 함수를 인수로 받는다.

  • 첫 번째: fulfilled 상태일 때 호출됨
  • 두 번째: rejected 상태일 때 호출됨
new Promise(resolve => resolve("fulfilled"))
  .then(v => console.log(v), e => console.error(e));
// → "fulfilled"

new Promise((_, reject) => reject(new Error("rejected")))
  .then(v => console.log(v), e => console.error(e));
// → Error: rejected

then은 항상 새로운 프로미스를 반환한다.
내부에서 반환한 값이 프로미스가 아니면, 그 값을 resolve로 감싸 새 프로미스를 만든다.

Promise.prototype.catch

catch는 하나의 콜백만 인수로 받으며,
rejected 상태일 때만 호출된다.

new Promise((_, reject) => reject(new Error("rejected")))
  .catch(e => console.error(e));
// → Error: rejected

Promise.prototype.finally

finally는 성공/실패와 관계없이 항상 한 번 실행된다.

new Promise(() => {})
  .finally(() => console.log("finally"));
// → finally

finally는 공통적으로 실행해야 하는 작업(로딩 해제, 연결 종료 등)에 유용하다.

프로미스로 콜백 헬 해결

이전 글의 XMLHttpRequest 예제를 프로미스로 변환하면 다음과 같다.

const promiseGet = url => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.send();

    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(JSON.parse(xhr.response));
      } else {
        reject(new Error(xhr.status));
      }
    };
  });
};

promiseGet("https://example.com/posts/1")
  .then(res => console.log(res))
  .catch(err => console.error(err))
  .finally(() => console.log("BYE"));

프로미스 체이닝을 통해 콜백 헬 문제를 간결하게 해결할 수 있다.


프로미스의 정적 메서드

1. Promise.resolve / Promise.reject

Promise.resolve(1); // 즉시 resolve된 프로미스 생성
Promise.reject(new Error("에러")); // 즉시 reject된 프로미스 생성

2. Promise.all — 여러 비동기 처리 병렬 실행

const request1 = () => new Promise(resolve => setTimeout(() => resolve(1), 3000));
const request2 = () => new Promise(resolve => setTimeout(() => resolve(2), 2000));
const request3 = () => new Promise(resolve => setTimeout(() => resolve(3), 1000));

Promise.all([request1(), request2(), request3()])
  .then(console.log) // [1, 2, 3]
  .catch(console.error);
  • 모든 프로미스가 fulfilled 상태가 되어야 결과가 반환된다.
  • 하나라도 rejected 되면 즉시 종료되어 catch로 넘어간다.

3. Promise.race — 가장 먼저 완료된 프로미스 반환

Promise.race([request1(), request2(), request3()])
  .then(console.log) // 3
  .catch(console.error);

가장 먼저 fulfilled 상태가 된 프로미스의 결과를 반환한다.
(단, 먼저 rejected 되면 그 에러를 즉시 반환)

4. Promise.allSettled — 모든 결과 수집

Promise.allSettled([
  new Promise(resolve => setTimeout(() => resolve(1), 2000)),
  new Promise((_, reject) => setTimeout(() => reject(new Error("Error!!")), 1000)),
]);
/*
[
  { status: "fulfilled", value: 1 },
  { status: "rejected", reason: Error: Error!! }
]
*/

모든 프로미스의 처리 결과(성공/실패 포함)를 배열로 반환한다.


✨ 정리

  • Promise는 비동기 상태(pending → fulfilled/rejected) 와 결과를 관리한다.
  • then, catch, finally로 후속 처리를 체이닝할 수 있다.
  • Promise.all, race, allSettled 등으로 복합 비동기 처리를 단순화할 수 있다.
  • 콜백 헬을 해결하지만, 여전히 콜백을 사용한다는 점에서
    async/await로 한 단계 더 발전할 수 있다.
profile
퍼블리셔에서 프론트앤드로 전향하기

0개의 댓글