Smart Dashboard라는 장난감을 하나 만들면서 서버단을 Node.js로 코딩을 하고 있다. Node.js에서 가장 햇갈리는 부분 그리고 인식의 전환을 해야하는 것들을 "Art of Node"에서 잘 정리해 주고 있다. Node.js의 핵심은 무엇인지 알아보자.
1. Callback
- Node.js 는 I/O Platform 이다 : filesystm과 network I/O를 위한 플랫폼이다. Gateway와 같다고 생각해 보자~~
- Callback은 node.js에서 나온 개념이 아니라 이미 있던 개념 : C언어에서 "함수포인터"와 같다
- Async가 기본이기 때문에 결과에 대한 처리를 요청하는 Callback function을 파라미터로 넘긴다
- 다음 예는 undefined가 나온다. Async이기 때문에 addOne() 호출(invoke)하고 바로 console.log(myNumber)으로 이동해 버린다. File I/O보다 Memory I/O가 훨씬 더 빠른다.
var fs = require('fs') // require is a special function provided by node var myNumber = undefined // we don't know what the number is yet since it is stored in a file function addOne() { fs.readFile('number.txt', function doneReading(err, fileContents) { myNumber = parseInt(fileContents) myNumber++ }) } addOne() console.log(myNumber) // logs out undefined -- this line gets run before readFile is done
- 다음 예가 정확한 예이고 Closure개념을 결합하여 정보를 받아서 출력하고 있다. callback을 addOne 함수의 파라미터로 넘겨서 fs.readFile내부의 doneReading Callback 펑션에서 Closure객체로 callback()을 호출하는 것이다
var fs = require('fs') var myNumber = undefined function addOne(callback) { fs.readFile('number.txt', function doneReading(err, fileContents) { myNumber = parseInt(fileContents) myNumber++ callback() }) } function logMyNumber() { console.log(myNumber) } addOne(logMyNumber)
- 이것에 대한 Pseudo Pattern 정리하면 다음과 같다
function addOne(thenRunThisFunction) { waitAMinute(function waitedAMinute() { thenRunThisFunction() }) } addOne(function thisGetsRunAfterAddOneFinishes() {})
- Callback을 하다보면 간혹 이런 가독성이 떨어지는 문구를 볼 수 있다. 이에 대한 해결은 Defer/Promise 를 사용한다. Java 진영에서는 Future라고 한다. 주로 Async 프로그래밍시 Callback을 사용할 때의 문제점을 serial하게 해결할 수 있는 do -> then -> error 처리가 가능함
a(function() {
b(function() {
c()
})
})
2. Events
- Node.js는 Even Machine (about ruby on rails or twisted of Python) 이다
- Observer Pattern을 사용하고, Publish/Subscribe 개념을 갖는다
- on 펑션을 통하여 Subscribe 하고, emit 펑션을 통하여 Publish 한다
- 하기 예는 Subscribe하여 처리할 Handler를 Callback 펑션 정의(Definition)을 미리한다. 그리고 connect() 펑션에 인자로 넣는다
var chatClient = require('my-chat-client') function onConnect() { // have the UI show we are connected } function onConnectionError(error) { // show error to the user } function onDisconnect() { // tell user that they have been disconnected } function onMessage(message) { // show the chat room message in the UI } chatClient.connect( 'http://mychatserver.com', onConnect, onConnectionError, onDisconnect, onMessage )
- 위 예를 다른 형태로 변경하면, 즉, connect() 메소드에서 작용하는 원칙은 다음과 같다. on(<EventName>, <Callback Function>) 를 통하여 Subscribe하는 것과 동일하다
var chatClient = require('my-chat-client').connect()
chatClient.on('connect', function() {
// have the UI show we are connected
})
chatClient.on('connectionError', function() {
// show error to the user
})
chatClient.on('disconnect', function() {
// tell user that they have been disconnected
})
chatClient.on('message', function() {
// show the chat room message in the UI
})
3. Stream
- Stream I/O 를 통하여 끊김없이 file system, network I/O를 수행할 수 있다. 성능의 병목없이 물흐르듯 데이터가 흘러간다.
- Substack사의 Stream Handbook을 보자
- stream module은 노드의 코어모듈이다 : require('stream')
- 하기 코드는 data.txt 파일읽는 것이 다 끝나면 Callback의 data로 전달이 된다. 이때 data.txt파일이 크다면 그만틈의 메모리를 사용하게 되고 병목이 생길 수 있다. Async 방식을 고려한다면 성능상의 문제를 야기할 수 있겠다. data.txt파일 읽으면서 http응답 병목발생.
var http = require('http');
var fs = require('fs');
var server = http.createServer(function (req, res) {
fs.readFile(__dirname + '/data.txt', function (err, data) {
res.end(data);
});
});
server.listen(8000);
var http = require('http');
var fs = require('fs');
var server = http.createServer(function (req, res) {
var stream = fs.createReadStream(__dirname + '/data.txt');
stream.pipe(res);
});
server.listen(8000);
.pipe()
is just a function that takes a readable source stream src
and hooks the output to a destination writable stream dst
: - 읽을 소스 스트림 src가 있고 쓸 스트림으로 output을 연결해 주는 펑션일 뿐이다
src.pipe(dst)
a.pipe(b).pipe(c).pipe(d)
위 아래 동일 표현
a.pipe(b);
b.pipe(c);
c.pipe(d);
the command-line to pipe programs
a | b | c | d
var Readable = require('stream').Readable;
var rs = new Readable;
rs.push('beep ');
rs.push('boop\n');
rs.push(null);
rs.pipe(process.stdout);
$ node read0.js
beep boop
var Writable = require('stream').Writable;
var ws = Writable();
ws._write = function (chunk, enc, next) {
console.dir(chunk);
next();
};
process.stdin.pipe(ws);
$ (echo beep; sleep 1; echo boop) | node write0.js
<Buffer 62 65 65 70 0a>
<Buffer 62 6f 6f 70 0a>
4. Modules
- 모듈개념으로 npm(Node Package Manager)를 통하여 필요한 것을 설치한다 : 34,000 개가량의 모듈이 등록되어 있음
- 모듈 찾기 npm search <ModuleName> : npm search pdf
- package.json 안에 해당 모듈이 사용하는 다른 모듈 정보와 모듈의 일반 정보(이름, 버전, 저장소등)가 있다 : npm init
- node코드에서 require('some-module'); 를 찾는다. require 동작 순서
some_module.js
exists in the current folder node will load that, otherwise - 현재 디렉토리에 있는지 찾는다 node_modules
folder with a some_module
folder in it - 현재 디렉토리에 node_modules 디렉토리가 있는지 찾고 있으면 그 밑으로 some_module 폴더가 있는지 찾는다 <참조>
'NodeJS > Concept' 카테고리의 다른 글
[Node.js] 배우는 방법 (2) | 2014.04.25 |
---|---|
[Sails.js] Ruby on Rails 가 있다면 Node on Sails 도 있다 (0) | 2013.09.23 |
[Node.js] 애플리케이션 개발을 위한 기본 스택 준비하기 & Sails.js (0) | 2013.05.07 |
[Open API] Apigee.com에서 이야기하는 Open API 원리 (1) | 2013.03.22 |
[Node.js] Deview 발표자료를 보고서 느낀점 (0) | 2013.03.14 |