event loop란 무엇인가?
이벤트 루프는 Node.js에서 여러 비동기 작업들을 모아서 관리하고 순서대로 실행할 수 있게 하는 도구이다.
ex) file.readFile('file.txt', callback)
이벤트 루프는 위와 같은 과정을 거쳐 동작한다. 각 박스들은 특정 작업을 수행하기 위한 페이즈(phase)를 의미하고 다음 페이즈로 넘어가는 것을 틱(Tick)이라고 한다. 각 페이즈마다 FIFO 큐가 있는데 함수가 호출되면 큐에 쌓이게 된다. 이벤트 루프는 싱글 스레드이기 때문에 이전 페이즈가 끝나야 혹은 페이즈의 최대 콜백 수에 도달하면 다음 페이즈가 실행되게 된다.
각 phase들의 역할
timer: setTimeout()이나 setInterval()에 의해 예약된 콜백을 실행한다. 이때 타이머는 콜백이 실행될 정확한 시간을 지정하는 것이 아니라 해당 콜백이 실행될 수 있는 최소 시간을 지정한다. 콜백은 지정된 시간이 지난 후 최대한 빨리 실행되지만 다른 콜백들의 실행에 의해 지연될 수 있다.
pending callbacks: TCP 오류 유형과 같은 일부 시스템 작업에 의한 콜백을 실행
idle, prepare: 내부적으로만 사용되고 이 단계에서 이벤트 루프는 아무 작업도 수행하지 않음.
poll: close 콜백, 타이머에 의해 예약된 콜백, setImmediate()을 제외한 거의 모든 I/O관련 콜백들이 실행(파일 읽기/쓰기, 네트워크 요청 등 다양한 비동기 작업이 수행되는 곳)
check: setImmediate() 콜백을 실행
close callbacks: socket.on('close', fn) 또는 process.exit()과 같은 종료 이벤트와 관련된 콜백 실행. 각 pahse 사이 모든 작업들이 수행됐는지 확인하고 완전히 종료.
이전에 이벤트 루프에서는 비동기 작업을 OS 커널에 맡기거나 워커스레드에거 넘겨 처리한다고 하였다. 특정 함수(작업)이 들어오면 이벤트 루프는 각 phase를 훑어가며 들어온 작업을 할당하고(큐에 콜백 할당) 해당 phase에서 OS 커널이나 워커스레드에 넘겨 비동기 작업을 처리하는(콜백실행) 방식으로 동작한다. 아래의 예시와 함께 보면 더 이해가 수월할 것이다.
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
이 코드를 실행하면 이벤트루프는 어떻게 동작할까?
1. 이벤트 루프는 fs.readFile이라는 작업은 poll phase에 추가하고 poll phase에서는 OS 커널에 작업을 넘겨 처리한다. 이때 OS 커널에서 작업을 처리할 때는 논블로킹 방식으로 하기 때문에 fs.readFile() 뒤에 코드가 있다면 readFile 작업이 수행될 때까지 처리될 수 있다.
2. poll phase에서 fs.readFile()를 읽어들인 후에 콜백을 실행하는데 이때 순차적으로 setTimeout() 콜백함수가 timer에 추가되고 이후 setImmediate() 콜백함수가 check에 추가된다.
3. check phase로 넘어가 check 큐에 대기중이던 setImmediate()의 콜백이 실행된다.
4. close callbacks phase에서 timer에 작업에 남아있는 것을 확인하고 timer로 다시 이동한다.
5. timer 큐에 남아있던 setTimeout()의 콜백함수를 실행한다.
6. 그 뒤의 비어있는 phase들을 거쳐 close callbacks phase에 오면 모든 작업들이 수행됨을 확인하고 작업을 종료한다.
이렇게 작업의 수행과정을 살펴보면 위 코드에서 setImmediate()가 setTimeout()보다 먼저 실행됨을 확인할 수 있다.
'NodeJS' 카테고리의 다른 글
Node.js는 무엇인가? (15) | 2024.09.19 |
---|