남잭슨의 개발 블로그

[NodeJS ]1. Nodejs 동작원리 및 이벤트 루프, 논블로킹I/O (Event Loop , Non-Blocking) 본문

NodeJs

[NodeJS ]1. Nodejs 동작원리 및 이벤트 루프, 논블로킹I/O (Event Loop , Non-Blocking)

남잭슨 2019. 6. 17. 05:29

NodeJS란 ? 

비동기 이벤트 기반 주도 , JavaScript 런타임으로써, Nodejs는 확장성 있는 네트워크 애플리케을 만들수 있는 언어.

Chrome V8엔진 기반의 이벤트 기반( Event-driven ) , 논-블로킹 I/O ( Non-Blocking) 패러다임을 채택한 언어로 

가볍고 빠르다. 

 

정확한 설명은 공식 문서를 참고하자! 

https://nodejs.org/ko/about/

 

Nodejs의 장점 및 동작원리에 대해 알아보자

크게 3가지를 알아볼것이다.

1. 이벤트 기반 ( Event-Driven) - 이벤트 루프 동작원리

2. 논블로킹 I/O ( Non-Blocking I/O)

3. 싱글 스레드

각각의 특징들이 어떤식으로 동작하는지 간단하게 알아보겠다.! 

 

1. 이벤트 기반 (Event-Driven)

- 이벤트가 발생하였을때, 저장해둔 작업을 수행하는 방식 

( ex>클릭 리스너, 네트워크 요청에 대한 처리 등등)

 

- 이벤트 리스너에 미리 콜백함수를 저장해놓고, 입력된 이벤트에 따라 

해당하는 작업을 수행한다

ex> 요청 url에 해당하는 콜백함수 선언

/about이라는 요청이 들어오면 res.send()를 호출하는 콜백함수가 실행된다.

 

- 발생한 이벤트는 순차적으로 처리하며, 발생한 이벤트가 없다면, 대기한다.

 

-이벤트 루프가 이벤트 처리 순서를 관리해준다. 

 

그렇다면 이벤트 루프란 무엇인가?

이벤트 루프를 설명하기 위해, Java Script의 동작 원리를 알아보자

JavaScript는 싱글스레드 기반이기 때문에 

console.log("Hi 1 !");
console.log("Hi 2 !");
console.log("Hi 3 !");

위코드를 실행하면. 위에서 아래로 순차적으로 실행이 된다. 

 

하지만 만약 중간에 오래걸리는 작업이 있다면, 

해당 작업이 완료되기전까지 그 아래 작업은 진행되지 않는다.

console.log("Hi 1 !");
Delay() // 오래걸리는 작업
console.log("Hi 2 !");
console.log("Hi 3 !");

 

다음 코드의 호출 스택을 확인해보자.

function one(){
 two();
 console.log("One");
}
function two(){
 three();
 console.log("Two");
}
function three(){
 console.log("Three");
}
one();

해당 코드의 호출 스택을 보면 아래와 같다.

 

자 이제 아래처럼 TimeOut이 포함된 코드의 호출 스택을 생각해보자!

console.log("start!");

setTimeout(function timeout(){
	console.log("Time Out log");
},5000);

console.log("end!");

물론 실행 순서는 예측할수 있다.

 

start!

end!

(5초이후)Time Out log

 

하지만 호출 스택을 어떻게 표현하고, 동작할까?

 

※참고 : 자바스크립트는 싱글스레드 기반에서 콜백이 실행될수 있는 이유는

Web환경에서 스레드를 제공해주는api환경을 제공해준다. 

( ex> Dom(Documnet), Ajax, setTimeOut )

 

위코드의 호출스택을 이해하기 위해 3가지 요소를 이해해야한다.

- 이벤트 루프(Event loop) : 이벤트 발생시, 콜백함수를 관리하고, 실행순서를 관리하는 역할

- 태스크 큐(Task Queue) : 이벤트 발생 후, 실행되어야 하는 작업들이 순차적으로 대기하는 공간

- 백그라운드(Background) : Timer, I/O , 이벤트 리스너 등등

 

전체 호출스택을 표현하자면 이렇게 된다.

1. 먼저 실행 코드들이 호출스택에 쌓인다.

2. 실행 코드중 백그라운드가 필요한 작업은 백그라운드공간으로 이동하고, 순차적으로 호출스택이 진행된다.

3. 백그라운드에서 자체적으로 작업완료된 콜백함수는 태스크큐로 이동하여 대기한다.

4. 호출 스택들이 완료되면 이벤트 루프(Event Loop) 가 태스크 큐에 있는 콜백을 순서대로 호출스택에 올린다.

5. 호출스택이 완료되면 이벤트루프(Event Loop) 가 돌면서 태스크 큐에 있는 콜백을 실행시키고,

