Frontend Development

JavaScript Promise 완벽 가이드: 비동기 처리의 새로운 패러다임

Kun Woo Kim 2025. 5. 27. 22:21
반응형

JavaScript의 비동기 처리에서 핵심적인 역할을 하는 Promise에 대해 자세히 알아보겠습니다. 이 글에서는 Promise의 개념, 상태, 사용법, 그리고 한계점까지 포괄적으로 다룹니다.


Promise란?

Promise는 JavaScript에서 비동기 작업을 관리하고, 해당 작업의 성공 또는 실패 결과를 나중에 사용할 수 있도록 하는 객체입니다. 쉽게 말해, Promise는 비동기 작업의 완료 여부를 "약속"해주는 개념이라고 할 수 있습니다.

Promise의 등장 배경

기존의 콜백 기반 비동기 처리 방식은 다음과 같은 문제점이 있었습니다:

// 콜백 지옥 예시
fetchData(function(data) {
    processData(data, function(processedData) {
        saveData(processedData, function(savedData) {
            displayData(savedData, function() {
                // 더 깊은 콜백 중첩...
            });
        });
    });
});

이러한 "콜백 지옥" 문제를 해결하기 위해 Promise가 도입되었습니다.


Promise의 상태

Promise는 다음 세 가지 상태를 가집니다:

  1. Pending (대기)
    • 비동기 작업이 아직 완료되지 않은 초기 상태
    • Promise가 생성된 직후의 상태
  2. Fulfilled (이행)
    • 비동기 작업이 성공적으로 완료된 상태
    • resolve()가 호출되어 결과값이 전달된 상태
  3. Rejected (거부)
    • 비동기 작업이 실패한 상태
    • reject()가 호출되어 오류가 전달된 상태
const promise = new Promise((resolve, reject) => {
    // 비동기 작업 수행
    if (/* 성공 */) {
        resolve(value);  // Fulfilled 상태로 전환
    } else {
        reject(error);   // Rejected 상태로 전환
    }
});

Promise 사용법

1. 기본 사용법

const fetchUserData = () => {
    return new Promise((resolve, reject) => {
        // 비동기 작업 (예: API 호출)
        fetch('https://api.example.com/user')
            .then(response => response.json())
            .then(data => resolve(data))
            .catch(error => reject(error));
    });
};

// 사용
fetchUserData()
    .then(userData => console.log(userData))
    .catch(error => console.error(error));

2. Promise 체이닝

fetchUserData()
    .then(userData => processUserData(userData))
    .then(processedData => saveUserData(processedData))
    .then(savedData => displayUserData(savedData))
    .catch(error => handleError(error));

3. Promise.all() - 병렬 처리

const fetchMultipleData = async () => {
    try {
        const [users, posts, comments] = await Promise.all([
            fetchUsers(),
            fetchPosts(),
            fetchComments()
        ]);
        // 모든 데이터가 준비된 후 처리
    } catch (error) {
        // 하나라도 실패하면 에러 처리
    }
};

Promise의 한계점

1. 복잡한 에러 처리

fetchUserData()
    .then(userData => {
        // 특정 단계에서만 발생하는 에러 처리
        if (!userData.isValid) {
            throw new Error('Invalid user data');
        }
        return processUserData(userData);
    })
    .catch(error => {
        // 모든 에러가 여기로 모임
        // 어떤 단계에서 발생한 에러인지 구분이 어려움
    });

2. 여전히 존재하는 가독성 문제

// Promise 체이닝이 길어질 경우
fetchUserData()
    .then(userData => {
        return processUserData(userData)
            .then(processedData => {
                return saveUserData(processedData)
                    .then(savedData => {
                        return displayUserData(savedData);
                    });
            });
    })
    .catch(error => handleError(error));

이러한 문제는 async/await을 통해 개선할 수 있습니다:

// async/await을 사용한 개선된 버전
async function handleUserData() {
    try {
        const userData = await fetchUserData();
        const processedData = await processUserData(userData);
        const savedData = await saveUserData(processedData);
        await displayUserData(savedData);
    } catch (error) {
        handleError(error);
    }
}

결론

Promise는 JavaScript의 비동기 처리 방식을 크게 개선했지만, 여전히 몇 가지 한계점이 있습니다. 이러한 한계는 async/await과 같은 최신 문법을 통해 보완할 수 있습니다. 실제 프로젝트에서는 상황에 따라 Promise와 async/await을 적절히 조합하여 사용하는 것이 좋습니다.


참고 자료

반응형