프로그래밍/Javascript

자바스크립트 비동기 프로그래밍 완벽 가이드: 프로미스(Promise)부터 async/await까지

shimdh 2025. 2. 14. 11:48
728x90

비동기 프로그래밍은 현대 웹 개발에서 필수적인 개념입니다. 특히, 자바스크립트에서 비동기 코드를 효율적으로 관리하는 것은 프론트엔드와 백엔드 개발 모두에서 중요한 역할을 합니다. 그중에서도 프로미스(Promise) 는 비동기 작업을 처리하는 강력한 도구로, 복잡한 코드 흐름을 보다 직관적으로 만들 수 있도록 도와줍니다.

이 글에서는 자바스크립트 비동기 프로그래밍의 개념부터 프로미스의 기본 원리, 활용 방법, 그리고 최신 문법인 async/await까지 체계적으로 정리해보겠습니다.


🧐 비동기 프로그래밍이란?

자바스크립트는 싱글 스레드(Single Thread) 언어로, 하나의 작업이 실행되는 동안 다른 작업이 블로킹되지 않도록 비동기(Asynchronous) 방식을 지원합니다.

예를 들어, 브라우저에서 버튼을 클릭할 때 서버에서 데이터를 가져오는 과정이 있다면, 이 요청이 완료될 때까지 기다리지 않고 다음 코드가 실행될 수 있어야 합니다. 그렇지 않으면 웹사이트가 멈춘 것처럼 보일 수 있기 때문이죠.

비동기 방식은 다음과 같은 작업에서 주로 활용됩니다.

  • 네트워크 요청 (API 호출, 데이터베이스 조회)
  • 파일 읽기/쓰기 (백엔드에서 파일 조작)
  • 타이머 기반 작업 (setTimeout, setInterval)
  • 사용자 입력 이벤트 처리 (버튼 클릭, 키보드 입력 등)

이러한 비동기 작업을 보다 효율적으로 관리하기 위해 등장한 개념이 바로 프로미스(Promise)입니다.


🔍 프로미스(Promise)란?

📌 프로미스의 정의

프로미스(Promise) 는 미래의 어떤 시점에 결과를 반환하는 비동기 작업을 나타내는 객체 입니다. 쉽게 말해, "작업이 완료되면 결과를 줄게!" 라는 약속을 하는 것이죠.

📌 프로미스의 3가지 상태

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

프로미스는 한 번 상태가 결정되면 변경되지 않습니다. 즉, fulfilled 또는 rejected 상태가 되면 다시 pending으로 돌아갈 수 없습니다.


🛠 프로미스 생성하기

자바스크립트에서 프로미스를 생성하려면 Promise 생성자를 사용해야 합니다. 이 생성자는 resolvereject라는 두 개의 콜백 함수를 인자로 받습니다.

const myPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
        const success = Math.random() > 0.5; // 50% 확률로 성공 또는 실패
        if (success) {
            resolve("🎉 작업 성공!");
        } else {
            reject("❌ 작업 실패!");
        }
    }, 2000);
});

위 코드에서 2초 후에 랜덤하게 성공(resolve)하거나 실패(reject)하는 프로미스를 생성했습니다.


🏗 프로미스 사용하기: .then().catch()

생성된 프로미스를 활용하려면 .then().catch() 메서드를 사용합니다.

myPromise
    .then(result => {
        console.log(result); // "🎉 작업 성공!"
    })
    .catch(error => {
        console.error(error); // "❌ 작업 실패!"
    });
  • .then() → 프로미스가 성공했을 때 실행됩니다.
  • .catch() → 프로미스가 실패했을 때 실행됩니다.

🔗 프로미스 체이닝 (Chaining)

프로미스의 강력한 기능 중 하나는 체이닝(Chaining) 입니다. 즉, 여러 개의 비동기 작업을 순차적으로 연결할 수 있습니다.

myPromise
    .then(result => {
        console.log(result);
        return new Promise(resolve => setTimeout(() => resolve("다음 단계 진행 중..."), 1000));
    })
    .then(nextResult => {
        console.log(nextResult); // "다음 단계 진행 중..."
    })
    .catch(error => {
        console.error("에러 발생:", error);
    });

이 방식을 활용하면 콜백 지옥(Callback Hell)을 방지할 수 있습니다.


⚡️ 여러 개의 프로미스 병렬 실행

때로는 여러 개의 비동기 작업을 동시에 실행해야 할 때가 있습니다. 이를 해결하기 위해 Promise.all()Promise.race()를 사용할 수 있습니다.

🏆 Promise.all(): 모든 프로미스가 완료될 때까지 기다리기

const promise1 = new Promise(resolve => setTimeout(() => resolve('첫 번째 완료'), 1000));
const promise2 = new Promise(resolve => setTimeout(() => resolve('두 번째 완료'), 2000));

Promise.all([promise1, promise2])
   .then(results => {
       console.log(results); // ["첫 번째 완료", "두 번째 완료"]
   });

Promise.all()모든 프로미스가 성공해야 결과를 반환하며, 하나라도 실패하면 catch()가 실행됩니다.


🏁 Promise.race(): 가장 빠른 프로미스 결과 받기

const promise1 = new Promise(resolve => setTimeout(() => resolve('첫 번째 완료'), 1000));
const promise2 = new Promise(resolve => setTimeout(() => resolve('두 번째 완료'), 500));

Promise.race([promise1, promise2])
   .then(result => {
       console.log(result); // "두 번째 완료"
   });

Promise.race()가장 먼저 완료된 프로미스의 결과만 반환합니다.


🚀 async/await: 더 쉬운 비동기 코드 작성법

async/await는 프로미스를 더욱 직관적으로 다룰 수 있도록 도와주는 최신 문법입니다.

async function fetchData() {
    try {
        const result = await myPromise;
        console.log(result);
    } catch (error) {
        console.error("에러 발생:", error);
    }
}

fetchData();

🎯 async/await의 장점

  • then() 체이닝보다 더 깔끔한 코드 작성 가능
  • 동기 코드처럼 가독성이 높아짐
  • try-catch 문을 활용해 예외 처리 간결화

🎯 결론

자바스크립트의 비동기 프로그래밍은 웹 개발에서 필수적인 요소입니다. 특히, Promiseasync/await을 활용하면 복잡한 비동기 코드도 간결하고 가독성 좋게 작성할 수 있습니다.

Promise를 활용하면 콜백 지옥을 방지하고,
Promise.all()Promise.race()여러 개의 비동기 작업을 효과적으로 실행할 수 있으며,
async/await를 사용하면 더 직관적인 비동기 코드 작성이 가능합니다.

728x90