6. 태스크 큐에 아무것도 없다면, 이벤트가 올때까지 대기한다.

 

 

참고 링크 ( 자바스크립트 이벤트 루프 상세한 내용 및 연습 동작 샘플)

http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D

 

http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIHNldFRpbWVvdXQoZnVuY3Rpb24gdGltZXIoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ1lvdSBjbGlja2VkIHRoZSBidXR0b24hJyk7ICAgIAogICAgfSwgMjAwMCk7Cn0pOwoKY29uc29sZS5sb2coIkhpISIpOwoKc2V0VGltZW91dChmdW5jdGlvbiB0aW1lb3V0KCkgewogICAgY29uc29sZS5sb2coIkNsaWNrIHRoZSBidXR0b24hIik7Cn0sIDUwMDApOwoKY29uc29sZS5sb2coIldlbGNvbWUgdG8gbG91cGUuIik7!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D

 

latentflip.com

아래는 전체적인 JS 이벤트 루프 내용이다.

 

다시 노드의 동작원리 및 장점으로 들어와서 

 

2. 논 블록킹 I/O ( Non-Blocking I/O) 에 대해 알아보자 

- 논-블록킹은 이전 작업이 완료될때 까지, 기다리면서 멈추지 않고, 다음작업이 지연되지 않게 동작하는 패러다임이다.

- 오래걸리는 작업은 백그라운드에서 진행하며 완료 후, 이벤트 루프를 통해 태스크 큐를 거쳐 호출스택에 올라오길 기다리는 방식

- (시간이 많이 걸리는) 다른 컴퓨팅 자원에 접근하는 I/O(=파일 입/출력,네트워크등)에서 논 블로킹(Non-Blocking) 방식으로 채택됨 

 

논 블로킹I/O는 비동기 개념으로 쉽게 이해할수 있다.

fun longTimeTask(){
	//...오래걸리는작업
    console.log("작업 끝");
}
console.log("시작");
longTimeTask();
console.log("끝, 다음작업");

위 코드를 실행하면 

 

시작

(//오래걸리는작업 이후) 작업끝

끝, 다음작업

 

이런 순서대로 실행이 된다. 

하지만 오래걸리는작업 (longTimeTask()의 함수)에서 

오래걸린다면 다음 작업 시작까지 오래걸린다.

 

fun longTimeTask(){
	//...오래걸리는작업
    console.log("작업 끝");
}
console.log("시작");
setTimeout(longTimeTask(),0);
console.log("끝, 다음작업");

하지만 위처럼 setTimeout을 사용해서, 실행하면

 

시작

끝, 다음작업

(//오래걸리는작업 이후) 작업끝

 

이런 순서로 실행된다. 

오래걸리는 작업 ( longTimeTask)를 논-블로킹 으로 처리하기때문에 

다음작업까지, 기다리지않고 바로 실행이 된다.

 

 

3. 다음은 노드의 싱글스레드이다.

NodeJs는 논/블로킹기반의 싱글스레드로 동작하고 있다.

(사실 NodeJs는 내부적으로 여러 스레드가 동작하지만, 개발자가 제어하는건 싱글스레드이다.)

 

아래의 예시다. 

주방 (요리 ) - I/O작업

점원 - 스레드

고객 - 요청

으로 생각하고 이해하면된다.

 

위의 그림은 스레드(점원)가 하나뿐이고, 

한 요청(고객)에 주문-> 요리 ->서빙 이 실행되지만,

Non-Blocking을 채택하지 않아서,

다른 요청들(고객2,고객3)은 스레드(점원)의 순서가 올때까지 대기한다. 

 

아래는 Non-Blocking을 채택한 싱글스레드 기반의 예시다.

스레드(점원)가 하나인것은 똑같지만, 주방요리(I/O)작업은 Non-Blocking작업으로 백그라운드에서 작업하므로

요청(고객)이 대기하지 않아도, 바로 주문을 할수 있고, 

주문후, 요리가 완료되면 바로바로 서빙이된다. 

 

이렇게 NonBlocking 싱글스레드로

아래의 멀티스레드 (점원여러명)와 비슷하게 처리할 수 있다.

 

 

 

이렇게 효율적으로 자원을 관리하기 때문에

멀티 스레드 기반의 프로그램보다, 

메모리나 기타 자원이 적게 소모된다.

이런면이 노드 Js 의 큰장점이다

 

 

☆ 필요시 NodeJs는 스레드를 늘리는 멀티스레드가 아닌

여러개의 프로세스를 동작하는 멀티프로세싱을 채택한다! (추후 , cluster모듈 참고)

 

'NodeJs' 카테고리의 다른 글

[Node.JS]Nodejs에서 OracleDB(oracledb) 연동하기(Winodw)  (0) 2017.06.12
Comments