[Node.js] 비동기 프로그래밍 깊게 파기: Promise부터 동시성 제어까지
1. Promise의 내부 상태와 에러 전파의 원리
Promise는 단순한 콜백 헬 해결사가 아닙니다. 이는 비동기 작업의 상태를 관리하는 객체입니다.
- 3가지 상태:
Pending,Fulfilled,Rejected. 한 번 결정된 상태는 바뀌지 않습니다(Immutability). - 에러 버블링:
.then()체인 내부에서 발생한 에러는 중간에.catch()가 없다면 계속 뒤로 전파됩니다. - Unhandled Rejection: 최신 Node.js 환경에서는
.catch()하지 않은 에러가 발생하면 프로세스가 종료될 수 있습니다. 반드시 최종 단계에서 예외 처리가 필요합니다.
2. async/await: 문법적 설탕 그 이상의 가치
async/await는 비동기 코드를 동기 코드처럼 읽게 해주지만, 내부적으로는 Generator와 Promise의 조합으로 동작합니다.
- Pause & Resume:
await키워드를 만나면 해당 함수의 실행 컨텍스트는 일시 중단(Suspend)되고, 제어권이 이벤트 루프로 돌아갑니다. 비동기 작업이 완료되면 다시 스택에 올라와 재개(Resume)됩니다. - 병렬 처리의 중요성: 무분별한
await사용은 성능 저하를 일으킵니다. 서로 의존성이 없는 작업은Promise.all을 통해 병렬로 처리해야 합니다.
| 패턴 | 실행 방식 | 소요 시간 (각 3초 작업 시) |
|---|---|---|
| Sequential | await A; await B; |
6초 (직렬) |
| Parallel | Promise.all([A, B]) |
3초 (병렬) |
3. 실무형 에러 핸들링: 타임아웃 패턴
비동기 작업(API 호출, DB 쿼리)은 무한정 기다릴 수 없습니다. Promise.race를 활용하면 자원을 효율적으로 관리할 수 있습니다.
// Promise.race를 이용한 타임아웃 유틸리티
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
return Promise.race([promise, timeout]);
}
Why? 외부 시스템의 장애가 내 서버의 이벤트 루프 지연으로 이어지는 것을 방지하는 최소한의 안전장치입니다.
4. Node.js에서의 동시성(Concurrency)과 Race Condition
많은 개발자가 "Node.js는 싱글 스레드니까 Race Condition이 없다"고 오해합니다. 하지만 논리적 시점에 의한 문제는 발생합니다.
- 문제 상황: 데이터 조회(await)와 수정(await) 사이에 다른 요청이 들어와 데이터를 변경하는 경우.
- 해결 방안:
- Application Level:
p-limit같은 라이브러리로 동시 실행 수 제한. - Database Level: 비관적 락(Pessimistic Lock) 또는 낙관적 락(Optimistic Lock) 사용.
5. Summary: 주간 회고 (시나리오 문제)
Q: Promise.all 중 일부가 실패해도 나머지를 살리려면?
- 방법 1:
Promise.allSettled를 사용하여 모든 결과를 수집 후 필터링. (권장) - 방법 2: 개별 Promise에
.catch()를 붙여 에러를null이나 특정 값으로 치환.
다음 단계 (Week 3 예고)
다음 주에는 이러한 비동기 객체들이 메모리 상에서 어떻게 관리되는지, V8 엔진의 Garbage Collection과 TypeScript의 고급 타입 시스템을 연결하여 학습할 예정입니다.
반응형
'Language > JS(Node.js)' 카테고리의 다른 글
| [Node.js] 멀티코어 전략과 비동기 모델의 진화 (0) | 2026.03.05 |
|---|---|
| [Node.js] 동시성 모델의 모든 것 (0) | 2026.03.02 |
| npm i (npm install)와 npm ci (npm clean-install) (0) | 2025.09.23 |
| NVM을 활용하여, Node Version을 관리하는 방법 (0) | 2023.04.22 |
| [Javascript] Array.prototype.forEach vs Array.prototype.Map (2) | 2022.04.03 |
댓글