//
Search
🎴

02.이벤트 루프 그리고 논 블로킹 그리고 노드 정리

저번예제

function run() { console.log('3초 후 실행'); } console.log('시작'); setTimeout(run, 3000); console.log('끝');
JavaScript
복사
실행화면
순서가 이렇게 나오는 이유는 setTimeout 이라는 함수는 스레드 풀에 들어가서 대기한 다음 출력되는 형태로써 단일스레드라는 노드JS 특성과 이해하기 혼돈할 점이 많지만 스레드풀에 있는 4개의 스레드와 같이 병행하며 작업한다는 점을 잊지말자.
호출 스택에서 setTimeout 함수의 콜백 결과인 run이 호출 스택에 언제 들어기는지에 대해서는 선행 학습 내용이 필요하다.
이벤트 루프: 이벤트 발생 시 호출할 콜백 함수들을 관리하고, 호출된 콜백 함수의 실행 순서를 결정하는 역할을 담당한다. 노드가 종료될 때까지 이벤트 처리를 위한 작업을 반복하므로 루프(loop)라고 부른다.
백그라운드: setTimeout 같은 타이머나 이벤트 리스너들이 대기하는 곳이다. 자바스크립트가 아닌 다른 언어로 작성된 프로그램이라고 봐도 된다. 여러 작업이 동시에 실행될 수 있다.
태스크 큐: 이벤트 발생 후, 백그라운드에서는 태스크 큐로 타이머나 이벤트 리스너의 콜백 함수를 보낸다. 정해진 순서대로 콜백들이 줄을 서 있으므로 콜백 큐라고도 부른다. 콜백들은 보통 완료된 순서대로 줄을 서 있지만 특정한 경우에는 순서가 바뀌기도 한다.

중요

1.
호출스택에 전역 컨텍스트인 anonymous 가 먼저 쌓이고 setTimeout()가 호출 스택에 쌓인다.
2.
호출 스택에 들어간 순서와 반대로 실행되므로, setTimeout이 먼저 실행된다.
3.
setTimeout이 실행되면 타이머와 함께 run 콜백을 백그라운드로 보내고, setTimeout은 호출 스택에서 빠진다.
4.
그다음으로 anonymous가 호출 스택에서 빠진다.
5.
백그라운드에서는 3초를 센 후 run 함수를 태스크 큐로 보낸다.
6.
3초를 세었다는 것은 백그라운드에 맡겨진 작업이 완료된 것으로 이해해도 된다.
7.
테스크 큐는 하나의 큐처럼 보이지만 여러개의 큐로 이루어져 있다.
8.
이벤트 루프는 정해진 규칙에 따라 콜백 함수들을 호출 스택으로 부른다.
9.
호출 스택에서 anonymous까지 실행이 완료되어 호출 스택이 비어 있는 상황에서 이벤트 루프는 호출 스택이 비어 있으면 태스크 큐에서 함수를 하나씩 가져와 호출 스택에 넣고 실행한다.
10.
이벤트 루프가 run 콜백을 태스크 큐에서 꺼내 호출 스택으로 올린 상황에서. 호출 스택으로 올려진 run은 실행되고, 실행 완료 후 호출 스택에서 비워진다. 이벤트 루프는 태스크 큐에 콜백 함수가 들어올 때까지 계속 대기
11.
호출 스택에 함수들이 너무 많이 들어 있으면 3초가 지난 후에도 run 함수가 실행되지 않을 수 있다. 이벤트 루프는 호출 스택이 비어 있을 때만 태스크 큐에 있는 run 함수를 호출 스택으로 가져오기 떄문에 setTimeout의 시간이 정확하지 않을 수도 있다.

논 블로킹 I/O

작업에는 두 가지 종류가 있는데, 동시에 실행될 수 있는 작업과 동시에 실행될 수 없는 작업
기본적으로 우리가 작성한 자바스크립트 코드는 동시에 실행될 수 없음.
자바스크립트상에서 돌아가는 것이 아닌 I/O 작업 같은 것은 동시에 처리될 수 있음
I/O는 입력(Input)/출력(Output)을 의미
파일 시스템 접근(파일 읽기, 파일 쓰기, 폴더 만들기 등)이나 네트워크를 통한 요청 같은 작업이 I/O의 일종
이러한 작업을 할 때 노드는 논 블로킹 방식으로 처리하는 방법을 제공

