콜백 지옥의 다음 세대
Promise 가 도입되기 전 '콜백 지옥' 은 어지러웠다. Java나 Go처럼 멀티 스레드를 사용하거나, PHP처럼 동기적 흐름이 기본인 언어를 다루다 Node.js 으로 넘어오면서 비동기 흐름 제어가 가장 큰 진입 장벽이었다.
하지만 자바스크립트는 ES6(ECMAScript 2015)가 등장하면서 비동기 작업의 최종 완료 또는 실패를 나타내는 Promise가 표준으로 채택되면서 보기 좋은 비동기 코드를 작성할 수 있게 되었다.
Promise: 비동기의 새로운 문법
콜백 패턴에서는 비동기 처리의 결과를 콜백 함수 내부에서만 처리할 수 있었다. 하지만 Promise 는 미래의 어떤 시점에 결과를 담은 객체를 반환한다. 이 객체는 성공(resolve)하거나 실패(reject)하며 then, catch, finally 라는 메서드로 체이닝(Chaining) 할 수 있다.
콜백 지옥의 리팩토링
Promise 를 도입한 예제
const fs = require('fs').promises;
function processUserData() {
return fs.readFile('config.json', 'utf8')
.then(configData => {
const config = JSON.parse(configData);
return db.connect(config.dbUrl); // Promise 반환
})
.then(connection => {
return connection.query('SELECT * FROM users') // Promise 반환
.then(rows => {
connection.end(); // 연결 종료
return rows;
});
})
.then(rows => {
return fs.writeFile('users.log', JSON.stringify(rows));
})
.then(() => {
console.log('완료');
})
.catch(err => {
// 모든 단계의 에러를 이곳에서 한 번에 처리
console.error('프로세스 처리 중 오류 발생:', err);
})
.finally(() => {
console.log('리소스 정리 등 마무리 작업');
});
}
이제 코드는 오른쪽으로 파고들지 않고 아래로 흐른다.
return 키워드를 통해 비동기 작업의 결과를 다음 then으로 넘겨주는 파이프라인 구조가 가독성을 개선했다.
Bluebird와 Q
지금은 Node.js가 네이티브 Promise 를 강력하게 지원하지만, ES6(ECMAScript 2015) 이전에는 네이티브 구현체의 성능 이슈나 기능 부족으로 인해 Bluebird나 Q 같은 폴리필 라이브러리가 필수였다.
특히 Bluebird는 네이티브보다 월등히 빠른 속도와 promisifyAll 같은 유틸리티를 제공했었다. 하지만 이젠 사용하지 불필요하게 되면서 비동기 생태계가 완성되었다.
병렬 처리
Promise가 도입되면서 여러 비동기 작업을 효율적으로 관리할 수 있게 되었다.
- Promise.all: 여러 개의 API 요청이나 파일 읽기 등을 동시에 시작하고, 모두 완료될 때까지 기다린다. 만약 하나라도 실패하면 즉시 에러를 반환한다.
- Promise.race: 가장 먼저 완료되는 작업의 결과만 취급하며 타임아웃 구현 등에 유용하다.
// 병렬 처리
const readFiles = [
fs.readFile('file1.txt'),
fs.readFile('file2.txt'),
fs.readFile('file3.txt')
];
Promise.all(readFiles)
.then(([file1, file2, file3]) => {
console.log('모든 파일 로딩 완료.);
})
.catch(err => {
console.error('파일 읽기 실패:', err);
});
프로미스 지옥 : Promise Hell
Promise 는 분명히 혁명이었지만 Promise Hell 이라는 문제가 나타나며 이는 프로미스 중첩으로 코드가 복잡해지고 가독성이 나빠지는 문제이다. 프로미스 체이닝으로 해결이 가능하나 너무 많은 then이 발생하며 프로미스 안에 또 다른 프로미스를 반환하는 형태가 반복되며 비동기 로직을 이해하는데 어려움이 발생하며 디버깅또한 복잡해지는 문제를 만들었다.
하지만 현재는 async/await 가 도입되면이 이러한 문제는 해결이 되었다.
'Programming > 노드 (NodeJS)' 카테고리의 다른 글
| [Node.js] 자바스크립트 비동기 처리 - 비동기의 완성 Async/Await (0) | 2026.01.09 |
|---|---|
| [Node.js] 자바스크립트 비동기 처리 - 콜백 지옥과 async 라이브러리 (0) | 2025.05.13 |