Node.js 이벤트 루프 이해하기
이벤트 루프는 작업자가 아닙니다. 조정자(coordinator)입니다.
많은 개발자들이 이벤트 루프를 복잡하게 느낍니다. 그 어려움은 종종 너무 많은 개념을 한꺼번에 섞어서 생각하는 데서 옵니다. libuv, 콜 스택, 프로미스, 그리고 I/O를 동시에 추적해야 하기 때문입니다.
간단한 진실은 이렇습니다. 이벤트 루프는 작업을 스케줄링할 뿐, 직접 작업을 수행하지는 않습니다.
JavaScript는 싱글 스레드에서 실행됩니다. 이는 코드가 직선 형태로 실행됨을 의미합니다. 한 작업이 끝나야 다음 작업이 시작될 수 있습니다. 우리는 프로그램 전체를 멈추지 않고 파일 읽기나 네트워크 요청과 같은 비동기 작업을 관리할 시스템이 필요합니다.
작동 방식:
이벤트 루프는 다양한 큐에 있는 콜백을 콜 스택으로 옮깁니다. 각 단계를 줄(line)이라고 생각해보세요. 이벤트 루프는 줄에 서 있는 사람들을 방 안으로 옮겨주는 사람과 같습니다.
주요 단계는 다음과 같습니다:
- Timers: setTimeout 및 setInterval의 콜백을 처리합니다.
- Pending Callbacks: TCP 오류와 같은 특정 시스템 오류를 처리합니다.
- Idle and Prepare: libuv에서 사용하는 내부 단계입니다. 직접 사용할 일은 없습니다.
- Poll: 가장 중요한 단계입니다. 파일 읽기나 HTTP 요청과 같은 새로운 I/O 이벤트를 가져옵니다.
- Check: setImmediate 콜백을 처리합니다.
- Close Callbacks: 소켓 종료와 같은 종료 이벤트를 처리합니다.
흔한 실수는 이벤트 루프가 자체적인 콜 스택을 가지고 있다고 생각하는 것입니다. 그렇지 않습니다. 런타임은 단 하나의 콜 스택과 하나의 마이크로태스크 큐를 가집니다.
프로세스는 다음과 같은 흐름을 따릅니다:
- 이벤트 루프가 현재 단계에서 콜백을 하나 선택합니다.
- 해당 콜백을 콜 스택에 넣습니다(push).
- 콜 스택이 코드를 실행합니다.
- 콜백이 완료되면, 엔진은 마이크로태스크 큐를 비웁니다(drain). 이곳에 프로미스와 async/await가 존재합니다.
- 이벤트 루프가 다음 단계로 넘어갑니다.
setTimeout(..., 0)을 사용하면 Timers 단계로 이동합니다. setImmediate(...)를 사용하면 Check 단계로 이동합니다.
실제 힘든 작업은 OS 커널과 libuv에서 일어납니다. 이벤트 루프는 단지 JavaScript에게 반응할 시점이 되었음을 알려줄 뿐입니다.
Source: https://dev.to/joaovictor6/event-loop-entendendo-uma-das-bases-do-node-41a