Node.js
주요 특징들
노드의 의도된 용도(Intended Purpose)
노드는 Scalable한 네트워크 어플리케이션을 제작하기 위해 만들어졌다. Scalable하다는 것은 크기를 늘였다 줄였다 자유롭게 할 수 있다라는 의미로 보면 된다. Lightweight한 규모부터 매우 Heavy traffic을 감당할 수 있는 규모까지 아우른다고 보면 된다.
이벤트 드리븐 구조
다수의 연결 요청은 concurrent하게 처리된다. Concurrent라는 것은 한번에 여러개가 동시에 처리된다는 뜻으로 보면 된다. Parallel과 다른 점은, Parallel은 실제 물리적으로 동시에 처리가 되는 것이고, Concurrent는 물리적으로는 동시가 아니지만, 겉보기에는 동시에 처리되는 것 처럼 보인다, 즉 논리적으로 동시에 처리가 된다는 차이점이 있다.
매 연결이 이루어질 때 마다 callback이 실행되지만 만약 처리할 일이 없으면 Node는 sleep상태로 쉬고 있게 된다.
Non-Blocking I/O
이러한 방식은 최근에 가장 많이 쓰는 OS 쓰레드를 사용한 concurrency 모델과는 다르다. 쓰레드 기반 네트워크는 비교적 비효율적이고 사용하기 매우 어렵다. 게다가 노드에서는 프로세스의 데드락과 같은 상황을 고려하지 않아도 된다. 왜냐면, lock을 사용하지 않기 때문에! Node에 있는 대부분의 함수는 I/O를 직접적으로 수행하지 않기 때문에, 노드 프로세스는 절때 block되지 않는다. Block이 없기 때문에 노드로 확장성 있는 시스템을 작성하면 좋다.
이벤트 루프 구조
노드의 설계는 루비의 이벤트 머신과 파이썬의 Twisted의 영향을 받았고, 따라서 비슷한 구조를 갖는다. 노드는 거기에 이벤트 모델이 적용되었고, 런타임을 만드는데에 라이브러리로서가 아닌 이벤트 루프가 존재하게 되었다. 다른 시스템의 경우 이벤트 루프를 시작하기 위해 항상 blocking 호출이 선행된다. 일반적으로 어떠한 동작을 할 것인지의 내용은 스크립트 시작 부분의 콜백을 통해서 시작되며, 그리고 스크립트 끝 부분에는 EventMachine::run()과 같은 blocking 호출을 통해서 서버가 시작됩니다. 노드에는 start-the-event-loop 와 같은 함수 호출이 없습니다. 노드는 단순히 서버 스크립트를 실행하고 나서 이벤트 루프로 들어갑니다. 더이상 처리해야할 콜백이 없을 경우 노드는 이벤트 루프를 탈출한다. 이러한 동작 방식은 브라우저에서 자바스크립트가 실행되는 방식과 비슷하다. 이벤트 루프가 유저로 부터 숨겨져 있는 방식이다.
HTTP는 스트리밍과 짧은 latency을 염두해 두고 설계된 Node의 첫번째 모듈이다. 따라서 Node는 웹 라이브러리 또는 프레임 워크의 기반으로 적합하다.
노드는 쓰레딩을 사용하지 않도록 설계되었다고 해서 당신의 시스템의 멀티코어를 활용하지 못하는 것은 아니다. child_process.fork() API를 이용하여 자식 프로세스를 만들어서 해당 프로세스와 쉽게 통신을 할 수 있다. 같은 인터페이스를 기반으로 구축된 cluster 모듈을 통해서 프로세스간 소캣을 공유하여 여러개의 코어에 로드 밸런싱을 할 수 있도록 해준다.
관련 개념들
Blocking vs Non-Blocking
Blocking
Concurrency와 Throughput
Blocking코드와 Non-Blocking 코드를 섞어 쓸 시 위험성
const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
if (err) throw err;
console.log(data);
});
fs.unlinkSync('/file.md');
위 예제를 보면 fs.unlinkSync()는 fs.readFile()이 실행되기 전에 실행 될 것같다. 실제 file.md가 읽어지기 전에 말이다.
따라서 순서가 제대로 보장되게 하기 위해서 non-blocking으로만 작성해서 맞는 방법으로 코드를 다시 작성해 보았다.
const fs = require('fs');
fs.readFile('/file.md', (readFileErr, data) => {
if (readFileErr) throw readFileErr;
console.log(data);
fs.unlink('/file.md', (unlinkErr) => {
if (unlinkErr) throw unlinkErr;
});
});
fs.unlink() 함수는 fs.readFile()함수의 콜백 내부에서 호출되어서, 맞는 호출 순서를 보장하게 된다.
참고
'개발 & CS 지식' 카테고리의 다른 글
[Apple iOS 튜토리얼] Swift로 iOS 앱 개발(1) - Start Developing iOS Apps (Swift) Jump Right In (0) | 2019.01.10 |
---|---|
Node.js Async 이용하여 콜백 함수 풀어내기 (0) | 2018.06.23 |
HLS를 이용한 라이브 라디오 방송 웹 앱 개발기 (2) (3) | 2018.02.25 |
HLS를 이용한 라이브 라디오 방송 웹 앱 개발기 (1) (0) | 2018.02.23 |
자바스크립트 동시성 모델과 이벤트 루프 (0) | 2018.02.21 |