Java 언어가 모든 OS 운영체제에서 Virtual Machine 환경 안에서 Runtime 이 구동 되듯이 Node.JS 는 웹브라우저에 종속적인 자바스크립트에서 외부에서 실행할 수 있는 Runtime 환경을 Chrome V8 엔진을 제공하여 여러 OS 환경에서 실행할 수 있는 환경을 제공하게 됩니다. 이것을 Node.JS 라고 정의할 수 있습니다.
function first() {
second()
console.log('첫 번째 실행')
}
function second() {
third()
console.log('두 번째 실행')
}
function third() {
console.log('세 번째 실행')
}
first()
위 코드의 출력은 세 번째, 두 번째, 첫 번째 실행으로 실행 됩니다. 이 부분의 실행 순서를 이야기 하기 위해선 호출 스택의 자료 구조로 first(), second(), third() 가 메모리에 들어가게 되어 있습니다. 스택의 특징으로는 Last In First Out(후입선출) 의 특징을 가지고 있습니다.
위의 코드는 first, second, third 함수가 정의 되어있고 first 함수 부터 호출을 합니다. 체이닝으로 실행이 연결되어 있고 first가 먼저 호출 되어있지만 second 함수는 first 함수에서 호출되고 second 함수는 third 함수에서 호출되게 됩니다. third 함수는 console.log를 실행하게 되고 가장 윗 부분에 상주해 있다가 스택에서 빠져 나오게 됩니다. 그 다음 second 안에 있는 console.log를 실행하게 되고 최종적으로는 first의 console.log를 가장 나중에 실행하게 됩니다. 결과는 아래와 같습니다.
function run() {
console.log(`3초 후 실행함`)
}
console.log(`시작`)
setTimeout(run, 3000)
console.log(`끝`)
위 코드의 출력은 아래와 같이 실행이 됩니다.
setTimeout은 시간을 지연시켜 함수를 실행하는 타이머 함수입니다. 시간은 정해져있지만 실제로 자바스크립트는 비동기 처리에 대한 함수가 많기 때문에 위의 예제를 통해 이벤트 루프를 설명하자면 이렇습니다.실제로 동기적인 처리를 할 때는 자바스크립트는 컨텍스트 상 호출 스택 구조에서 가장 나중에 처리된 부분이 가장 먼저 처리 되는 현상을 볼 수가 있었습니다. 그 다음 console.log를 통해 찍어주는 단순한 키워드인 ‘시작' 과 ‘끝' 이후 setTimeout 이라는 타이머 함수를 통해 ‘3초 후에 실행함' 이라는 키워드가 가장 마지막에 동작하는 것을 보실 수 있었습니다. 이 때 비동기적인 결과에 대한 부분들은 태스크 큐라는 부분에 적재를 시키게 되고 큐는 자료구조상 First In First Out(선입선출) 특성을 가지고 있습니다.
이 때 3초 뒤에 스택으로 넘겨서 태스크가 실행이 되도록 하게 할 것이냐 라는 문제를 관리하는 놈이 바로 이벤트루프입니다. 이상 대략적인 스택, 태스크 큐 그리고 이벤트 루프의 설명이였습니다.
프론트에서 대부분
<script src='{소스위치}">
// 참조해야 하는 constants.js 파일
module.exports = {
sayHello : 'Hello!',
name : 'CaptainChain'
}
위의 선언된 변수와 오브젝트들은 아래의 코드처럼 참조가 가능합니다.
// funcModule.js
const { sayHello, name } = require('{파일위치}');
console.log(`${name} 님 ${sayHello}`)
Node.js는 위와같은 특성을 가지고 있습니다.
Single Thread, Event Driven, Non-Blocking I/O를 간단히 정리하면 아래와 같습니다.
우선 자바스크립트의 경우 싱글 쓰레드로 처리하는 방식을 채택하고 있습니다. 실제로 처리해야 할 일은 많은데 한번에 하나의 일만 하고 있다는 것으로 생각하면 자바스크립트는 왜 멀티 쓰레드 방식이 아닐까에 대해서는 고민이 자연스럽게 됩니다. 추측으로는 OS 운영체제의 자원을 할당하고 관리하는 부분에서 자바스크립트의 태스크에 대한 제어가 비동기 처리 방식으로 인해 어렵지 않을까 생각됩니다. Node.JS 는 이러한 싱글쓰레드의 단점을 멀티쓰레드 방식과 비슷하게 같은 동작을 병렬로 처리할 수 있는 방법들로 개선하고 있습니다.
이벤트 드리븐이란?
위에서 설명한 개념 중 스택과 이벤트 루프 그리고 태스크 큐에 대한 개념에서 이벤트 드리븐은 내가 서비스하고 있는 하나의 사이트를 통해 기능별로 등록된 리스너들입니다. 위의 코드 처럼 콘솔로 바로 실행 이후 종료되는 프로그램이 아니라 언제 누가 내가 만든 사이트에 들어올지 모르는 상황에서 대기를 하고 있다는 것이죠. 이 때 이벤트별로 기능이 정의가 되게 되는데요. 예를 들어 방문했을 때 text/html contentType으로 사용자에게 페이지를 보여준다던지 게시물에 좋아요 버튼을 눌렀을 때 결과를 json 형식으로 클라이언트에게 보내줘서 사용자의 액션이 제대로 수행이 되었는지 사용자한테 알려줘야 겠지요. 이때 모든 일련의 이벤트들의 동작을 정의하고 등록된 상태가 이벤트 리스너에 등록된 상태입니다.
Non-Blocking I/O 란?
자바스크립트는 기본적으로 싱글쓰레드 방식을 채택중입니다. 이 때 비동기적 처리(Asyncronous Processing)의 태스크들을 호출 스택에서 태스크 큐로 보내거나 태스크 큐로 부터 이벤트 루프를 통해 다시 스택으로 가져오는 I/O의 형태를 Non-Blocking 이라고 하죠. 이에 따라 실행 순서에 영향을 미치는 행위를 Non-Blocking I/O 라고 간단히 말할 수 있을 것 같습니다. 반대로 Blocking 의 경우 동기적 처리(Syncronous Processing)들에 대해 뒤에 작업들이 해당 작업으로 인해 지연되는 현상을 이야기 합니다.