프로그래밍/Javascript

자바스크립트 비동기 프로그래밍의 핵심: 프로미스 완전 정복 가이드

shimdh 2025. 2. 13. 09:35
728x90

자바스크립트 비동기 프로그래밍, 왜 중요할까요?

웹 개발에서 사용자 경험을 극대화하는 핵심 요소는 응답성입니다. 사용자가 웹 페이지와 상호작용할 때, 즉각적인 반응을 보이는 것이 중요합니다. 만약 웹 페이지가 서버에서 데이터를 가져오거나 복잡한 계산을 처리하는 동안 멈춰버린다면 어떨까요? 사용자들은 기다리다 지쳐 페이지를 떠나버릴 것입니다.

자바스크립트의 비동기 프로그래밍은 이러한 문제를 해결하는 핵심 기술입니다. 비동기 프로그래밍을 통해 우리는 시간이 오래 걸리는 작업(예: 서버 통신, 파일 처리)을 백그라운드에서 처리하면서도 메인 스레드의 실행을 멈추지 않고 다른 작업을 수행할 수 있습니다. 이를 통해 사용자는 웹 페이지가 멈추는 현상 없이 부드럽게 상호작용할 수 있습니다.

비동기 프로그래밍, 어떻게 구현할까요?

자바스크립트에서 비동기 프로그래밍을 구현하는 방법은 여러 가지가 있지만, 그중 가장 강력하고 널리 사용되는 방법은 프로미스(Promise)를 사용하는 것입니다. 프로미스는 비동기 작업의 성공 또는 실패 결과를 미래에 받아 처리할 수 있도록 설계된 객체입니다.

프로미스, 대체 무엇일까요?

프로미스는 미래에 완료될 작업에 대한 일종의 약속입니다. 우리는 프로미스를 통해 비동기 작업이 완료되면 어떤 값을 반환할지, 또는 실패하면 어떤 에러가 발생할지를 미리 정의할 수 있습니다. 프로미스는 다음 세 가지 상태 중 하나를 가집니다.

  1. 대기 (Pending): 비동기 작업이 아직 완료되지 않은 초기 상태입니다.
  2. 이행 (Fulfilled): 비동기 작업이 성공적으로 완료된 상태입니다.
  3. 거부 (Rejected): 비동기 작업이 실패한 상태입니다.

프로미스, 왜 사용해야 할까요?

프로미스를 사용하면 비동기 코드를 훨씬 더 읽기 쉽고 관리하기 쉽게 작성할 수 있습니다. 프로미스가 등장하기 전에는 콜백 함수를 사용하여 비동기 작업을 처리했습니다. 하지만 콜백 함수를 중첩해서 사용하면 코드가 복잡해지고 가독성이 떨어지는 "콜백 지옥(Callback Hell)" 문제가 발생했습니다.

프로미스는 이러한 콜백 지옥 문제를 해결하고, 비동기 코드의 흐름을 명확하게 파악할 수 있도록 도와줍니다. 또한 프로미스는 에러 처리를 효율적으로 수행할 수 있도록 메커니즘을 제공합니다.

프로미스, 어떻게 생성하고 사용할까요?

프로미스는 new Promise() 생성자를 사용하여 생성합니다. Promise() 생성자는 두 개의 함수 인자를 받습니다.

  1. resolve: 비동기 작업이 성공적으로 완료되었을 때 호출하는 함수입니다.
  2. reject: 비동기 작업이 실패했을 때 호출하는 함수입니다.
const myPromise = new Promise((resolve, reject) => {
  // 비동기 작업 수행 (예: setTimeout, fetch)
  setTimeout(() => {
    const success = true; // 작업 성공 여부

    if (success) {
      resolve("작업 성공!"); // 작업 성공 시 resolve 호출
    } else {
      reject("작업 실패!"); // 작업 실패 시 reject 호출
    }
  }, 1000); // 1초 후 실행
});

생성된 프로미스는 .then() 메서드와 .catch() 메서드를 사용하여 처리합니다.

  1. .then(): 프로미스가 이행(Fulfilled) 상태가 되었을 때 실행되는 함수를 등록합니다.
  2. .catch(): 프로미스가 거부(Rejected) 상태가 되었을 때 실행되는 함수를 등록합니다.
myPromise
  .then((result) => {
    console.log(result); // "작업 성공!" 출력
  })
  .catch((error) => {
    console.error(error); // "작업 실패!" 출력 (실패 시)
  });

.then(), .catch() 그리고 .finally()

.then() 메서드는 두 번째 인자로 거부(Rejected) 상태일 때 실행할 함수를 받을 수 있습니다. 하지만 일반적으로 .catch() 메서드를 사용하여 거부 상태를 처리하는 것이 더 명확하고 효율적입니다. 또한 .finally() 메서드를 사용하여 프로미스의 성공 또는 실패 여부와 상관없이 항상 실행되는 코드를 작성할 수 있습니다.

myPromise
  .then((result) => {
    console.log(result); // "작업 성공!" 출력
  })
  .catch((error) => {
    console.error(error); // "작업 실패!" 출력 (실패 시)
  })
  .finally(() => {
    console.log("작업 완료!"); // 성공/실패 여부와 관계없이 실행
  });

여러 개의 프로미스 처리하기

여러 개의 비동기 작업을 동시에 처리해야 할 경우 Promise.all() 또는 Promise.race() 메서드를 사용할 수 있습니다.

  1. Promise.all(): 모든 프로미스가 이행(Fulfilled) 상태가 되어야 결과를 반환합니다.
  2. Promise.race(): 가장 먼저 이행(Fulfilled) 또는 거부(Rejected) 상태가 된 프로미스의 결과를 반환합니다.
const promise1 = Promise.resolve(1);
const promise2 = new Promise((resolve) => setTimeout(resolve, 2000, 2));
const promise3 = new Promise((resolve, reject) => setTimeout(reject, 1000, "실패"));

Promise.all([promise1, promise2, promise3])
  .then((values) => {
    console.log(values); // 모든 프로미스가 성공하면 결과 배열 반환
  })
  .catch((error) => {
    console.error(error); // 하나라도 실패하면 에러 처리
  });

Promise.race([promise1, promise2, promise3])
  .then((value) => {
    console.log(value); // 가장 먼저 완료된 프로미스의 결과 반환
  })
  .catch((error) => {
    console.error(error); // 가장 먼저 실패한 프로미스의 에러 처리
  });

async/await

ES2017에 도입된 async/await 키워드를 사용하면 프로미스 코드를 더욱 간결하고 동기적인 코드처럼 작성할 수 있습니다. async 함수는 항상 프로미스를 반환하며, await 키워드는 프로미스가 완료될 때까지 기다립니다.

async function myAsyncFunction() {
  try {
    const result = await myPromise; // 프로미스 완료까지 대기
    console.log(result); // "작업 성공!" 출력
  } catch (error) {
    console.error(error); // "작업 실패!" 출력 (실패 시)
  }
}

마무리

프로미스는 자바스크립트 비동기 프로그래밍의 핵심 개념입니다. 프로미스를 이해하고 능숙하게 활용하면 효율적이고 안정적인 웹 애플리케이션을 개발할 수 있습니다.

728x90