Callback을 이용한 비동기 제어
- 간단한 구성에도 Callback hell에 빠진다.
- 가독성이 떨어진다
- 변수 이름의 중복
- 클로저가 성능 및 메모리 공간에 영향을 미친다. ( 활성 클로저의 context가 가비지 수집시에 유지됨을 기억해보자. )
- code 규칙을 중요시 해야 한다.
Callback Rule
1. 가능한 빨리 종료하라
// Anti-Pattern
if (err) {
cb(err);
} else {
---
}
// Quick Retrun Pattern
if (err) {
return cb(err);
}
- 콜백이 호출된 후 함수 종료를 잊지 말아야 한다.
- 또한 함수가 반환하는 결과는 중요하지 않다. 실제 결과는 비동기적으로 생성돼 콜백에 전달된다.
2. 콜백을 위해 명명된 함수를 생성해 클로저 바깥에 배치 후 중간 결과를 인자로 전달하라.
이런 복잡도가 높은 코드를 모듈화를 통해 분리하자.
순차 실행
CPS로 구성시 callback hell의 주요 원인이다.
정적 패턴 ( 작업의 종류와 수를 미리 알고 있을 때 )
동적 패턴 ( 작업의 수를 모를 때 )
재귀 패턴을 이용한다.
SpiderLinks 함수는 비동기 작업을 하며 컬렉션을 반복하는 명확한 예시이다.
동적 패턴의 일반화
- 배열의 값들을 비동기적으로 맵핑 가능
- 반복문에서 연산의 결과를 다음 반복에 전달해 reduce 알고리즘 구현 가능
- 특정 조건 충족시 루프 조기 종료 가능
- 무한한 요소에 대한 반복 또한 가능
주의!
task()가 동기적 작업일 경우 알고리즘은 완전히 재귀적이 된다는 점에 유의해야 한다.
스택이 매 사이클마다 해지되지 않아 콜 스택 오버플로우를 주의해야 한다.
병렬 실행
💡Node는 Single Thread인데??
node.js에서는 이벤트 루프에 권한을 넘기며 논 블록킹 성질을 이용해 동시성을 달성하기에 병렬보단 동시성이란 표현이 옳다.
즉, Node.js에서는 논 블록킹 API에 의해 내부적으로 동시 처리되기 때문에 병렬 비동기 작업으로 실행된다는 것!!
위에서 만든 spiderlink의 task를 한 번에 실행시키는 것 ( 병렬 ) 으로 바꿔보자.
앞선 작업이 완료되기 기다리지 않고, 단순히 배열 내의 링크에 대해 각각의 작업을 시작시키는 것으로 충분하다.
hasErrors 는 하나의 병렬 작업이 실패하면 에러를 가지고 콜백을 즉시 호출시키기 위한 변수이다.
병렬 실행의 일반화
동시 작업 & Race Condition 수정
- Multi Thread : lock/mutex/semaphor/monitor 등 비동기 처리 기술을 사용
- Single Thread : 기본적으로 race condition handle tool은 없으나 race가 생기지 않는 것이 아니라 오히려 일반적인 상황이 race condition이다.
Single Thread의 race condition이 일반적인 이유
비동기 작업 호출과 그 결과 통지 사이의 지연
Spider에서의 race condition
동일한 URL에 대한 두 spider 작업은 다른 하나가 다운로드를 완료하고 파일을 생성 하기 전, 같은 파일에 대한 read를 할 때 race가 발생한다.
따라서 동일한 URL에 대한 spider 작업을 막기 위해 set() 자료구조를 사용해서 해결하자.
동시성 제한
일례로 무한히 많은 병렬 작업 처리가 존재할 수 있다.
하지만 이는 리소스를 매우 많이 잡아먹고 Single Thread인 노드 특성상 제한 없는 동시성 처리는 우리 프로그램을 무한 대기 상태에 놓이게 할 수 있다.
- 동시 실행의 제한을 설정 후 이를 넘지 않는 최대한 많은 작업을 생성
- 작업이 완료될 때 마다 하나 혹은 그 이상의 작업을 만든다
위 두 규칙으로 동시성 제한을 만들어 낼 수 있다.
- next() 라는 반복 함수가 있으며 동시성 제한 내 많은 작업을 병렬로 생성하는 내부 루프 존재
- 각 작업에 콜백을 넘겨 모든 작업을 완료 시켰는지 확인한다.
전역적 동시성 제한
조건
- 실행하기 위한 일련의 작업을 알고 있다. ( Static )
- 작업의 수가 선형적으로 증가한다. ( Linear-dynamic )
spider에서는 이 패턴이 적절하지 않다.
Queue를 활용한 동시성 제한
동시에 다운로드 할 작업 전체의 수를 Queue를 이용해 제한하는데 이 때 작업 실패 혹은 완료 여부를 main thread에 전달하기 위해 EventEmitter를 사용했다.
함수에 this.next.bind(this) 로 묶어주는 이유
https://ko.javascript.info/bind
Queue를 이용해 spiderLinks 함수의 복잡도 개선
동시성 제한, 작업 알림, 등 여러 기능을 Queue에 위임했기 때문에 Link는 queue만 받아서 계속 팔로잉 해주면 된다.
'Backend > Node.js' 카테고리의 다른 글
Observer 패턴 (0) | 2025.01.06 |
---|---|
Callback 패턴 (0) | 2025.01.06 |
Module System ( ESM ) (0) | 2025.01.06 |
Module System ( CJS ) (0) | 2025.01.06 |
Node.js Interview (0) | 2024.12.30 |