논 블로킹이란 이전 작업이 완료될 때까지 대기하지 않고 다음 작업을 수행

반대로 블로킹은 이전 작업이 끝나야만 다음 작업을 수행하는 것을 의미

노드에서는 I/O 작업을 백그라운드로 넘겨 동시에 처리하곤 한다.

따라서 동시에 처리될 수 있는 작업들은 최대한 묶어서 백그라운드로 넘긴다.

function longRunningTask() { // 오래 걸리는 작업 console.log('오래걸리는 작업 진행'); } console.log('시작'); longRunningTask(); console.log('완료!');
JavaScript
복사
위의 파일을 콘솔창에서 실행 시킬 시 다음과 같은 결과가 나오게 된다.
시작 오래걸리는 작업 진행 다음작업
JavaScript
복사
longRunningTask 함수는 블로킹 I/O 작업을 진행한다.
위의 코드를 논 블로킹 작업으로 만들기 위해서는 다음과 같이 소스코드를 짠다.
function longRunningTask() { // 오래 걸리는 작업 console.log('작업 끝'); } console.log('시작'); setTimeout(longRunningTask, 0); console.log('다음 작업');
JavaScript
복사

아무리 논 블로킹 방식으로 코드를 작성하더라도 코드가 전체 소요 시간이 짧아지지는 않는다. 코드는 서로 동시에 실행되지 않기 때문이다.

I/O 작업이 없다고 해서 논 블로킹이 의미가 없는 것은 아니다.
오래 걸리는 작업을 처리해야 하는 경우, 논 블로킹을 통해 실행 순서를 바꿔줌으로써 그 작업 때문에 간단한 작업들이 대기하는 상황을 막을 수 있다는 점에서 의의가 있다.

논 블로킹과 동시가 같은 의미가 아니다.

밀리초를 0으로 설정했도 브라우저와 노드에서는 기본적인 지연 시간이 있으므로 HTML5 브라우저에서는 4ms, 노드에서는 1ms의 지연 시간이 있다.
JavaScript
복사
노드에서 가장 큰 핵심 기능은 싱글 스레드이다.
싱글 스레드란 스레드가 하나뿐이라는 것을 의미한다.
그래서 자바스크립트 코드가 동시에 실행될 수 없다.
프로세스는 운영체제에서 할당하는 작업의 단위이다.
노드나 웹 브라우저 같은 프로그램은 개별적인 프로세스이다.
프로세스 간에는 메모리 등의 자원을 공유하지 않는다.

즉 운영체제라는 큰 틀 안에서 다수의 프로세스가 있고 프로세스안에 스레드가 여러 개 있는 것이다.

노드가 싱글 스레드다.
하지만 엄밀히 말하면 싱글 스레드로 동작하지는 않는다.
노드를 실행하면 먼저 프로세스가 하나 생성되고 그 프로세스에서 스레드들을 생성하는데, 이때 내부적으로 스레드를 여러 개 생성한다.
그중에서 직접 제어할 수 있는 스레드는 하나다.
스레드를 작업을 사람으로 따지면 손이랑 비슷하다. 하나의 스레드만 직접 조작할 수 있으므로 손은 하나인 셈이다
요청이 많이 들어오면 한 번에 하나씩 요청을 처리한다.
블로킹이 심하게 일어나는 작업을 처리하지만 않는다면 스레드 하나로도 충분하다.
블로킹이 발생할 것 같은 경우에는 논 블로킹 방법으로 대기 시간을 최대한 줄인다.

스레드풀은 노드가 특정 동작을 수행할 때 스스로 멀티 스레드를 사용한다.

노드에서는 하나의 스레드(알바생)이 여러개의 주문을 받고 스레드 풀(주방)에 보낸 다음 무작위로 응답(서빙)을 한다.

멀티스레드로 할 경우에는 요청이 들어올 때 스레드가 늘어나는 것도 문제이지만, 모두 논 블로킹으로 하게 된다면 노드보다 효율성은 좋을 수 있다. 하지만 코딩의 난이도는 배가 된다.