프라미스 체이닝이란
비동기 작업의 결과가 순차적으로 다음 작업에 전달되도록 하는 패턴입니다.
각 .then() 핸들러가 값을 반환하면, 그 값이 새로운 프라미스로 감싸져 체인의 다음 단계에 전달됩니다. 이 덕분에 여러 비동기 작업을 마치 동기적인 작업처럼 순서대로 처리할 수 있습니다.
프라미스 체이닝의 기본 구조
new Promise((resolve, reject) => {
// 1초 후 숫자 1로 프라미스 이행
setTimeout(() => resolve(1), 1000);
})
.then(result => {
console.log("첫 번째 then:", result); // 출력: 1
// 이전 결과에 2를 곱하여 반환: 1 * 2 = 2
return result * 2;
})
.then(result => {
console.log("두 번째 then:", result); // 출력: 2
// 다시 이전 결과에 2를 곱하여 반환: 2 * 2 = 4
return result * 2;
})
.then(result => {
console.log("세 번째 then:", result); // 출력: 4
// 체인 종료: 최종 결과는 4
});
각 .then() 핸들러는 이전 단계에서 반환된 값을 인자로 수신하고, 새로운 값으로 변환하여 반환하는 함수입니다. 이때 반환된 값은 암묵적으로 Promise.resolve()에 의해 프라미스 래핑되어, 다음 .then() 핸들러로 전달되는 비동기 컨텍스트를 유지합니다.
프라미스 체이닝에서 프라미스를 반환
.then() 핸들러 내부에서 새로운 프라미스를 반환하는 것은, 비동기 작업의 순차적 실행을 제어합니다. 핸들러가 프라미스를 반환할 경우, 프라미스 체인은 해당 프라미스의 상태 변화(이행 또는 거부)를 감지하고, 상태가 확정될 때까지 후속 단계를 일시 중단합니다.
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000);
})
.then(result => {
console.log("Step 1:", result); // 출력: 1
// 새로운 프라미스 반환: 1초 후 result * 2 값으로 이행
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result * 2), 1000);
});
})
.then(result => {
console.log("Step 2:", result); // 출력: 2 (1 * 2)
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result * 2), 1000);
});
})
.then(result => {
console.log("Step 3:", result); // 출력: 4 (2 * 2)
});
동일 프라미스에 대한 복수 핸들러 등록: 병렬적 처리
하나의 프라미스 객체에 여러 개의 .then() 핸들러를 등록하는 것은, 각 핸들러가 동일한 프라미스의 결과를 독립적으로 처리하도록 합니다. 이는 병렬적인 작업 처리가 필요한 경우에 유용하며, 체이닝과는 다른 동작 방식을 보여줍니다.
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000);
});
// 동일 프라미스에 등록된 각 핸들러는 독립적으로 결과 (1) 을 수신
promise.then(result => console.log("Handler 1:", result)); // 출력: 1
promise.then(result => console.log("Handler 2:", result)); // 출력: 1
promise.then(result => console.log("Handler 3:", result)); // 출력: 1
각 .then() 핸들러는 원본 프라미스가 이행된 시점에 비동기적으로 실행되며, 동일한 이행 값을 공유합니다. 그러나 이들은 서로 독립적인 실행 컨텍스트를 가지므로, 체이닝과는 구분되는 병렬적 처리 패턴을 구현합니다.
프라미스 체이닝의 장점
- 가독성: 각 단계가 순차적으로 나열되어 비동기 작업의 흐름을 한눈에 파악할 수 있습니다.
- 효율적인 에러 핸들링: 체인 내에서 발생하는 예외는 .catch() 핸들러를 통해 중앙 집중적으로 처리할 수 있습니다.
- 확장성: 이미 이행된 프라미스에도 나중에 핸들러를 추가할 수 있으며, 새 프라미스를 반환하여 추가 비동기 작업을 손쉽게 연결할 수 있습니다.
사용자 정보 순차 로드 예시
// 사용자 정보를 가져오는 함수
function fetchUserInfo(userId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (userId === 123) {
resolve({ id: 123, name: "Alice", role: "admin" });
} else {
reject(new Error("사용자를 찾을 수 없습니다."));
}
}, 1000);
});
}
// 사용자의 권한을 확인하는 함수
function checkUserPermissions(user) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (user.role === "admin") {
resolve({ ...user, permissions: ["read", "write", "delete"] });
} else {
resolve({ ...user, permissions: ["read"] });
}
}, 1000);
});
}
// 사용자의 활동 로그를 가져오는 함수
function fetchUserActivityLog(user) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
...user,
activityLog: [
"로그인: 2023-02-18 10:00",
"프로필 수정: 2023-02-18 11:30",
"로그아웃: 2023-02-18 18:00"
]
});
}, 1000);
});
}
// 프라미스 체이닝을 이용한 사용자 정보 처리
fetchUserInfo(123)
.then(user => {
console.log("사용자 정보 로드:", user);
return checkUserPermissions(user);
})
.then(userWithPermissions => {
console.log("사용자 권한 확인:", userWithPermissions.permissions);
return fetchUserActivityLog(userWithPermissions);
})
.then(userWithActivityLog => {
console.log("사용자 활동 로그:", userWithActivityLog.activityLog);
return userWithActivityLog;
})
.then(finalUserData => {
console.log("최종 사용자 데이터:", finalUserData);
})
.catch(error => {
console.error("에러 발생:", error.message);
})
.finally(() => {
console.log("사용자 데이터 처리 완료");
});
- fetchUserInfo는 사용자 ID를 받아 사용자 기본 정보를 반환합니다.
- checkUserPermissions는 사용자 정보를 받아 권한 정보를 추가합니다.
- fetchUserActivityLog는 사용자 정보에 활동 로그를 추가합니다.
- 각 함수는 프라미스를 반환하며, setTimeout을 사용해 비동기 작업을 시뮬레이션합니다.
- 프라미스 체이닝을 통해 각 단계의 결과를 다음 단계로 전달합니다.
- catch를 사용해 에러를 처리하고, finally를 사용해 작업 완료를 로깅합니다.
'JavaScript' 카테고리의 다른 글
[JavaScript] 프라미스 API (0) | 2025.02.18 |
---|---|
[JavaScript] 프라미스 체이닝의 에러 처리 (0) | 2025.02.18 |
[JavaScript] Promise를 활용한 비동기 작업 (0) | 2025.02.18 |
[JavaScript] 콜백 패턴과 비동기 처리 (0) | 2025.02.18 |
[JavaScript] 커스텀 에러 클래스를 통한 에러 구분 및 확장 (0) | 2025.02.18 |