Node.js v0.8.15 Manual & Documentation


Table of Contents

About this Documentation#

이 문서의 목적은 Node.js API를 레퍼런스부터 개념적인 부분까지 광범위하게 설명하는 데 있다. 각 섹션은 내장(built-in) 모듈이나 고수준의 개념을 설명한다.

프로퍼티 타입, 메서드 아규먼트, 이벤트 핸들러의 아규먼트은 제목 밑에 리스트로 상세하게 적혀있다.

.html 문서는 구조화되어 같은 정보를 담고 있는 .json과 대응된다. 이 기능은 실험적이고 문서를 프로그래밍 적인 것으로 다루는 IDE나 다른 유틸리티의 이점을 위해서 추가되었다.

.html.json파일은 node의 소스트리에서 doc/api/ 폴더 안에 대응되는 .markdown 파일로 생성된다. 이 문서는 tools/doc/generate.js 프로그램을 사용해서 생성한다. HTML 템플릿은 doc/template.html에 있다.

Stability Index#

문서 전체에서 섹션의 안정성 지표를 볼 것이다. Node.js API는 아직도 조금씩 바뀌고 있고 성숙도에 따라 어떤 부분은 다른 부분보다 더 신뢰할 수 있다. 어떤 것들은 그렇게 증명되었고 전혀 변경될 가능성이 없어 보이는 것에 의존하고 있다. 다른 것들은 새롭고 실험적이거나 위험하다고 알려져 있거나 새로 디자인하고 있다.

이 표시는 다음과 같다:

Stability: 1 Experimental

안정성 지표는 다음과 같다:

  • 0 - Deprecated 이 기능은 문제의 소지가 있는 것으로 알려졌고 변경될 예정이다. 여기에 의존하지 말아라. 이 기능을 사용하면 경고나 나올 것이다. 하위호환성을 기대하지 말아야 한다.
  • 1 - Experimental 이 기능은 최근에 도입되었고 차기 버전에서 변경되거나 제거될 것이다. 이 기능을 시험해보고 피드백을 주기 바란다. 이 기능이 당신에게 중요한 유즈케이스를 제공한다면 노드 코어팀에게 말해줘라.
  • 2 - Unstable API가 안정화되는 과정에 있지만 아직 안정적이라고 고려될 만큼 충분한 실세계의 테스트를 거치지 않았다. 합당하다면 하위호환성은 유지될 것이다.
  • 3 - Stable API가 충분히 검증되었지만 기반이 되는 코드의 정리때문에 마이너한 변경이 있을 수 있다. 하위호완성이 보장된다.
  • 4 - API Frozen 이 API는 프로덕션레벨에서 광범위하게 테스트되었고 변경될 여지가 거의 없다.
  • 5 - Locked 심각한 버그가 발견되지 않는한 이 코드는 절대 변경되지 않을 것이다. 이 영역에서 변경사항을 제안하지 말아라. 아마도 거절될 것이다.

JSON Output#

Stability: 1 - Experimental

마크다운의 모든 HTML파일은 같은 데이터를 가진 대응되는 JSON 파일이 있다.

이 기능은 node v0.6.12에서 추가되었고 실험적인 것이다.

Synopsis#

Node로 작성한 'Hello World'를 응답하는 web server 예제:

var http = require('http');

http.createServer(function (request, response) {
  response.writeHead(200, {'Content-Type': 'text/plain'});
  response.end('Hello World\n');
}).listen(8124);

console.log('Server running at http://127.0.0.1:8124/');

서버를 실행하려면 example.js파일에 코드를 입력하고 node 프로그램으로 실행해라.

> node example.js
Server running at http://127.0.0.1:8124/

문서의 모든 예제는 비슷하게 실행할 수 있다.

Global Objects#

이 객체들은 모든 모듈에서 이용할 수 있다. 이 객체들 중 일부는 실제로 전역 범위를 가지지 않고 모듈 범위를 가진다. - 이는 따로 표시할 것이다.

global#

  • {Object} 전역 네임스페이스 객체.

브라우저에서 최상위 범위는 전역 범위이다. 이는 브라우저의 전역 범위에서 var something가 전역 변수를 정의한다는 것을 의미한다. Node에서는 다르다. 최상위 범위는 전역 범위가 아니다. Node 모듈에서 var something는 해당 모듈의 지역 범위가 된다.

process#

  • {Object}

process 객체. process object부분을 봐라.

console#

  • {Object}

stdout와 stderr에 출력하는 데 사용한다. stdio부분을 봐라.

Class: Buffer#

  • {Function}

바이너리 데이터를 다루는데 사용한다. buffer section을 봐라.

require()#

  • {Function}

모듈을 require한다. Modules부분을 봐라. require는 실제로 전역이 아니라 각 모듈의 지역범위다.

require.resolve()#

모듈의 위치를 검색하는데 내부 require() 장치(machinery)를 사용한다. 모듈을 로딩하는 것이 아니라 처리된 파일명을 리턴할 뿐이다.

require.cache#

  • Object

모듈을 require했을 때 모듈은 이 객체에 캐시된다. 이 객체에서 키 값을 삭제하면 다음 번 require에서 해당 모듈을 다시 로드할 것이다.

require.extensions#

  • Array

특정 파일 확장자를 어떻게 다룰지를 require에 지시한다.

.sjs 확장자의 파일을 .js처럼 처리한다.

require.extensions['.sjs'] = require.extensions['.js'];

자신만의 확장자 핸들러를 작성한다.

require.extensions['.sjs'] = function(module, filename) {
  var content = fs.readFileSync(filename, 'utf8');
  // 파일 내용을 파싱해서 module.exports에 전달한다
  module.exports = content;
};

__filename#

  • {String}

실행되는 코드의 파일명이다. 이 코드 파일을 처리한 절대경로이다. 메인 프로그램에서 이는 커맨드라인에서 사용한 것과 반드시 같은 파일명은 아니다. 모듈내부에서 이 값은 해당 모듈 파일에 대한 경로이다.

예제: /Users/mjr에서 node example.js를 실행한다.

console.log(__filename);
// /Users/mjr/example.js

__filename은 실제로 전역이 아니라 각 모듈의 지역범위이다.

__dirname#

  • {String}

현재 실행되는 스크립트가 존재하는 디렉토리 이름이다.

예제: /Users/mjr에서 node example.js를 실행한다.

console.log(__dirname);
// /Users/mjr

__dirname는 실제로 전역이 아니라 각 모듈의 지역범위이다.

module#

  • {Object}

현재 모듈에 대한 참조이다. 특히 module.exportsexports 객체와 같다. module는 실제로 전역이 아니라 각 모듈의 지역범위이다.

더 자세한 내용은 module system documentation를 봐라.

exports#

현재 모듈과 require()로 접근가능하게 된 모듈의 모든 인스턴스 사이에서 공유되는 객체다. exportsmodule.exports객체와 동일하다. exports는 실제로 전역이 아니라 각 모듈의 지역범위이다.

더 자세한 내용은 module system documentation를 봐라.

더 자세한 내용은 module section를 봐라.

setTimeout(cb, ms)#

최소 ms 밀리초 후에 콜백 cb를 실행한다. 실제 지연시간은 OS 타이머의 크기와 시스템 부하같은 외부 요소에 달려있다.

타임아웃은 1-2,147,483,647의 범위여야 한다. 값이 이 범위 밖이면 타임아웃은 1 밀리초로 바뀐다. 대략적으로 말해서 타이머는 24.8일이상이 될 수 없다.

타이머를 나타내는 불투명한 값을 반환한다.

clearTimeout(t)#

이전에 setTimeout()로 생성된 타이머를 멈춘다. 콜백은 실행하지 않을 것이다.

setInterval(cb, ms)#

ms 밀리초마다 반복적으로 콜백 cb를 실행한다. 실제 간격은 OS 타이머의 크기나 시스템 부하같은 외부 요소에 따라 다양하다. 시간간격은 ms보다 작을 수 없다.

간격은 1-2,147,483,647의 범위여야 한다. 값이 이 범위 밖이면 1밀리초로 바뀐다. 대력적으로 말해서 타이머는 24.8일 이상이 될 수 없다.

타이머를 나타내는 불투명한 값을 반환한다.

clearInterval(t)#

이전에 setInterval()로 생성된 타이머를 멈춘다. 콜백은 실행하지 않을 것이다.

timer 함수는 전역 변수이다. timers부분을 봐라.

console#

Stability: 4 - API Frozen
  • {Object}

stdout와 stderr에 출력하기 위해 사용한다. 대부분의 웹 브라우저가 제공하는 console 객체의 기능과 유사하게 stdout이나 stderr로 출력한다.

console.log([data], [...])#

새로운 라인으로 stdout에 출력한다. 이 함수는 printf()와 같은 방식으로 여러 아규먼트를 받는다. 예제:

console.log('count: %d', count);

첫 문자열에 포매팅 객체가 없으면 각 아규먼트에 util.inspect를 사용한다. 더 자세한 내용은 util.format()를 봐라.

console.info([data], [...])#

console.log와 동일하다.

console.error([data], [...])#

console.log와 같지만 stderr에 출력한다.

console.warn([data], [...])#

console.error와 같다.

console.dir(obj)#

objutil.inspect를 사용하고 결과 문자열을 stdout에 출력한다.

console.time(label)#

시간을 마킹한다.

console.timeEnd(label)#

타이머를 종료하고 결과를 기록한다. 예제:

console.time('100-elements');
for (var i = 0; i < 100; i++) {
  ;
}
console.timeEnd('100-elements');

console.trace(label)#

현지 위치의 stderr에 스택트레이스를 출력한다.

console.assert(expression, [message])#

expressionfalse이면 message로 AssertionError를 던지는 assert.ok()와 같다.

Timers#

Stability: 5 - Locked

timer 함수는 모두 전역객체이다. timer를 사용하기 위해 이 모듈을 require()할 필요가 없다.

setTimeout(callback, delay, [arg], [...])#

delay 밀리초 후에 callback을 한번만 실행하도록 스케쥴링한다. clearTimeout()과 사용할 수 있도록 timeoutId를 리턴한다. 선택적으로 콜백에 아규먼트를 전달할 수 있다.

콜백이 정확한 delay 밀리초에 실행되지 않을 수도 있다는 점은 중요하다. - Node.js는 콜백이 정확한 타이밍에 실행된다는 것을 보장하지 않고 순차적으로 실행된다는 것도 보장하지 않는다. 콜백은 지정한 시간과 가능한한 가깝게 호출할 것이다.

clearTimeout(timeoutId)#

타임머가 트리거되는 것을 막는다.

setInterval(callback, delay, [arg], [...])#

delay 밀리초마다 callback 실행을 반복하도록 스케쥴링한다. clearInterval()에서 사용할 수 있도록 intervalId를 리턴한다. 선택적으로 콜백에 아규먼트를 전달할 수도 있다.

clearInterval(intervalId)#

인터벌을 트리거되는 것을 멈춘다.

Modules#

Stability: 5 - Locked

매우 간단하게 모듈을 로딩할 수 있다. 노드에서는 파일 하나가 모듈 하나다. 예를 들어 foo.js 파일에서 같은 디렉토리에 있는 circle.js를 로드하는 것을 살펴보자.

foo.js:

var circle = require('./circle.js');
console.log( 'The area of a circle of radius 4 is '
           + circle.area(4));

circle.js:

var PI = Math.PI;

exports.area = function (r) {
  return PI * r * r;
};

exports.circumference = function (r) {
  return 2 * PI * r;
};

circle.js 모듈은 area()circumference()를 Export했다. 뭔가 Export하려면 해당 객체를 exports 객체에 할당한다. exports는 Export하기 위해 사용하는 객체다.

로컬 변수는 모듈 외부에 노출되지 않는다(private). 이 예제에서 PIcircle.js에서만 사용할 수 있는 private 변수다.

이 모듈 시스템은 module이라는 모듈에 구현했다.

Cycles#

두 모듈이 require() 함수로 서로 참조할 때는 한쪽 모듈은 아직 완전히 로딩하지 못한 미완성 모듈을 그냥 반환한다.

이게 무슨 소리냐 하면:

a.js:

console.log('a starting');
exports.done = false;
var b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

b.js:

console.log('b starting');
exports.done = false;
var a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');

main.js:

console.log('main starting');
var a = require('./a.js');
var b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);

main.jsa.js를 로드하고, a.jsb.js를 로드한다. 여기서 b.js는 다시 a.js를 로드하려고 한다. 무한 루프가 생기지 않도록 아직 미완성인 a.js의 exports 객체를 b.js에 반환해 버린다. 그리고 b.js가 완성되면 a.js에 반환된다.

main.js이 두 모듈을 로드할 때는 이미 둘 다 완성됐다. 이 프로그램의 실행 결과는 다음과 같다:

$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true

그러니까 꼭 모듈을 서로 참조하게 하여야 하면 계획을 잘 짜야 한다.

Core Modules#

Node 모듈 중에서는 바이너리로 컴파일해야 하는 모듈이 있다. 코어 모듈은 이 문서 곳곳에서 설명한다.

코어 모듈은 Node 소스코드의 lib/ 폴더에 들어 있다.

모듈을 require하면 항상 코어 모듈이 먼저 로드된다. 예를 들어, require('http')로 로드될 것 같은 파일이 있어도 Node에 들어 있는 HTTP 모듈이 반환된다.

File Modules#

입력한 이름으로 파일을 못 찾으면 Node는 그 이름에 .js, .json, .node를 붙이고 해당 파일이 있는지 찾는다.

.js 파일은 JavaScript 텍스트 파일로 Interpret하고 .json은 JSON 텍스트 파일로 Interpret한다. 그리고 .node 파일은 컴파일한 addon 모듈이라서 dlopen으로 로드한다.

모듈을 절대 경로로 찾을 때는 모듈 이름을 '/'로 시작하면 된다. 예를 들어, require('home/marco/foo.js')/home/marco/foo.js 파일을 로드한다.

모듈을 상대 경로로 찾으려면 모듈 이름이 './'로 시작하면 된다. 즉, foo.js라는 파일에서 require('./circle')라고 호출하면 같은 디렉토리에 있는 circle.js를 로드한다.

'/'이나 './'로 시작하지 않으면 그냥 파일이 아니라 "코어 모듈"이나 node_modules 폴더에 있는 모듈을 찾는다.

주어진 경로가 존재하지 않으면 require()code 프로퍼티를 'MODULE_NOT_FOUND'로 설정해서 Error를 던질 것이다.

Loading from node_modules Folders#

require()에 넘어온 모듈 ID가 네이티브 모듈을 가리키는 것도 아니고, 그 모듈 ID가 '/', './', '../'로 시작하지도 않으면 Node는 그 모듈의 상위 디렉토리에서 찾기 시작한다. 상위 디렉토리에 있는 /node_modules에서 해당 모듈을 찾는다.

만약 못 찾으면 상위상위 디렉토리에서 찾고, 그래도 못 찾으면 상위상위상위 디렉토리에서 찾는다. 루트 디렉토리에 다다를 때까지 계속 찾는다.

예를 들어, 'home/ry/projects/foo.js'라는 파일에서 requre('bar.js')라고 호출하면 다음과 같은 순서로 모듈을 찾는다:

  • /home/ry/projects/node_modules/bar.js
  • /home/ry/node_modules/bar.js
  • /home/node_modules/bar.js
  • /node_modules/bar.js

그래서 해당 프로그램만의 의존성을 독립적으로 관리할 수 있다. 다른 프로그램에 영향을 끼치지 않는다.

Folders as Modules#

모듈을 폴더로 관리하면 프로그램과 라이브러리를 묶음으로 관리할 수 있어 편리하다. 마치 한 파일로 된 모듈처럼 취급한다. 모듈이 폴더일 때 require()는 세 가지 방법으로 모듈을 찾는다.

프로그램 폴더에 package.json 파일을 만들고 main 모듈이 무엇인지 적는다:

{ "name" : "some-library",
  "main" : "./lib/some-library.js" }

이 파일이 ./some-library라는 폴더에 있다고 하고, require('./some-library')를 호출하면 ./some-library/lib/some-library.js를 찾아 로드한다.

Node가 package.json을 읽고 사용하기 때문에 이런 게 가능하다.

그 디렉토리에 package.json 파일이 없으면 Node는 index.jsindex.node 파일을 찾는다. package.json 파일이 없으면 require('./some-library')는 다음과 같은 파일을 로드한다:

  • ./some-library/index.js
  • ./some-library/index.node

Caching#

한 번 로드한 모듈은 계속 캐싱한다. 그래서 require('foo')을 여러 번 호출해도 계속 같은 객체를 반환한다. 단, `require('foo')가 계속 같은 파일을 로드할 때만 그렇다.

require('foo')를 여러 번 호출해도 해당 모듈 코드는 단 한 번만 호출된다. 그리고 아직 미완성인 객체가 반환될 수 있다는 점까지 더하면 특정 모듈이 서로 의존하고 있어도 성공적으로 로드되는 마법이 이루어진다.

어떤 코드가 꼭 여러 번 호출돼야 하면 함수 자체를 Export하고 그 함수를 여러 번 호출하라.

Module Caching Caveats#

모듈은 찾은(resolved) 파일 이름을 키로 캐싱한다. node_modules 폴더에서 로딩하는 것이기 때문에 같은 require 코드라도 호출하는 위치에 따라 찾은 파일이 다를 수 있다. 즉, require('foo')가 다른 파일을 찾아낸다면 다른 객체를 리턴한다.

The module Object#

  • {Object}

모듈에서 module 변수는 해당 모듈 객체를 가리킨다. 특히 module.exportsexports와 같은 객체를 가리킨다. module은 글로벌 변수가 아니라 모듈마다 다른 객체를 가리키는 로컬 변수다.

module.exports#

  • Object

exports 객체는 Module 시스템이 자동으로 만들어 준다. Export하려는 객체를 module.exports에 할당해서 직접 만든 객체가 반환되게 할 수도 있다. .js라는 모듈을 만들어 보자:

var EventEmitter = require('events').EventEmitter;

module.exports = new EventEmitter();

// Do some work, and after some time emit
// the 'ready' event from the module itself.
setTimeout(function() {
  module.exports.emit('ready');
}, 1000);

이 모듈은 다음과 같이 사용한다:

var a = require('./a');
a.on('ready', function() {
  console.log('module a is ready');
});

module.exports에 할당하는 것은 바로 실행되도록 해야 한다. 콜백으로 할당문이 실행되는 것을 미루면 뜻대로 동작하지 않는다. 다음과 같이 하지 마라:

x.js:

setTimeout(function() {
  module.exports = { a: "hello" };
}, 0);

y.js:

var x = require('./x');
console.log(x.a);

module.require(id)#

  • id 문자열
  • Return: 객체 처리된 모듈의 exports

module.require 메소드로 모듈을 로드하면 해당 모듈에서 require()를 호출하는 것처럼 모듈을 로드한다.

이 메소드를 호출하려면 일단 module 객체의 레퍼런스를 얻어야 한다. module 객체의 레퍼런스는 해당 모듈에서만 접근할 수 있고 require()module이 아니라 exports를 리턴하기 때문에 해당 모듈에서 module 객체의 레퍼런스를 직접 리턴해야 한다.

module.id#

  • String

모듈 ID인데 보통은 모듈 파일의 전체 경로를 사용한다.

module.filename#

  • String

모듈 파일의 전체 경로(fully resolved filename).

module.loaded#

  • Boolean

모듈이 로드하고 있는 중인지 다 로드했는지를 나타낸다.

module.parent#

  • Module Object

모듈을 require한 모듈을 가리킨다.

module.children#

  • Array

모듈이 require한 모듈 객체를 가리킨다.

All Together...#

require()로 모듈을 찾을 때 정확한 파일 경로가 궁금하면 require.resolve() 함수로 얻어온다.

require.resolve가 정확히 어떻게 동작하는지 슈도 코드로 살펴보자. 이 슈도 코드는 여태까지 설명한 것을 모두 합쳐 놓은 것이다:

require(X) from module at path Y
1. If X is a core module,
   a. return the core module
   b. STOP
2. If X begins with './' or '/' or '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW "not found"

require(X) from module at path Y
1. If X is a core module,
   a. return the core module
   b. STOP
2. If X begins with './' or '/' or '../'
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW "not found"

LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text.  STOP
2. If X.js is a file, load X.js as JavaScript text.  STOP
3. If X.node is a file, load X.node as binary addon.  STOP

LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
   a. Parse X/package.json, and look for "main" field.
   b. let M = X + (json main field)
   c. LOAD_AS_FILE(M)
2. If X/index.js is a file, load X/index.js as JavaScript text.  STOP
3. If X/index.node is a file, load X/index.node as binary addon.  STOP

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_AS_FILE(DIR/X)
   b. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let ROOT = index of first instance of "node_modules" in PARTS, or 0
3. let I = count of PARTS - 1
4. let DIRS = []
5. while I > ROOT,
   a. if PARTS[I] = "node_modules" CONTINUE
   c. DIR = path join(PARTS[0 .. I] + "node_modules")
   b. DIRS = DIRS + DIR
   c. let I = I - 1
6. return DIRS

Loading from the global folders#

Node는 모듈을 못 찾으면 환경변수 NODE_PATH에 등록된 경로에서도 찾는다. 절대경로를 NODE_PATH에 할당하면 되는데 콜론(:)으로 구분해서 절대경로를 여러 개 등록할 수 있다(주의: 윈도우는 세미콜론(;)으로 구분한다).

그리고 Node는 다른 디렉토리에서도 찾는다:

  • 1: $HOME/.node_modules
  • 2: $HOME/.node_libraries
  • 3: $PREFIX/lib/node

$HOME은 사용자의 홈 디렉토리이고 $PREFIX는 노드에 설정된 node_prefix를 말한다.

왜 그런지 말하자면 길다. 무엇보다 node_modules 폴더를 이용해 모듈을 로컬에 설치하는 것이 좋다. 이 방법이 속도도 더 빠르고 더 안전하다.

Accessing the main module#

node로 어떤 파일을 실행하면 require.main은 그 파일의 module 객체를 가리킨다. 그래서 Node로 파일을 직접 실행한 건지 아닌지 알 수 있다:

require.main === module

foo.js라는 파일에 이런 게 들어 있다고 하자. 이 구문의 결과는 node foo.js로 실행하면 true이고 require('./foo')로 실행하면 false가 된다.

module에는 filename 프로퍼티가 있어서(__filename과 같은 값이다) require.main.filename의 값을 확인하면 처음 실행한 파일을 무엇인지 알 수 있다.

Addenda: Package Manager Tips#

require() 함수는 웬만한 디렉토리면 어디에서나 사용할 수 있다. dpkg, rpm 같은 패키지 매니저처럼 npm도 네이티브 Node 패키지를 아무런 수정 없이 빌드하게 할 수 있다.

다음은 어떻게 디렉토리를 구성해야 모듈이 제대로 동작하는지 설명한다:

모듈은 /usr/lib/node/<some-package>/<some-version>에 설치하는 것을 권장한다. 어떤 패키지의 어떤 버전이 설치됐는지 한 눈에 알 수 있어 좋다.

패키지는 다른 패키지에 의존할 수도 있다. 예를 들어 foo 패키지를 설치하려면 bar 패키지도 설치해야 한다. 그것도 특정 버전의 bar 패키지가 설치돼야 한다. 그리고 bar 패키지도 다른 패키지에 의존할 수 있는데 충돌이 있거나 서로(cycle) 의존할 수도 있다.

Node는 로드할 모듈을 찾을 때 node_modules 폴더에서 필요한 모듈을 찾는다. 그중에 심볼릭 링크가 있으면 그 링크가 가리키는 모듈도 잘 찾는다. 다음과 같이 모듈을 찾는 매커니즘은 매우 간단하다:

  • /usr/lib/node/foo/1.2.3/ - 버전이 1.2.3인 foo 패키지
  • /usr/lib/node/bar/4.3.2/ - foo가 의존하는 bar 패키지
  • /usr/lib/node/foo/1.2.3/node_modules/bar - /usr/lib/node/bar/4.3.2/에 대한 심볼릭 링크
  • /usr/lib/node/bar/4.3.2/node_modules/* - bar가 의존하는 패키지에 대한 심볼릭 링크

그리고 상호 참조나 의존성 충돌이 있어도 모듈을 사용할 수만 있으면 잘 로드한다.

foo 패키지에서 require('bar')라고 하면 /usr/lib/node/foo/1.2.3/node_modules/bar가 가리키는 모듈을 가져온다. 또 그 bar 패키지에서 require('quux')라고 호출하면 /usr/lib/node/bar/4.3.2/node_modules/quux가 가리키는 모듈을 가져온다.

최적화된 방법으로 모듈을 찾는 방법이 있는데 /usr/lib/node 디렉토리가 아니라 /usr/lib/node_modules/<name>/<version>에 모듈을 넣는다. 그러면 Node는 /usr/node_modules이나 /node_modules에서는 모듈을 찾지 않는다.

/usr/lib/node_modules 폴더를 환경 변수 $NODE_PATH에 넣으면 Node REPL에서도 모듈을 사용할 수 있다. require()를 호출한 파일이 있는 곳에서부터 상대경로로 node_modules 폴더에 있는 모듈을 찾기 때문에 패키지는 그 node_modules 폴더 중 한 곳에 넣으면 된다.

Addons#

애드온은 동적으로 공유 객체를 연결합니다. 애드온은 C나 C++ 라이브러리에 연결할 수 있다. API는(현 시점에) 여러 가지 라이브러리에 대한 지식을 포함해서 상당히 복잡하다.

  • V8 자바스트립트, C++ 라이브러리. 자바스크립트와 인터페이스로 연결하는데 사용한다: 객체를 생성하고 함수를 호출하는 등. v8.h 헤더파일(Node 소스트리의 deps/v8/include/v8.h)에 주로 문서화되어 있고 online에서도 확인할 수 있다.

  • libuv, C 이벤트루프 라이브러리. 파일 디스크립터가 읽을 수 있게 되기를 기다린다거나 타이머를 기다리거나 받아야할 신호를 기다려야 할 때는 언제든지 libuv와 인터페이스로 연결할 필요가 있을 것이다. 즉 어떤 I/O라도 수행한다면 libuv를 사용해야 한다.

  • 내부 Node 라이브러리. 가장 중요한 것은 node::ObjectWrap 클래스이다. 아마 이 클래스에서 파생하기를 원할 것이다.

  • 그 밖에. 무엇을 사용할 수 있는지 알고 싶으면 deps/를 봐라.

Node는 실행가능하도록 모든 의존성을 정적으로 컴파일한다. 모듈을 컴파일할 때 이러한 라이브러리의 연결에 대해서 걱정할 필요가 없다.

Hello world#

다음 자바스크립트 코드와 동일한 작은 애드온을 C++로 작성하면서 시작해 보자.

exports.hello = function() { return 'world'; };

우선 hello.cc파일을 생성한다.

#include <node.h>
#include <v8.h>

using namespace v8;

Handle<Value> Method(const Arguments& args) {
  HandleScope scope;
  return scope.Close(String::New("world"));
}

void init(Handle<Object> target) {
  target->Set(String::NewSymbol("hello"),
      FunctionTemplate::New(Method)->GetFunction());
}
NODE_MODULE(hello, init)

모든 Node 애드온은 초기화 함수를 외부에 노출해야 한다.

void Initialize (Handle<Object> target);
NODE_MODULE(module_name, Initialize)

함수가 아니므로 NODE_MODULE뒤에 세미콜론이 없다.(node.h를 봐라.)

module_name은 최종 바이너리의 파일명과 일치시켜야 한다.(.node 접미사는 제외하고)

소스코드는 바이너리 애드온인 hello.node로 빌드되어야 한다. 이를 위해서 JSON과 유사한 형식으로 모듈의 빌드 설정을 나타내는 binding.gyp파일을 생성해야 한다. 이 파일은 node-gyp가 컴파일한다.

{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ]
    }
  ]
}

다음 과정은 현재 플랫폼에 적절한 프로젝트 빌드 파일을 생성하는 것이다. 빌드파일을 생성하기 위해서 node-gyp configure를 사용해라.

이제 build/ 디렉토리에 Makefile(Unix 플랫폼)과 vcxproj(Windows 플랫폼)가 있을 것이다. 그 다음 node-gyp build명령어를 실행한다.

컴파일된 .node 바인딩 파일을 얻었다! 컴파일된 파인딩 파일들은 build/Release/에 있다.

최근에 빌드한 hello.node 모듈을 require함으로써 Node 프로젝트 hello.js에서 바이너리 애드온을 사용할 수 있다.

var addon = require('./build/Release/hello');

console.log(addon.hello()); // 'world'

더 많은 정보는 아래의 패턴들을 보거나 실사용 예제를 보려면

https://github.com/pietern/hiredis-node를 봐라.

Addon patterns#

다음은 애드온 개발을 시작할 때 도움이 될만한 애드폰 패턴들이다. 여러 가지 v8 호출에 대해서는 온라인 v8 reference를 참고하고 핸들, 범위, 함수 템플릿 등과 같이 사용된 여러가지 개념에 대한 설명은 v8의 Embedder's Guide를 참고해라.

이 예제를 사용하려면 node-gyp를 사용해서 컴파일해야 한다. 다음과 같은 binding.gyp 파일을 생성해라.

{
  "targets": [
    {
      "target_name": "addon",
      "sources": [ "addon.cc" ]
    }
  ]
}

하나 이상의 .cc 파일이 있는 경우에 sources 배열에 파일명을 다음과 같이 추가해라.

"sources": ["addon.cc", "myexample.cc"]

binding.gyp 파일을 준비했으면 애드온을 설정하고 빌드할 수 있다.

$ node-gyp configure build

Function arguments#

다음 패턴은 자바스크립트 함수 호출에서 어떻게 아규먼트들을 읽고 결과를 리턴하는 지 보여준다. 다음 파일이 메인파일이고 소스파일인 addon.cc만 필요하다.

#define BUILDING_NODE_EXTENSION
#include <node.h>

using namespace v8;

Handle<Value> Add(const Arguments& args) {
  HandleScope scope;

  if (args.Length() < 2) {
    ThrowException(Exception::TypeError(String::New("Wrong number of arguments")));
    return scope.Close(Undefined());
  }

  if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
    ThrowException(Exception::TypeError(String::New("Wrong arguments")));
    return scope.Close(Undefined());
  }

  Local<Number> num = Number::New(args[0]->NumberValue() +
      args[1]->NumberValue());
  return scope.Close(num);
}

void Init(Handle<Object> target) {
  target->Set(String::NewSymbol("add"),
      FunctionTemplate::New(Add)->GetFunction());
}

NODE_MODULE(addon, Init)

다음 자바스크립트 코드로 이를 테스트할 수 있다:

var addon = require('./build/Release/addon');

console.log( 'This should be eight:', addon.add(3,5) );

Callbacks#

C++ 함수에 자바스크립트 함수를 전달해서 C++ 함수에서 자바스크립트 함수를 실행할 수 있다. 다음은 addon.cc이다:

#define BUILDING_NODE_EXTENSION
#include <node.h>

using namespace v8;

Handle<Value> RunCallback(const Arguments& args) {
  HandleScope scope;

  Local<Function> cb = Local<Function>::Cast(args[0]);
  const unsigned argc = 1;
  Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) };
  cb->Call(Context::GetCurrent()->Global(), argc, argv);

  return scope.Close(Undefined());
}

void Init(Handle<Object> target) {
  target->Set(String::NewSymbol("runCallback"),
      FunctionTemplate::New(RunCallback)->GetFunction());
}

NODE_MODULE(addon, Init)

다음 자바스크립트 코드를 실행해서 이를 테스트 할 수 있다:

var addon = require('./build/Release/addon');

addon.runCallback(function(msg){
  console.log(msg); // 'hello world'
});

Object factory#

createObject()에 전달된 문자열을 출력하는 msg 프로퍼티를 가진 객체를 리턴하는 이 addon.cc 패턴과 함께 C++ 함수내에서 새로운 객체를 생성해서 리턴할 수 있다.

#define BUILDING_NODE_EXTENSION
#include <node.h>

using namespace v8;

Handle<Value> CreateObject(const Arguments& args) {
  HandleScope scope;

  Local<Object> obj = Object::New();
  obj->Set(String::NewSymbol("msg"), args[0]->ToString());

  return scope.Close(obj);
}

void Init(Handle<Object> target) {
  target->Set(String::NewSymbol("createObject"),
      FunctionTemplate::New(CreateObject)->GetFunction());
}

NODE_MODULE(addon, Init)

자바스크립트에서 다음과 같이 테스트한다:

var addon = require('./build/Release/addon');

var obj1 = addon.createObject('hello');
var obj2 = addon.createObject('world');
console.log(obj1.msg+' '+obj2.msg); // 'hello world'

Function factory#

이 패턴은 C++ 함수를 감싸는 자바스크립트 함수를 어떻게 생성하고 리턴하는지 보여준다:

#define BUILDING_NODE_EXTENSION
#include <node.h>

using namespace v8;

Handle<Value> MyFunction(const Arguments& args) {
  HandleScope scope;
  return scope.Close(String::New("hello world"));
}

Handle<Value> CreateFunction(const Arguments& args) {
  HandleScope scope;

  Local<FunctionTemplate> tpl = FunctionTemplate::New(MyFunction);
  Local<Function> fn = tpl->GetFunction();
  fn->SetName(String::NewSymbol("theFunction")); // omit this to make it anonymous

  return scope.Close(fn);
}

void Init(Handle<Object> target) {
  target->Set(String::NewSymbol("createFunction"),
      FunctionTemplate::New(CreateFunction)->GetFunction());
}

NODE_MODULE(addon, Init)

다음과 같이 테스트한다:

var addon = require('./build/Release/addon');

var fn = addon.createFunction();
console.log(fn()); // 'hello world'

Wrapping C++ objects#

new 오퍼레이터로 자바스크립트에서 인스턴스화할 수 있는 MyObject C++ 객체/클래스에 대한 랩퍼(wrapper)를 생성할 것이다. 우선 메인 모듈 addon.cc를 준비하자.

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"

using namespace v8;

void InitAll(Handle<Object> target) {
  MyObject::Init(target);
}

NODE_MODULE(addon, InitAll)

그 다음 myobject.h는 랩퍼가 node::ObjectWrap를 상속받도록 한다:

#ifndef MYOBJECT_H
#define MYOBJECT_H

#include <node.h>

class MyObject : public node::ObjectWrap {
 public:
  static void Init(v8::Handle<v8::Object> target);

 private:
  MyObject();
  ~MyObject();

  static v8::Handle<v8::Value> New(const v8::Arguments& args);
  static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);
  double counter_;
};

#endif

그리고 myobject.cc에서 노출할 다양한 메서드를 구현한다. 여기서 생성자의 프로토타입에 추가해서 plusOne 메서드를 노출했다.

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"

using namespace v8;

MyObject::MyObject() {};
MyObject::~MyObject() {};

void MyObject::Init(Handle<Object> target) {
  // Prepare constructor template
  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
  tpl->SetClassName(String::NewSymbol("MyObject"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  // Prototype
  tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),
      FunctionTemplate::New(PlusOne)->GetFunction());

  Persistent<Function> constructor = Persistent<Function>::New(tpl->GetFunction());
  target->Set(String::NewSymbol("MyObject"), constructor);
}

Handle<Value> MyObject::New(const Arguments& args) {
  HandleScope scope;

  MyObject* obj = new MyObject();
  obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
  obj->Wrap(args.This());

  return args.This();
}

Handle<Value> MyObject::PlusOne(const Arguments& args) {
  HandleScope scope;

  MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());
  obj->counter_ += 1;

  return scope.Close(Number::New(obj->counter_));
}

다음 코드로 테스트한다:

var addon = require('./build/Release/addon');

var obj = new addon.MyObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13

Factory of wrapped objects#

이는 자바스크립트에서 new 오퍼레이터로 명시적인 인스턴스화 없이 네이티브 객체를 생성할 수 있도록 하고 싶을 때 유용하다.

var obj = addon.createObject();
// 대신에:
// var obj = new addon.Object();

addon.cccreateObject 메서드를 등록하자:

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"

using namespace v8;

Handle<Value> CreateObject(const Arguments& args) {
  HandleScope scope;
  return scope.Close(MyObject::NewInstance(args));
}

void InitAll(Handle<Object> target) {
  MyObject::Init();

  target->Set(String::NewSymbol("createObject"),
      FunctionTemplate::New(CreateObject)->GetFunction());
}

NODE_MODULE(addon, InitAll)

myobject.h에서 객체의 인스턴스화를 처리하는 정적 메서드 NewInstance를 도입한다. (예를 들어 자바스크립트에서 new가 하는 일이다.)

#define BUILDING_NODE_EXTENSION
#ifndef MYOBJECT_H
#define MYOBJECT_H

#include <node.h>

class MyObject : public node::ObjectWrap {
 public:
  static void Init();
  static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);

 private:
  MyObject();
  ~MyObject();

  static v8::Persistent<v8::Function> constructor;
  static v8::Handle<v8::Value> New(const v8::Arguments& args);
  static v8::Handle<v8::Value> PlusOne(const v8::Arguments& args);
  double counter_;
};

#endif

myobject.cc에서 구현체는 위와 유사하다:

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"

using namespace v8;

MyObject::MyObject() {};
MyObject::~MyObject() {};

Persistent<Function> MyObject::constructor;

void MyObject::Init() {
  // 생성자 템플릿 준비
  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
  tpl->SetClassName(String::NewSymbol("MyObject"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);
  // 프로토타입
  tpl->PrototypeTemplate()->Set(String::NewSymbol("plusOne"),
      FunctionTemplate::New(PlusOne)->GetFunction());

  constructor = Persistent<Function>::New(tpl->GetFunction());
}

Handle<Value> MyObject::New(const Arguments& args) {
  HandleScope scope;

  MyObject* obj = new MyObject();
  obj->counter_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
  obj->Wrap(args.This());

  return args.This();
}

Handle<Value> MyObject::NewInstance(const Arguments& args) {
  HandleScope scope;

  const unsigned argc = 1;
  Handle<Value> argv[argc] = { args[0] };
  Local<Object> instance = constructor->NewInstance(argc, argv);

  return scope.Close(instance);
}

Handle<Value> MyObject::PlusOne(const Arguments& args) {
  HandleScope scope;

  MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());
  obj->counter_ += 1;

  return scope.Close(Number::New(obj->counter_));
}

다음으로 테스트한다:

var addon = require('./build/Release/addon');

var obj = addon.createObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13

var obj2 = addon.createObject(20);
console.log( obj2.plusOne() ); // 21
console.log( obj2.plusOne() ); // 22
console.log( obj2.plusOne() ); // 23

Passing wrapped objects around#

C++ 객체를 감싸고 리턴하는 부분에 대해서 추가적으로 Node의 node::ObjectWrap::Unwrap 헬퍼 함수로 이 객체들을 풀어줌으로써(unwrapping) 전달할 수 있다. 다음 addon.cc에서 두 MyObject 객체받을 수 있는 add() 함수를 도입한다.

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"

using namespace v8;

Handle<Value> CreateObject(const Arguments& args) {
  HandleScope scope;
  return scope.Close(MyObject::NewInstance(args));
}

Handle<Value> Add(const Arguments& args) {
  HandleScope scope;

  MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>(
      args[0]->ToObject());
  MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>(
      args[1]->ToObject());

  double sum = obj1->Val() + obj2->Val();
  return scope.Close(Number::New(sum));
}

void InitAll(Handle<Object> target) {
  MyObject::Init();

  target->Set(String::NewSymbol("createObject"),
      FunctionTemplate::New(CreateObject)->GetFunction());

  target->Set(String::NewSymbol("add"),
      FunctionTemplate::New(Add)->GetFunction());
}

NODE_MODULE(addon, InitAll)

흥미롭게 myobject.h에서 퍼블릭 메서드를 도입해서 객체를 풀어버린(unwrapping) 후 private 값을 자세히 조사할 수 있다:

#define BUILDING_NODE_EXTENSION
#ifndef MYOBJECT_H
#define MYOBJECT_H

#include <node.h>

class MyObject : public node::ObjectWrap {
 public:
  static void Init();
  static v8::Handle<v8::Value> NewInstance(const v8::Arguments& args);
  double Val() const { return val_; }

 private:
  MyObject();
  ~MyObject();

  static v8::Persistent<v8::Function> constructor;
  static v8::Handle<v8::Value> New(const v8::Arguments& args);
  double val_;
};

#endif

myobject.cc의 구현체는 이전과 유사하다:

#define BUILDING_NODE_EXTENSION
#include <node.h>
#include "myobject.h"

using namespace v8;

MyObject::MyObject() {};
MyObject::~MyObject() {};

Persistent<Function> MyObject::constructor;

void MyObject::Init() {
  // 생성자 템플릿 준비
  Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
  tpl->SetClassName(String::NewSymbol("MyObject"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  constructor = Persistent<Function>::New(tpl->GetFunction());
}

Handle<Value> MyObject::New(const Arguments& args) {
  HandleScope scope;

  MyObject* obj = new MyObject();
  obj->val_ = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
  obj->Wrap(args.This());

  return args.This();
}

Handle<Value> MyObject::NewInstance(const Arguments& args) {
  HandleScope scope;

  const unsigned argc = 1;
  Handle<Value> argv[argc] = { args[0] };
  Local<Object> instance = constructor->NewInstance(argc, argv);

  return scope.Close(instance);
}

다음으로 테스트한다:

var addon = require('./build/Release/addon');

var obj1 = addon.createObject(10);
var obj2 = addon.createObject(20);
var result = addon.add(obj1, obj2);

console.log(result); // 30

process#

process 객체는 전역객체이고 어디서나 접근할 수 있다. 이 객체는 EventEmitter 인스턴스이다.

Event: 'exit'#

프로세스가 종료될 때 발생한다. 이 이벤트는 모듈의 상태를 상수시간으로 확인하는데 좋은 훅(hook)이다.(유닛테스트에서처럼) 메인 이벤트루프는 'exit' 콜백이 종료된 후에는 더이상 실행되지 않으므로 타이머도 스케쥴링되지 않을 것이다.

exit이벤트 예제:

process.on('exit', function () {
  process.nextTick(function () {
   console.log('This will not run');
  });
  console.log('About to exit.');
});

Event: 'uncaughtException'#

예외가 이벤트루프까지 버블링되었을 때 발생한다. 이 예외에 대한 리스너를 추가하면 기본 동작(스택트레이스를 출력하고 종료한다)은 수행되지 않을 것이다.

uncaughtException이벤트 예제:

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});

setTimeout(function () {
  console.log('This will still run.');
}, 500);

// 의도적인 예외로 예외를 처리하지 않는다.
nonexistentFunc();
console.log('This will not run.');

uncaughtException는 세련되지 않은 예외처리 방법이이고 차후에는 제거될 것이다.

uncaughtException를 사용하지 말고 대신 domains를 사용해라. uncaughtException를 사용한다면 처리하지 않은 예외마다 어플리케이션을 리스타트해라!

uncaughtException를 node.js의 On Error Resume Next로 사용하지 마라. 처리하지 않은 예외는 어플리케이션(그리고 node.js 확장)이 정의되지 않은 상태에 있음을 의미한다. 맹목적으로 복구하면 무슨 일이든 발생할 수 있다.

시스템을 업그레이드 하는 중에 파워코드가 빠졌을 때의 복구를 생각해보자. 열의 아홉은 아무일도 발생하지 않을 것이다. 하지만 10번째는 시스템이 깨진다.

주의해라.

Signal Events#

프로세스가 Signal을 받았을 때 발상한다. SIGINT, SIGUSR1와 같은 표준 POSIX 신호이름의 목록은 sigaction(2)를 봐라.

SIGINT 예제:

// stdin를 읽기 시작하므로 종료되지 않는다.
process.stdin.resume();

process.on('SIGINT', function () {
  console.log('Got SIGINT.  Press Control-D to exit.');
});

SIGINT 신호를 보내는 쉬운 방법은 대부분의 터미털 프로그램에서 Control-C를 입력하는 것이다.

process.stdout#

stdout에 대한 Writable Stream이다.

예제: console.log의 정의

console.log = function (d) {
  process.stdout.write(d + '\n');
};

process.stderrprocess.stdout을 쓰기를 할 때 보통 블락킹된다는 점에서 Node의 다른 스크림과는 다르다. 이 둘은 보통의 파일이나 TTY 파일 디스크립터를 참조할 때 블락킹된다. 파이프를 참조하는 경우에는 다른 스크림처럼 넌블락킹이다.

process.stderr#

stderr에 대한 writable stream이다.

process.stderrprocess.stdout는 쓰기를 할 때 보통 블락킹된다는 점에서 Node의 다른 스크림과는 다르다. 이 둘은 보통의 파일이나 TTY 파일 디스크립터를 참조할 때 블락킹된다. 파이프를 참조하는 경우에는 다른 스크림처럼 넌블락킹이다.

process.stdin#

stdin에 대한 Readable Stream이다. stdin 스트림은 기본적으로 멈추기 때문에 stdin에서 읽으려면 process.stdin.resume()를 호출해야 한다.

표준 입력을 열고 두 이벤트를 리스닝하는 예제:

process.stdin.resume();
process.stdin.setEncoding('utf8');

process.stdin.on('data', function (chunk) {
  process.stdout.write('data: ' + chunk);
});

process.stdin.on('end', function () {
  process.stdout.write('end');
});

process.argv#

커맨드라인 아규먼트를 담고 있는 배열이다. 첫 엘리먼트는 'node'일 것이고 두 번째 엘리먼트는 자바스트립트 파일명이 될 것이다. 다음 엘리먼트들은 추가적인 커맨드라인 아규먼트일 것이다.

// process.argv 출력 
process.argv.forEach(function (val, index, array) {
  console.log(index + ': ' + val);
});

다음과 같이 출력된다:

$ node process-2.js one two=three four
0: node
1: /Users/mjr/work/node/process-2.js
2: one
3: two=three
4: four

process.execPath#

프로세스가 시작되는 실행가능한 절대경로명이다.

예제:

/usr/local/bin/node

process.abort()#

이는 node가 abort를 발생시키게 한다. 즉 node가 종료되고 코어파일을 생성한다.

process.chdir(directory)#

프로세스의 현재 워킹디렉토리를 바꾸거나 바꾸는데 실패할 경우 예외를 던진다.

console.log('Starting directory: ' + process.cwd());
try {
  process.chdir('/tmp');
  console.log('New directory: ' + process.cwd());
}
catch (err) {
  console.log('chdir: ' + err);
}

process.cwd()#

프로세스의 현재 워킹디렉토리를 리턴한다.

console.log('Current directory: ' + process.cwd());

process.env#

사용자의 환경변수를 담고 있는 객체다. environ(7)를 봐라.

process.exit([code])#

지정한 code로 프로세스를 종료한다. code를 생략하면 'success' 코드 0을 사용해서 종료한다.

'failure' 코드로 종료하려면:

process.exit(1);

node를 실행한 쉘은 1을 종료코드로 간주할 것이다.

process.getgid()#

Note: 이 함수는 POSIX 플랫폼에서만 사용할 수 있다.(예를 들어 Windows에서는 안된다.)

프로세스의 그룹식별자를 얻는다.(getgid(2)를 봐라.) 이는 그룹 이름이 아니라 숫자로 된 그룹 id 이다.

if (process.getgid) {
  console.log('Current gid: ' + process.getgid());
}

process.setgid(id)#

Note: 이 함수는 POSIX 플랫폼에서만 사용할 수 있다.(예를 들어 Windows에서는 안된다.)

프로세스의 그룹 식별자를 설정한다.(setgid(2)를 봐라.) 이 함수는 숫자로 된 ID나 문자열로 된 그룹명을 모두 받아들인다. 그룹명을 지정하면 이 메서드가 그룹명을 숫자로된 ID로 처리할 때까지 블락킹한다.

if (process.getgid && process.setgid) {
  console.log('Current gid: ' + process.getgid());
  try {
    process.setgid(501);
    console.log('New gid: ' + process.getgid());
  }
  catch (err) {
    console.log('Failed to set gid: ' + err);
  }
}

process.getuid()#

Note: 이 함수는 POSIX 플랫폼에서만 사용할 수 있다.(예를 들어 Windows에서는 안된다.)

프로세스의 사용자 식별자를 얻는다.(getuid(2)를 봐라.) 이는 사용자명이 아니라 숫자로된 userid이다.

if (process.getuid) {
  console.log('Current uid: ' + process.getuid());
}

process.setuid(id)#

Note: 이 함수는 POSIX 플랫폼에서만 사용할 수 있다.(예를 들어 Windows에서는 안된다.)

프로세스의 사용자 식별자를 설정한다. (setuid(2)를 봐라.) 이는 숫자로된 ID와 문자열로 된 사용자명을 모두 받아들인다. 사용자명을 지정하면 이 메서드가 사용자명을 숫자로 된 ID로 처리할 때까지 블락킹한다.

if (process.getuid && process.setuid) {
  console.log('Current uid: ' + process.getuid());
  try {
    process.setuid(501);
    console.log('New uid: ' + process.getuid());
  }
  catch (err) {
    console.log('Failed to set uid: ' + err);
  }
}

process.version#

NODE_VERSION으로 노출된 컴파일된 프로퍼티이다.

console.log('Version: ' + process.version);

process.versions#

node와 의존성에 대한 버전 문자열을 노출하는 프로퍼티이다.

console.log(process.versions);

다음과 같이 출력될 것이다:

{ node: '0.4.12',
  v8: '3.1.8.26',
  ares: '1.7.4',
  ev: '4.4',
  openssl: '1.0.0e-fips' }

process.config#

현재 실행되는 노드를 컴파일하는데 사용한 설정 옵션의 자바스크립트 표현을 담고 있는 객체다. 이는 ./configure 스크립트를 실행했을 때 생성되는 "config.gypi" 파일과 같다.

출력은 다음 예제와 같다.

{ target_defaults:
   { cflags: [],
     default_configuration: 'Release',
     defines: [],
     include_dirs: [],
     libraries: [] },
  variables:
   { host_arch: 'x64',
     node_install_npm: 'true',
     node_install_waf: 'true',
     node_prefix: '',
     node_shared_v8: 'false',
     node_shared_zlib: 'false',
     node_use_dtrace: 'false',
     node_use_openssl: 'true',
     node_shared_openssl: 'false',
     strict_aliasing: 'true',
     target_arch: 'x64',
     v8_use_snapshot: 'true' } }

process.kill(pid, [signal])#

프로세스에 신호를 보낸다. pid는 프로세스 id이고 signal은 보내려는 신호를 설명하는 문자열이다. 신호이름은 'SIGINT'나 'SIGUSR1'같은 문자열이다. signal을 생략하면 'SIGTERM'가 될 것이다. 더 자세한 내용은 kill(2)를 봐라.

이 함수의 이름이 process.kill이므로 실제로 kill 시스템 호출처럼 단순히 신호 전송자이다. 보낸 신호는 타겟 신호를 죽이는 일이외에 다른 일을 할 것이다.

자신에게 신호를 보내는 예제:

process.on('SIGHUP', function () {
  console.log('Got SIGHUP signal.');
});

setTimeout(function () {
  console.log('Exiting.');
  process.exit(0);
}, 100);

process.kill(process.pid, 'SIGHUP');

process.pid#

프로세스의 PID.

console.log('This process is pid ' + process.pid);

process.title#

'ps'에서 표시될 어떻게 표시되는 지에 대한 Getter와 Setter

process.arch#

어떤 프로세스 아키텍쳐에서 실행되고 있는지 보여준다.: 'arm', 'ia32', 'x64'.

console.log('This processor architecture is ' + process.arch);

process.platform#

어떤 플랫폼에서 실행되고 있는지 보여준다. 'darwin', 'freebsd', 'linux', 'sunos', 'win32'

console.log('This platform is ' + process.platform);

process.memoryUsage()#

Node 프로세스의 메모리 사용량을 바이트로 나타내서 보여주는 객체를 리턴한다.

var util = require('util');

console.log(util.inspect(process.memoryUsage()));

다음과 같이 출력될 것이다:

{ rss: 4935680,
  heapTotal: 1826816,
  heapUsed: 650472 }

heapTotalheapUsed는 V8의 메모리 사용량을 참조한다.

process.nextTick(callback)#

이벤트 루프의 다음 번 루프에서 이 callback을 호출한다. 이는 단순히 setTimeout(fn, 0)에 대한 별칭이 아니라 훨씬 더 효율적이다.

process.nextTick(function () {
  console.log('nextTick callback');
});

process.umask([mask])#

프로세스의 파일 모드 생성 마스크를 설정하거나 읽는다. 자식 프로세스는 부모 프로세스에서 이 마스크를 상속받는다. mask아규먼트를 전달하면 이전의 마스크를 리턴하고 mask아규먼트를 전달하지 않으면 현재 마스크를 리턴한다.

var oldmask, newmask = 0644;

oldmask = process.umask(newmask);
console.log('Changed umask from: ' + oldmask.toString(8) +
            ' to ' + newmask.toString(8));

process.uptime()#

Node가 실행되고 있는 시간을 초단위로 나타낸다.

process.hrtime()#

[seconds, nanoseconds] 튜플 배열 형식으로 현재의 고해상도(high-resolution) 실제 시간을 반환한다. 이는 과거 임의의 시간과 관계가 있고 시각과는 관련이 없으므로 클럭 드리프트(clock drift)를 따르지 않는다. 어떤 구간사이의 성능을 측정하는 것이 주요 사용처이다.

어떤 구간을 벤치마킹을 위해 시간 간격을 얻기 위해 process.hrtime()에 이전 호출의 결과를 전달한다.

var time = process.hrtime();
// [ 1800216, 927643717 ]

setTimeout(function () {
  var diff = process.hrtime(time);
  // [ 1, 6962306 ]

  console.log('benchmark took %d seconds and %d nanoseconds',
              diff[0], diff[1]);
  // benchmark took 1 seconds and 6962306 nanoseconds
}, 1000);

util#

Stability: 5 - Locked

이 함수들은 'util' 모듈에 있다. 이 모듈에 접근하려면 require('util')를 사용해야 한다.

util.format(format, [...])#

printf같은 형식으로 첫 아규먼트를 사용해서 포매팅된 문자열을 반환한다.

첫 아규먼트는 플레이스홀더가 포함된 문자열이다.(플레이스 홀더는 없어도 된다.) 각 플레이스 홀더는 대응되는 아규먼트의 값으로 대체된다. 플레이스 홀더는 다음을 지원한다.

  • %s - 문자열.
  • %d - 숫자 (integer 와 float를 모두 지원한다.).
  • %j - JSON.
  • % - 퍼센트기호 ('%'). 이 기호는 플레이스홀더 아규먼트를 사용하지 않는다.

플레이스 홀더에 대응되는 아규먼트가 없으면 플레이스홀더는 치환되지 않는다.

util.format('%s:%s', 'foo'); // 'foo:%s'

플레이스홀더보다 많은 수의 아규먼트가 있으면 남는 아규먼트들은 util.inspect()를 사용해서 문자열로 변환되고 스페이스를 구분자로 이 문자열들을 이어붙힌다.

util.format('%s:%s', 'foo', 'bar', 'baz'); // 'foo:bar baz'

첫 아규먼트가 문자열이 아니라면 util.format()은 모든 아규먼트를 공백문자로 이어붙혀서 리턴한다. 각 아규먼트는 util.inspect()를 통해 문자열로 변환한다.

util.format(1, 2, 3); // '1 2 3'

util.debug(string)#

동기적인 출력함수. 프로세스를 블락할 것이고 stderr에 즉각적으로 string을 출력한다.

require('util').debug('message on stderr');

util.error([...])#

즉시 모든 아규먼트를 stderr에 출력한다는 점을 제외하면 util.debug()와 같다.

util.puts([...])#

동기적인 출력함수. 프로세스를 블락할 것이고 각 아규먼트마다 새로운 라인으로 stdout에 모든 아규먼트를 출력할 것이다.

util.print([...])#

동기적인 출력함수. 프로세스를 블락할 것이고 각 아규먼트를 문자열로 변환해서 stdout에 출력한다. 아규먼트마다 새로운 라인을 넣지 않는다.

util.log(string)#

stdout에 타임스탬프를 출력한다.

require('util').log('Timestamped message.');

util.inspect(object, [showHidden], [depth], [colors])#

디버깅에 유용한 object의 문자열 표현을 리턴한다.

showHiddentrue이면 객체의 enumerable하지 않는 프로퍼티도 보여준다. 기본값은 false이다.

depth는 객체를 포매팅할 때 inspect가 몇 번 재귀를 할 것인지를 지정한다. 이 값은 크고 복잡한 객체를 검사할 때 유용하다.

기본값은 딱 두번만 재귀한다. 무한대로 재귀하려면 depthnull을 지정한다.

colorstrue이면 ANSI 색상코드로 스타일을 입혀서 출력한다.

다음은 util객체의 모든 프로퍼티를 검사하는 예제다:

var util = require('util');

console.log(util.inspect(util, true, null));

객체들도 util.inspect()가 호출해서 객체를 건사한 결과를 사용하는 자신만의 inspect(depth) 함수를 정의할 수 있다.

var util = require('util');

var obj = { name: 'nate' };
obj.inspect = function(depth) {
  return '{' + this.name + '}';
};

util.inspect(obj);
  // "{nate}"

util.isArray(object)#

주어진 "object"가 Array이면 true를 리턴하고 Array가 아니면 false를 리턴한다.

var util = require('util');

util.isArray([])
  // true
util.isArray(new Array)
  // true
util.isArray({})
  // false

util.isRegExp(object)#

주어진 "object"가 RegExp이면 true를 리턴하고 RegExp가 아니면 false를 리턴한다.

var util = require('util');

util.isRegExp(/some regexp/)
  // true
util.isRegExp(new RegExp('another regexp'))
  // true
util.isRegExp({})
  // false

util.isDate(object)#

주어진 "object"가 Date이면 true를 리턴하고 Date가 아니면 false를 리턴한다.

var util = require('util');

util.isDate(new Date())
  // true
util.isDate(Date())
  // false (without 'new' returns a String)
util.isDate({})
  // false

util.isError(object)#

주어진 "object"가 Error이면 true를 리턴하고 Error가 아니면 false를 리턴한다.

var util = require('util');

util.isError(new Error())
  // true
util.isError(new TypeError())
  // true
util.isError({ name: 'Error', message: 'an error occurred' })
  // false

util.pump(readableStream, writableStream, [callback])#

Stability: 0 - Deprecated: readableStream.pipe(writableStream)를 사용해라

readableStream에서 데이터를 읽어서 읽은 데이터를 writableStream으로 보낸다. writableStream.write(data)false를 리턴하면 writableStream에서 drain이벤트가 발생할 때까지 readableStream은 멈출 것이다. callback은 유일한 아규먼트로 error를 받고 writableStream이 닫히거나 오류가 발생했을 때 호출된다.

util.inherits(constructor, superConstructor)#

한 객체의 생성자 에서 다른 객체로 프로토타입 메서드를 상속 받는다. constructor의 프로토타입은 superConstructor에서 생성된 새로운 객체로 설정될 것이다.

superConstructorconstructor.super_ 프로퍼티를 통해서 편리하게 접근할 수 있다.

var util = require("util");
var events = require("events");

function MyStream() {
    events.EventEmitter.call(this);
}

util.inherits(MyStream, events.EventEmitter);

MyStream.prototype.write = function(data) {
    this.emit("data", data);
}

var stream = new MyStream();

console.log(stream instanceof events.EventEmitter); // true
console.log(MyStream.super_ === events.EventEmitter); // true

stream.on("data", function(data) {
    console.log('Received data: "' + data + '"');
})
stream.write("It works!"); // Received data: "It works!"

Events#

Stability: 4 - API Frozen

노드의 많은 객체들은 이벤트를 발생시킨다. net.Server는 서버에 연결이 생길 때마다 이벤트를 발생시키고 fs.readStream는 파일이 열렸을 때 이벤트를 발생시킨다. 이벤트를 발생시키는 모든 객체들은 events.EventEmitter의 인스턴스다. require("events");를 사용해서 이 모듈에 접근할 수 있다.

보통 이벤트명은 카멜케이스의 문자열로 표시하지만 이벤트명에 어떤 제약사항은 없기 때문에 어떤 문자열이라도 사용할 수 있다.

이벤트가 발생할 때 실행할 함수를 객체에 연결할 수 있다. 이러한 함수들을 리스너(listener)라고 부른다.

Class: events.EventEmitter#

EventEmitter 클래스에 접근하려면 require('events').EventEmitter를 사용한다.

EventEmitter 인스턴스에서 오류가 발생하면 보통 'error' 이벤트를 발생시킨다. 노드는 오류 이벤트를 특별한 경우로 대한다. 오류 이벤트에 대한 리스너가 등록되어 있지 않은 경우 기본 동작은 스택 트레이스를 출력하고 프로그램을 종료하는 것이다.

모든 이벤트이미터는 새로운 리스너를 추가되었을 때 'newListener' 이벤트를 발생시킨다.

emitter.addListener(event, listener)#

emitter.on(event, listener)#

지정한 event에 대한 리스너 배열의 끝에 listener를 추가한다.

server.on('connection', function (stream) {
  console.log('someone connected!');
});

emitter.once(event, listener)#

event에 일회성 listener를 추가한다. 이 리스너는 이벤트가 다음 번에 발생했을 때 딱 한번만 실행된 후 제거된다.

server.once('connection', function (stream) {
  console.log('Ah, we have our first user!');
});

emitter.removeListener(event, listener)#

지정한 event에 대한 리스너 배열에서 listener를 제거한다. 주의: 리스너보다 뒤쪽에서 리스너 배열의 배열인덱스를 수정해라.

var callback = function(stream) {
  console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);

emitter.removeAllListeners([event])#

event를 지정하지 않으면 모든 리스너를 제거하고 event를 지정하면 지정한 이벤트의 모든 리스너를 제거한다.

이 함수는 이전에 emitter.listeners(event)가 반환한 모든 배열을 무력화한다.

emitter.setMaxListeners(n)#

기본적으로 EventEmitter는 특정 이벤트에 10개 이상의 리스너가 추가되면 경고메시지를 출력할 것이다. 이 경고메시지는 메모리 누출을 찾는데 도움을 주는 유용한 기본기능이다. 명백히 모든 이미터가 10개로 제한되어야 하는 것은 아닐 것이다. 이 함수로 이 리스너 제한을 늘릴 수 있다. 0을 지정하면 무한대로 등록할 수 있다.

emitter.listeners(event)#

지정한 이벤트의 리스너 배열을 리턴한다.

server.on('connection', function (stream) {
  console.log('someone connected!');
});
console.log(util.inspect(server.listeners('connection'))); // [ [Function] ]

이 배열은 이벤트 하위시스템이 사용하는 의존 리스너의 리스트에 대한 변경될 수 있는 참조가 될 것이다. 하지만 특정 동작은(특히 removeAllListeners) 이 참조를 무력화한다.

특정 지점의 리스너에 대한 변경되지 않는 복사본을 얻으려면 emitter.listeners(event).slice(0)등으로 복사본을 만든다.

node의 차기 버전에서 이 동작은 일관성을 위해서 항상 복사본을 반환하도록 변경될 것이다. 작성하는 프로그램에서 배열 메서드를 사용해서 EventEmitter 리스너를 수동할 수 있도록 하지 마라. 항상 'on' 메서드를 사용해서 새로운 리스너를 추가해라.

emitter.emit(event, [arg1], [arg2], [...])#

전달한 아규먼트의 순서대로 각 리스너를 실행한다.

Event: 'newListener'#

  • event String 이벤트명
  • listener Function 이벤트 핸들러 함수

이 이벤트를 새로운 리스너가 어딘가에 추가될 때마다 발생한다.

Domain#

Stability: 1 - Experimental

도메인은 하나의 그룹으로 여러 가지 다른 IO 작업을 다룰 수 있는 방법을 제공한다. 도메인에 등록된 이벤트 이미터나 콜백 등에서 error 이벤트가 발생하거나 오류를 던졌을 때 process.on('uncaughtException')에서 오류의 컨텍스트를 읽어버리거나 오류 코드로 프로그램이 종료되는 대신 도메인 객체가 이를 인지할 수 있다.

이 기능은 Node 0.8버전에서 새롭게 추가된 기능이다. 처음 도입된 것이고 차후 버전에서 꽤 많은 변경이 있을 것이다. 사용해보고 개발팀에 피드백을 주기 바란다.

아직 실험적인 기능이기 때문에 최소 한번이라도 domain 모듈을 로드하지 않는한 도메인 기능은 사용하지 않도록 되어 있다. 기본적으로 도메인은 생성되거나 등록되지 않는다. 이는 현재 프로그램에 좋지 않은 영향을 주지 않으려는 설계적인 의도이다. 차후 Node.js 버전에서는 기본적으로 활성화되기를 기대한다.

Additions to Error objects#

도데인으로 오류객체가 전달될 때 몇가지 추가적인 필드가 추가된다.

  • error.domain 최초에 오류를 다루는 도메인.
  • error.domain_emitter 오류 객체와 함게 'error' 이벤트를 발생시킨 이벤트 이미터.
  • error.domain_bound 도메인에 바인딩된 콜백함수로 첫 아규먼트로 오류객체를 전달한다.
  • error.domain_thrown 오류가 던져졌는지 오류 이벤트가 발생했는지 바인딩된 콜백함수로 전달되었는지를 나타내는 불리언 값.

Implicit Binding#

도메인을 사용하면 새로 생성되는 모든 EventEmitter 객체(스트림 객체, 요청, 응답등을 포함해서)는 생성되면서 암묵적으로 활성화된 도메인에 바인딩 될 것이다.

게다가 저수준 이벤트 루프 요청(fs.open나 콜백을 받는 메서드같은)에 전달한 콜백은 자동적으로 활성화된 도메인에 바인딩된다. 이 콜백이 예외를 던지면 도메인이 이 오류를 잡아낸다.

과도한 메모리 사용을 막기 위해 도메인 객체 자체는 활성화된 도메인의 자식에 암묵적으로 추가되지 않는다. 도메인 객체가 있다면 요청 객체와 응답객체가 가비지 컬렉트되지 않도록 하는 것은 아주 쉽다.

부모 도메인의 자식처럼 중첩된 도메인 객체가 필요하다면 반드시 명시적으로 추가한 뒤 나중에 정리해야 한다.

암묵적인 바인딩은 던져진 오류나 'error' 이벤트를 도메인의 error 이벤트로 보내지만 도메인에 EventEmitter를 등록하지는 않기 때문에 domain.dispose()를 해도 EventEmitter를 종료하지 않을 것이다. 암묵적인 바인딩은 던져진 오류나 'error' 이벤트만 처리한다.

Explicit Binding#

때때로 사용중인 도메인을 특정 이벤트 이미터에서 사용하지 않아야 하는 경우가 있다. 또는 이벤트 이미터가 한 도메인의 컨텍스트에서 생성되었지만 다른 도메인에 바인딩되어야 하는 경우가 있다.

예를 들면 HTTP 서버에서 사용 중인 도메인이 있지만 각 요청마다 다른 도메인을 사용하길 원할 수 있다.

이는 명시적으로 바인딩해서 할 수 있다.

예를 들면:

// 서버에 대한 최상위 도메인을 생성한다
var serverDomain = domain.create();

serverDomain.run(function() {
// 서버는 serverDomain의 범위내에서 생성되었다
  http.createServer(function(req, res) {
    // req와 res도 serverDomain의 범위내에서 생성되었지만
    // 요청마다 다른 도메인을 사용하길 원한다.
    // 먼저 도메인을 생성하고 req와 res를 도메인에 추가한다
    var reqd = domain.create();
    reqd.add(req);
    reqd.add(res);
    reqd.on('error', function(er) {
      console.error('Error', er, req.url);
      try {
        res.writeHead(500);
        res.end('Error occurred, sorry.');
        res.on('close', function() {
          // 이 도메인에 추가한 다른 것들도 강제적으로 종료한다.
          reqd.dispose();
        });
      } catch (er) {
        console.error('Error sending 500', er, req.url);
        // 최선을 다했다. 남아있는 모든 것을 정리한다.
        reqd.dispose();
      }
    });
  }).listen(1337);
});

domain.create()#

  • return: Domain

새로운 Domain 객체를 반환한다.

Class: Domain#

Domain 클래스는 오류와 잡지 못한 예외를 활성화된 도메인 객체로 보내는 기능을 은닉한다.

Domain은 EventEmitter의 자식 클래스이다. 도메인이 잡은 오류를 다루려면 도메인의 error 이벤트에 리스너를 추가해라.

domain.run(fn)#

  • fn Function

암묵적으로 모든 이벤트 이미터, 타이머, 해당 컨텍스트에서 생성된 저수준 요청을 바인딩한 도메인의 컨텍스트에서 제공된 함수를 실행한다

이는 도메인을 사용하는 가장 기본적인 방법이다.

예제:

var d = domain.create();
d.on('error', function(er) {
  console.error('Caught error!', er);
});
d.run(function() {
  process.nextTick(function() {
    setTimeout(function() { // 여러 비동기의 작업들을 시뮤레이트한다
      fs.open('non-existent file', 'r', function(er, fd) {
        if (er) throw er;
        // 처리중...
      });
    }, 100);
  });
});

이 예제에서 프로그램이 멈추는 대신에 d.on('error') 핸들러가 실행될 것이다.

domain.members#

  • Array

도메인에 명시적으로 추가한 타이머와 이벤트 이미터의 배열

domain.add(emitter)#

  • emitter EventEmitter | Timer 도메인에 추가할 이미터나 타이머

명시적으로 이미터를 도메인에 추가한다. 이미터에서 호출된 이벤트 핸들러가 오류를 던졌거나 이미터에서 error 이벤트가 발생했을 때 암묵적으로 바인딩한 것과 마찬가지로 도메인의 error 이벤트로 전달된다.

이 함수는 setIntervalsetTimeout가 반환하는 타이머에서도 동작한다. 타이머의 콜백함수가 예외를 던지면 도메인의 'error' 핸들러가 잡는다.

Timer나 EventEmitter가 도메인에 이미 바인딩되어 있으면 이전에 바인딩되어 있던 도에인에서는 제거되고 이 도메인에 다시 바인딩된다.

domain.remove(emitter)#

  • emitter EventEmitter | Timer 도메인에서 제거할 이미터나 타이머

domain.add(emitter)과는 반대로 지정한 이미터를 도메인에서 제거한다.

domain.bind(callback)#

  • callback Function 콜백 함수
  • return: Function 바인딩된 함수

전달한 콜백함수를 감싸고 있는 랩퍼 함수를 반환한다. 반환된 함수를 호출했을 때 던져지는 모든 오류는 도메인의 error 이벤트로 전달된다.

Example#

var d = domain.create();

function readSomeFile(filename, cb) {
  fs.readFile(filename, 'utf8', d.bind(function(er, data) {
    // 여기서 오류를 던지면 도메인으로 전달된다.
    return cb(er, data ? JSON.parse(data) : null);
  }));
}

d.on('error', function(er) {
  // 어디선가 오류가 발생함.
  // 여기서 오류를 던지면 프로그램은 보통의 줄번호와
  // 스택 메시지와 함께 종료된다.
});

domain.intercept(callback)#

  • callback Function 콜백 함수
  • return: Function 가로챈 함수

이 함수는 domain.bind(callback)와 대부분 같지만 던져진 오류를 잡기 위해 함수의 첫 아규먼트로 보낸 Error객체를 가로챈다

이 방법을 통해 일반적인 if (er) return callback(er); 패턴을 하나의 오류 핸들러로 바꿀 수 있다.

Example#

var d = domain.create();

function readSomeFile(filename, cb) {
  fs.readFile(filename, 'utf8', d.intercept(function(data) {
    // 첫 아규먼트를 'Error' 라고 가정하지만 도메인이 
    // 가로챘기 때문에 첫 아규먼트는 콜백에 
    // 전달되지 않는다

    // 여기서 오류를 던지면 도메인으로 전달되기 때문에
    // 프로그램 곳곳에 반복해서 오류 핸들링 로직을 작성하는 대신에
    // 도메인의 'error' 이벤트로 오류 핸들링 로직을 옮길 수 있다
    return cb(null, JSON.parse(data));
  }));
}

d.on('error', function(er) {
  // 어디선가 오류가 발생함.
  // 여기서 오류를 던지면 프로그램은 보통의 줄번호와
  // 스택 메시지와 함께 종료된다.
});

domain.dispose()#

dispose 함수는 도메인을 파괴하고 도메인과 연관된 모든 IO를 정리하려고 최선을 다한다. 스트림은 중지되고 종료되고 닫힌 뒤 파괴된다. 타이머는 정리된다. 명시적으로 바인딩된 콜백은 더이상 호출하지 않는다. 여기서 발생한 모든 오류 이벤트를 무시한다.

dispose 호출의 의도는 보통 Domain 컨텍스트의 핵심 부분이 오류 상태라는 것을 발견했을 때 연쇄적인 오류를 막기 위한 것이다.

도메인이 처분되었을 때 dispose 이벤트가 발생할 것이다.

IO는 여전히 수행될 것이다. 하지만 도메인이 처분되고 나면 도메인의 이미터에서 발생하는 추가적인 오류는 무시할 것이다. 그래서 남아있는 작업이 진행중이더라도 Node.js는 그러한 작업과 더이상 통신하지 않을 것이다.

Buffer#

Stability: 3 - Stable

자바스크립트 자체는 유니코드에 친화적이지만 바이너리 데이터에는 별로 좋지 않다. TCP 스트림이나 파일시스템을 다룰 때 옥텟(octet) 스트림을 다룰 필요가 있다. Node에는 옥텟 스트림을 조작하고, 생성하고, 소비하는 여러 전략이 있다.

로우(raw) 데이터는 Buffer클래스의 인스턴스에 저장된다. Buffer는 정수(integer) 배열과 비슷하지만 V8 heap 외부에 할당된 로우 메모리에 대응된다. Buffer는 크기를 다시 조정할 수 없다.

Buffer클래스는 전역범위로 require('buffer')가 필요한 경우는 매우 흔치 않다.

버퍼와 자바스크립트 문자열 객체간에 변환을 하려면 명시적인 인코딩 메서드가 필요하다. 여러 가지 문자열 인코딩이 있다.

  • 'ascii' - 7 비트 ASCII 데이터 전용이다. 이 인코딩 메서드는 아주 빠르고 7비트가 넘는 비트가 있는 경우 제거한다. 이 인코딩은 null 문자('\0''\u0000')를 0x20 (공백의 문자코드)로 변환한다. null 문자를 0x00로 변환하려면 'utf8'를 사용해야 한다.

  • 'utf8' - 멀티바이트로 인코딩된 유니코드 문자다. 다수의 웹페이지와 문서 형식을 UTF-8을 사용한다.

  • 'utf16le' - 2 바이트나 4바이트의 리들 엔디언(little endian)으로 인코딩된 유니코드 문자이다. 대리쌍(surrogate pairs)을 지원한다.(U+10000 ~ U+10FFFF)

  • 'ucs2' - 'utf16le'의 별칭이다.

  • 'base64' - Base64 문자열 인코딩.

  • 'binary' - 각 문자의 첫 8 비트만을 사용해서 로우(raw) 바이너리 데이터를 문자열로 인코딩하는 방법이다. 이 인코딩 메서드는 폐기되었고 Buffer 객체가 가능한 곳에서 사용하지 말아야 한다. 이 인코딩은 Node의 차기 버전에서는 제거될 것이다.

  • 'hex' - 각 바이트를 두 16진수 문자로 인코딩한다.

Buffer도 Typed Array View와 DataView에 사용할 수 있다.

var buff = new Buffer(4);
var ui16 = new Uint16Array(buff);
var view = new DataView(buff);

ui16[0] = 1;
ui16[1] = 2;
console.log(buff);

view.setInt16(0, 1);       // 오프셋 0 바이트에 빅엔디언 int16을 설정한다
view.setInt16(2, 2, true); // 오프셋 2 바이트에 리틀엔디언 int16을 설정한다
console.log(buff);

// <Buffer 01 00 02 00>
// <Buffer 00 01 02 00>

Class: Buffer#

Buffer 클래스는 바이너리 데이터를 직접 다루는 글로벌 타입니다. 다양한 방법으로 생성할 수 있다.

new Buffer(size)#

  • size 숫자

size 옥텟의 새로운 버퍼를 할당한다.

new Buffer(array)#

  • array 배열

옥텟의 array를 사용해서 새로운 버퍼를 할당한다.

new Buffer(str, [encoding])#

  • str 문자열 - 인코딩할 문자열.
  • encoding 문자열 - 사용할 인코딩(선택사항).

주어진 str를 담고있는 새로운 버퍼를 할당한다. encoding의 기본값은 'utf8'이다.

buf.write(string, [offset], [length], [encoding])#

  • string 문자열 - 버퍼에 작성할 데이터
  • offset 숫자, 선택사항, 기본값: 0
  • length 숫자, 선택사항, 기본값: buffer.length - offset
  • encoding 문자열, 선택사항, 기본값: 'utf8'

주어진 인코딩을 사용해서 버퍼의 offset위치에 string을 작성한다. offset의 기본값은 0이고 encoding의 기본값은 'utf8'이다. length는 작성할 바이트의 수이다. 작성된 옥텟의 수를 반환한다. 전체 문자열을 작성하기에 buffer가 충분한 공간을 가지고 있지 않다면 문자열의 일부만 작성할 것이다. length의 기본값은 buffer.length - offset이다. 이 메서드는 문자의 일부만 작성하지는 않는다.

buf = new Buffer(256);
len = buf.write('\u00bd + \u00bc = \u00be', 0);
console.log(len + " bytes: " + buf.toString('utf8', 0, len));

작성한 문자의 수(작성한 바이트의 수와는 다를 수 있다)는 Buffer._charsWritten에 설정되고 buf.write()를 다음 번에 호출했을 때 덮어써질 것이다.

buf.toString([encoding], [start], [end])#

  • encoding 문자열, 선택사항, 기본값: 'utf8'
  • start 숫자, 선택사항, 기본값: 0
  • end 숫자, 선택사항, 기본값: buffer.length

start(기본값은 0)부터 end (기본값은 buffer.length)까지 encoding로 인코딩된 버퍼 데이터를 디코딩해서 문자열을 리턴한다.

buffer.write() 예제를 봐라.

buf[index]#

index위치의 옥텟을 가져오거나 설정한다. 이 값들은 개별적인 바이트를 참조하므로 0x00부터 0xFF의 16진수나 0부터 255 사이의 적법한 범위이다.

예제: ASCII 문자열을 한번에 한 바이트씩 버퍼로 복사한다.

str = "node.js";
buf = new Buffer(str.length);

for (var i = 0; i < str.length ; i++) {
  buf[i] = str.charCodeAt(i);
}

console.log(buf);

// node.js

Class Method: Buffer.isBuffer(obj)#

  • obj 객체
  • 반환타입: 불리언

objBuffer인지 확인한다.

Class Method: Buffer.byteLength(string, [encoding])#

  • string 문자열
  • encoding 문자열, 선택사항, 기본값: 'utf8'
  • 반환타입: 숫자

문자열의 실제 바이트 길이를 리턴한다. encoding의 기본값은 'utf8'이다. String.prototype.length는 스트링에서 문자의 수를 리턴하기 때문에 String.prototype.length와 이 메서드는 같지 않다.

예제:

str = '\u00bd + \u00bc = \u00be';

console.log(str + ": " + str.length + " characters, " +
  Buffer.byteLength(str, 'utf8') + " bytes");

// ½ + ¼ = ¾: 9 characters, 12 bytes

Class Method: Buffer.concat(list, [totalLength])#

  • list 배열 연결할 Buffer 객체의 리스트
  • totalLength 숫자 연결된 버퍼의 전체 길이

list의 모든 버퍼를 연결한 버퍼를 반환한다.

list에 아이템이 없거나 totalLength가 0이면 길이가 0인 버퍼를 반환한다.

list에 딱 하나의 아이템만 있으면 list의 첫 아이템을 반환한다.

list에 하나 이상의 아이템에 있으면 새로운 Buffer가 생성된다.

totalLength를 전달하지 않으면 list의 버퍼들에서 읽어들인다. 하지만 이는 함수에 추가적인 루프가 생기므로 명시적으로 길이를 전달하는 것이 더 빠르다.

buf.length#

  • 숫자

바이트로 나타낸 버퍼 크기. 이는 반드시 내용의 크기인 것은 아니다. length는 버퍼 객체가 할당된 메모리의 양을 참조한다. 버퍼의 내용이 변경되었을 때도 변경되지 않는다.

buf = new Buffer(1234);

console.log(buf.length);
buf.write("some string", 0, "ascii");
console.log(buf.length);

// 1234
// 1234

buf.copy(targetBuffer, [targetStart], [sourceStart], [sourceEnd])#

  • targetBuffer Buffer 객체 - 복사할 Buffer다
  • targetStart 숫자, 선택사항, 기본값: 0
  • sourceStart 숫자, 선택사항, 기본값: 0
  • sourceEnd 숫자, 선택사항, 기본값: buffer.length

버퍼들간에 복사를 한다. 소스영역과 타겟영역은 일치할 수도 있다. targetStartsourceStart의 기본값은 0이다. sourceEnd의 기본값은 buffer.length이다.

예제: 두 버퍼를 만들고 buf1의 16 바이트부터 19 바이트까지를 buf2의 8번째 바이트위치에 복사한다.

buf1 = new Buffer(26);
buf2 = new Buffer(26);

for (var i = 0 ; i < 26 ; i++) {
  buf1[i] = i + 97; // 97 is ASCII a
  buf2[i] = 33; // ASCII !
}

buf1.copy(buf2, 8, 16, 20);
console.log(buf2.toString('ascii', 0, 25));

// !!!!!!!!qrst!!!!!!!!!!!!!

buf.slice([start], [end])#

  • start 숫자, 선택사항, 기본값: 0
  • end 숫자, 선택사항, 기본값: buffer.length

기존의 버퍼가 참조하던 메모리와 같은 메모리를 참조하지만 start (기본값은 0)부터 end (기본값은 buffer.length)의 인덱스로 잘려진 새로운 버퍼를 리턴한다.

새로운 버퍼 부분(slice)을 변경하면 기존 버퍼의 메모리를 변경할 것이다.!

예제: ASCII 알파벳으로 버퍼를 만들고 slice를 한 뒤 기존 버퍼의 한 바이트를 수정한다.

var buf1 = new Buffer(26);

for (var i = 0 ; i < 26 ; i++) {
  buf1[i] = i + 97; // 97 is ASCII a
}

var buf2 = buf1.slice(0, 3);
console.log(buf2.toString('ascii', 0, buf2.length));
buf1[0] = 33;
console.log(buf2.toString('ascii', 0, buf2.length));

// abc
// !bc

buf.readUInt8(offset, [noAssert])#

  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false
  • 반환타입: 숫자

버퍼의 지정한 offset에서 기호가 없은 8비트 정수(unsigned 8 bit integer)를 읽는다.

offset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 offset이 버퍼의 끝을 넘어갈 수도 있다는 의미다. 기본값은 false다.

예제:

var buf = new Buffer(4);

buf[0] = 0x3;
buf[1] = 0x4;
buf[2] = 0x23;
buf[3] = 0x42;

for (ii = 0; ii < buf.length; ii++) {
  console.log(buf.readUInt8(ii));
}

// 0x3
// 0x4
// 0x23
// 0x42

buf.readUInt16LE(offset, [noAssert])#

buf.readUInt16BE(offset, [noAssert])#

  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false
  • 반환타입: 숫자

버퍼의 지정한 offset에서 지정한 엔디언(endian) 형식으로 기호가 없는 16비트 정수(unsigned 16 bit integer)를 읽는다.

offset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 offset이 버퍼의 끝을 넘어갈 수도 있다는 의미다. 기본값은 false다.

예제:

var buf = new Buffer(4);

buf[0] = 0x3;
buf[1] = 0x4;
buf[2] = 0x23;
buf[3] = 0x42;

console.log(buf.readUInt16BE(0));
console.log(buf.readUInt16LE(0));
console.log(buf.readUInt16BE(1));
console.log(buf.readUInt16LE(1));
console.log(buf.readUInt16BE(2));
console.log(buf.readUInt16LE(2));

// 0x0304
// 0x0403
// 0x0423
// 0x2304
// 0x2342
// 0x4223

buf.readUInt32LE(offset, [noAssert])#

buf.readUInt32BE(offset, [noAssert])#

  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false
  • 반환타입: 숫자

버퍼의 지정한 offset에서 지정한 엔디언(endian) 형식으로 기호가 없는 32비트 정수(unsigned 32 bit integer)를 읽는다.

offset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 offset이 버퍼의 끝을 넘어갈 수도 있다는 의미다. 기본값은 false다.

예제:

var buf = new Buffer(4);

buf[0] = 0x3;
buf[1] = 0x4;
buf[2] = 0x23;
buf[3] = 0x42;

console.log(buf.readUInt32BE(0));
console.log(buf.readUInt32LE(0));

// 0x03042342
// 0x42230403

buf.readInt8(offset, [noAssert])#

  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false
  • 반환타입: 숫자

버퍼에서 지정한 offset에서 기호가 있는 8비트 정수(signed 8 bit integer)를 읽는다.

offset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 offset이 버퍼의 끝을 넘어갈 수도 있다는 의미다. 기본값은 false다.

buffer.readUInt8와 같이 동작하지만 버퍼의 내용을 2가지 완전한 기호가 있는 값으로 다룬다는 점이 다르다.

buf.readInt16LE(offset, [noAssert])#

buf.readInt16BE(offset, [noAssert])#

  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false
  • 반환타입: 숫자

버퍼에서 지정한 offset에서 기호가 있는 16비트 정수(signed 16 bit integer)를 읽는다.

offset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 offset이 버퍼의 끝을 넘어갈 수도 있다는 의미다. 기본값은 false다.

buffer.readUInt16*와 같이 동작하지만 버퍼의 내용을 2가지 완전한 기호가 있는 값으로 다룬다는 점이 다르다.

buf.readInt32LE(offset, [noAssert])#

buf.readInt32BE(offset, [noAssert])#

  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false
  • 반환타입: 숫자

버퍼에서 지정한 offset에서 기호가 있는 32비트 정수(signed 32 bit integer)를 읽는다.

offset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 offset이 버퍼의 끝을 넘어갈 수도 있다는 의미다. 기본값은 false다.

buffer.readUInt32*와 같이 동작하지만 버퍼의 내용을 2가지 완전한 기호가 있는 값으로 다룬다는 점이 다르다.

buf.readFloatLE(offset, [noAssert])#

buf.readFloatBE(offset, [noAssert])#

  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false
  • 반환타입: 숫자

버퍼의 지정한 offset에서 지정한 엔디언(endian) 형식으로 32비트 소수(32 bit float)를 읽는다.

offset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 offset이 버퍼의 끝을 넘어갈 수도 있다는 의미다. 기본값은 false다.

예제:

var buf = new Buffer(4);

buf[0] = 0x00;
buf[1] = 0x00;
buf[2] = 0x80;
buf[3] = 0x3f;

console.log(buf.readFloatLE(0));

// 0x01

buf.readDoubleLE(offset, [noAssert])#

buf.readDoubleBE(offset, [noAssert])#

  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false
  • 반환타입: 숫자

버퍼의 지정한 offset에서 지정한 엔디언(endian) 형식으로 64 bit double을 읽는다.

offset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 offset이 버퍼의 끝을 넘어갈 수도 있다는 의미다. 기본값은 false다.

예제:

var buf = new Buffer(8);

buf[0] = 0x55;
buf[1] = 0x55;
buf[2] = 0x55;
buf[3] = 0x55;
buf[4] = 0x55;
buf[5] = 0x55;
buf[6] = 0xd5;
buf[7] = 0x3f;

console.log(buf.readDoubleLE(0));

// 0.3333333333333333

buf.writeUInt8(value, offset, [noAssert])#

  • value 숫자
  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false

버터의 지정한 offset에 value를 작성한다. value는 반드시 유효한 기호가 없은 8비트 정수여야 한다.

valueoffset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 지정한 함수에 value가 너무 크거나 offset이 버퍼의 끝을 넘어가서 값들이 어떤 경고없이 버려질 수 있다는 것을 의미한다. 확실히 정확함을 유지할 수 없다면 사용하지 말아야 한다. 기본값은 false다.

예제:

var buf = new Buffer(4);
buf.writeUInt8(0x3, 0);
buf.writeUInt8(0x4, 1);
buf.writeUInt8(0x23, 2);
buf.writeUInt8(0x42, 3);

console.log(buf);

// <Buffer 03 04 23 42>

buf.writeUInt16LE(value, offset, [noAssert])#

buf.writeUInt16BE(value, offset, [noAssert])#

  • value 숫자
  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false

버퍼의 지정한 offset에 지정한 엔디언(endian) 형식으로 value을 작성한다. value는 반드시 유효한 기호가 없는 16비트 정수여야 한다.

valueoffset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 지정한 함수에 value가 너무 크거나 offset이 버퍼의 끝을 넘어가서 값들이 어떤 경고없이 버려질 수 있다는 것을 의미한다. 확실히 정확함을 유지할 수 없다면 사용하지 말아야 한다. 기본값은 false다.

예제:

var buf = new Buffer(4);
buf.writeUInt16BE(0xdead, 0);
buf.writeUInt16BE(0xbeef, 2);

console.log(buf);

buf.writeUInt16LE(0xdead, 0);
buf.writeUInt16LE(0xbeef, 2);

console.log(buf);

// <Buffer de ad be ef>
// <Buffer ad de ef be>

buf.writeUInt32LE(value, offset, [noAssert])#

buf.writeUInt32BE(value, offset, [noAssert])#

  • value 숫자
  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값 : false

버퍼의 지정한 offset에 지정한 엔디언(endian) 형식으로 value을 작성한다. value는 반드시 유효한 기호가 없는 32비트 정수여야 한다.

valueoffset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 지정한 함수에 value가 너무 크거나 offset이 버퍼의 끝을 넘어가서 값들이 어떤 경고없이 버려질 수 있다는 것을 의미한다. 확실히 정확함을 유지할 수 없다면 사용하지 말아야 한다. 기본값은 false다.

예제:

var buf = new Buffer(4);
buf.writeUInt32BE(0xfeedface, 0);

console.log(buf);

buf.writeUInt32LE(0xfeedface, 0);

console.log(buf);

// <Buffer fe ed fa ce>
// <Buffer ce fa ed fe>

buf.writeInt8(value, offset, [noAssert])#

  • value 숫자
  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false

버퍼의 지정한 offset에 value를 작성한다. value는 반드시 유효하고 기호가 있는 8비트 정수여야 한다.

valueoffset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 지정한 함수에 value가 너무 크거나 offset이 버퍼의 끝을 넘어가서 값들이 어떤 경고없이 버려질 수 있다는 것을 의미한다. 확실히 정확함을 유지할 수 없다면 사용하지 말아야 한다. 기본값은 false다.

buffer.writeUInt8와 같이 동작하지만 buffer에 값을 2가지 완전한 기호가 있는 정수로 작성한다는 점이 다르다.

buf.writeInt16LE(value, offset, [noAssert])#

buf.writeInt16BE(value, offset, [noAssert])#

  • value 숫자
  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false

버퍼의 지정한 offset에 지정한 엔디언(endian) 형식으로 value를 작성한다. value는 반드시 유효하고 기호가 있는 16비트 정수여야 한다.

valueoffset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 지정한 함수에 value가 너무 크거나 offset이 버퍼의 끝을 넘어가서 값들이 어떤 경고없이 버려질 수 있다는 것을 의미한다. 확실히 정확함을 유지할 수 없다면 사용하지 말아야 한다. 기본값은 false다.

buffer.writeUInt16*와 같이 동작하지만 buffer에 값을 2가지 완전한 기호가 있는 정수로 작성한다는 점이 다르다.

buf.writeInt32LE(value, offset, [noAssert])#

buf.writeInt32BE(value, offset, [noAssert])#

  • value 숫자
  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false

버퍼의 지정한 offset에 지정한 엔디언(endian) 형식으로 value를 작성한다. value는 반드시 유효하고 기호가 있는 32비트 정수여야 한다.

valueoffset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 지정한 함수에 value가 너무 크거나 offset이 버퍼의 끝을 넘어가서 값들이 어떤 경고없이 버려질 수 있다는 것을 의미한다. 확실히 정확함을 유지할 수 없다면 사용하지 말아야 한다. 기본값은 false다.

buffer.writeUInt32*와 같이 동작하지만 buffer에 값을 2가지 완전한 기호가 있는 정수로 작성한다는 점이 다르다.

buf.writeFloatLE(value, offset, [noAssert])#

buf.writeFloatBE(value, offset, [noAssert])#

  • value 숫자
  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false

버퍼의 지정한 offset에 지정한 엔디언(endian) 형식으로 value를 작성한다. value는 반드시 유효한 32비트 실수여야 한다.

valueoffset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 지정한 함수에 value가 너무 크거나 offset이 버퍼의 끝을 넘어가서 값들이 어떤 경고없이 버려질 수 있다는 것을 의미한다. 확실히 정확함을 유지할 수 없다면 사용하지 말아야 한다. 기본값은 false다.

예제:

var buf = new Buffer(4);
buf.writeFloatBE(0xcafebabe, 0);

console.log(buf);

buf.writeFloatLE(0xcafebabe, 0);

console.log(buf);

// <Buffer 4f 4a fe bb>
// <Buffer bb fe 4a 4f>

buf.writeDoubleLE(value, offset, [noAssert])#

buf.writeDoubleBE(value, offset, [noAssert])#

  • value 숫자
  • offset 숫자
  • noAssert 불리언, 선택사항, 기본값: false

버퍼의 지정한 offset에 지정한 엔디언(endian) 형식으로 value를 작성한다. value는 반드시 유효한 64비트 더블이여야 한다.

valueoffset의 유효성 검사를 건너뛰려면 noAssert을 true로 설정한다. 이 말은 지정한 함수에 value가 너무 크거나 offset이 버퍼의 끝을 넘어가서 값들이 어떤 경고없이 버려질 수 있다는 것을 의미한다. 확실히 정확함을 유지할 수 없다면 사용하지 말아야 한다. 기본값은 false다.

예제:

var buf = new Buffer(8);
buf.writeDoubleBE(0xdeadbeefcafebabe, 0);

console.log(buf);

buf.writeDoubleLE(0xdeadbeefcafebabe, 0);

console.log(buf);

// <Buffer 43 eb d5 b7 dd f9 5f d7>
// <Buffer d7 5f f9 dd b7 d5 eb 43>

buf.fill(value, [offset], [end])#

  • value
  • offset 숫자, 선택사항
  • end 숫자, 선택사항

버퍼를 지정한 값으로 채운다. offset (기본값은 0)과 end (기본값은 buffer.length)를 전달하지 않으면 전체 버퍼를 채울 것이다.

var b = new Buffer(50);
b.fill("h");

buffer.INSPECT_MAX_BYTES#

  • 숫자, 기본값: 50

buffer.inspect()가 호출되었을 때 얼마나 많은 바이트가 반환될 것인가를 지정한다. 이 값은 사용자 모듈에서 오버라이드할 수 있다.

이 값은 Buffer 전역객체가 아니라 require('buffer')에서 반환되는 버퍼모듈이나 버퍼 인스턴스의 프로퍼티이다.

Class: SlowBuffer#

이 클래스는 주로 내부에서 사용한다. 자바스크립트 프로그램은 SlowBuffer 대신 Buffer를 사용해야 한다.

서버가 운영되는 동안 메모리의 작은 블럭에 많은 C++ Buffer 객체들을 할당하는 오버헤드를 피하려고 Node는 8Kb (8192 byte) 청크에 메모리를 할당한다. 버퍼가 이 크기보다 작으면 부모 SlowBuffer 객체에가 보완할 것이다. 버퍼가 이 크기보다 크면 Node는 직접적으로 버퍼에 SlowBuffer slab을 할당할 것이다.

Stream#

Stability: 2 - Unstable

스프림은 Node에서 여러 가지 객체로 구현되는 추상 인터페이스다. 예를 들어 HTTP 서버에 대한 요청은 stout과 같은 스트림이다. 스트림은 읽을수 있거나 쓸 수 있고 때로는 둘 다 가능하다. 모든 스트림은 EventEmitter의 인스턴스다.

require('stream')을 사용해서 기반 Stream 클래스를 로드할 수 있다.

Readable Stream#

Readable Stream에는 다음과 같은 메서드, 멤버, 이벤트가 있다.

Event: 'data'#

function (data) { }

'data' 이벤트는 Buffer(기본값)를 발생시키거나 setEncoding()가 사용된 경우 문자열을 발생시킨다.

Readable Stream'data' 이벤트를 발생시켰을 때 리스너가 없다면 데이터를 잃어버릴 것이다.

Event: 'end'#

function () { }

스트림이 EOF(TCP 용어로 FIN)를 받았을 때 발생한다. 더이상 'data' 이벤트가 발생하지 않는다는 것을 나타낸다. 스트림이 쓰기도 가능하다면 쓰기는 계속해서 가능할 것이다.

Event: 'error'#

function (exception) { }

데이터를 받는데 오류가 있으면 발생한다.

Event: 'close'#

function () { }

의존하는 리소스(예를 들면 파일 디스크립터)가 닫혔을 때 발생한다. 모든 스트림이 이 이벤트를 발생키시는 것은 아니다.

stream.readable#

true가 기본값인 불리언이지만 'error'가 발생하거나 스트림이 'end'가 되거나 destroy()이 호출된 뒤에 false로 바뀐다.

stream.setEncoding([encoding])#

'data' 이벤트가 Buffer 대신 문자열을 발생시키도록 한다. encoding'utf8', 'utf16le' ('ucs2'), 'ascii', 'hex'가 될 수 있다. 기본값은 'utf8'이다.

stream.pause()#

의존하는 통신계층에 resume()을 호출할 때까지 더이상 데이터를 보내지 않도록 요청하는 신호를 발생시킨다.

이런한 특성 때문에 특정 스크림은 즉시 멈추지 않고 pause()를 호출한 후에도 불확실한 기간동안 'data' 이벤트는 발생할 것이다.

stream.resume()#

pause()후에 들어오는 'data' 이벤트를 다시 시작한다.

stream.destroy()#

기반이 되는 파일 디스크립터를 닫는다. 스트림은 더 이상 writable도 아니고 readable도 아니다. 스트림은 더는 'data'나 'end' 이벤트를 발생시키지 않는다. 큐에 있는 어떤 작성데이터도 보내지 않을 것이다. 스트림은 관련된 리소스를 처리하는 'close' 이벤트를 실행해야 한다.

stream.pipe(destination, [options])#

이 메서드는 모든 Stream에서 사용할 수 있는 Stream.prototype 메서드이다.

이 읽는 스트림을 destination 작성할 스트림에 연결한다. 이 스트림에 들어오는 데이터는 destination에 쓰여진다. 목적지 스트림과 출처 스트림은 필요에 따라 멈추거나 다시 시작함으로써 동기화가 유지된다.

이 함수는 destination 스트림을 반환한다.

Unix의 cat 명령어를 에뮬레이팅한다.:

process.stdin.resume(); process.stdin.pipe(process.stdout);

출처 스트림이 end를 발생하면 기본적으로 목적지 스트림도 end()를 호출하므로 destination는 더이상 쓸 수 없다. 목적지 스트림을 열려진 상태로 놔두려면 options으로 { end: false }를 전달한다.

이는 마지막에 "Goodbye"를 작성할 수 있도록 process.stdout를 열어둔 채로 놔둔다.

process.stdin.resume();

process.stdin.pipe(process.stdout, { end: false });

process.stdin.on("end", function() {
process.stdout.write("Goodbye\n"); });

Writable Stream#

Writable Stream에는 다음의 메서드, 멤버, 이벤트가 있다.

Event: 'drain'#

function () { }

스트림의 작성 큐가 비어있고 다시 버퍼링하지 않고 안전하게 쓸 수 있을 때 발생한다. stream.write()false를 반환할 때를 리스닝한다.

stream.write()가 이전에 false를 반환했는 지 여부와 관계없이 'drain' 이벤트는 언제든지 발생할 수 있다. 원치않는 'drain' 이벤트를 받지 않으려면 stream.once()를 사용해라.

Event: 'error'#

function (exception) { }

오류가 있을 때 예외 exception과 함께 발생시킨다.

Event: 'close'#

function () { }

기반이 되는 파일 디스크립터가 닫혔을 때 발생한다.

Event: 'pipe'#

function (src) { }

스트림이 읽을 수 있는 스트림의 pipe 메서드에 전달되었을 때 발생한다.

stream.writable#

기본값이 true인 불리언이지만 'error'가 발생하거나 end() / destroy()가 호출된 후에 false로 바뀐다.

stream.write(string, [encoding])#

주어진 encoding으로 스트림에 string를 작성한다. 문자열이 커널 버퍼에 플러시되면 true를 반환한다. 커널 버퍼가 꽉차면 데이터가 차후에 보내진다는 것을 알리기 위해 false를 반환한다. 커널버퍼가 다시 비워지면 'drain' 이벤트로 알려줄 것이다. encoding의 기본값은 'utf8'이다.

stream.write(buffer)#

raw buffer를 사용한다는 점만 빼고는 위와 동일하다.

stream.end()#

EOF나 FIN로 스트림을 종료시킨다. 이 함수를 호출하면 스트림을 닫기 전에 큐에 있는 작성 데이터를 보낼 것이다.

stream.end(string, encoding)#

주어진 encoding으로 string을 보내고 EOF나 FIN로 스트림을 종료시킨다. 이는 보내는 패킷의 수를 줄이는 데 유용하다.

stream.end(buffer)#

위와 동일하지만 buffer를 사용한다.

stream.destroy()#

기반이 되는 파일 디스크립터를 닫는다. 스트림은 더이상 writable도 아니고 readable도 아니다. 스트림은 더는 'data'나 'end' 이벤트를 발생시키지 않는다. 큐에 있는 어떤 작성데이터도 보내지 않을 것이다. 스트림은 관련된 리소스를 처리하는 'close' 이벤트를 실행해야 한다.

stream.destroySoon()#

작성 큐를 소모한 후에 파일 디스크립터를 닫는다. 작성 큐에 남아있는 데이터가 없다면 destroySoon()는 즉시 파일 디스크립터를 닫을 수 있다.

Crypto#

Stability: 2 - Unstable; 차기 버전에서 API 변경을 논의 중이다.
호환성을 깨뜨리는 정도의 변경은 최소화할 것이다. 하단을 참고해라.

이 모듈에 접근하려면 require('crypto')를 사용해라.

crypto 모듈은 의존 플랫폼에서 이용할 수 있는 OpenSSL을 필요로 한다. 안정한 HTTPS net이나 http 연결에서 사용되는 안정한 인증서를 캡슐화하는 방법을 제공한다.

OpenSSL의 hash, hmac, cipher, decipher, sign, verify 메서드의 랩퍼(wrapper)도 제공한다.

crypto.createCredentials(details)#

인증서 객체를 생성한다. 선택사항인 details는 키를 가진 딕셔너리가 된다.

  • pfx : PFX나 PKCS12로 암호화된 개인키, 인증서, CA 증명서를 담고 있는 문자열이나 버퍼
  • key : PEM으로 암호화된 개인키를 담고 있는 문자열
  • passphrase : 개인키나 pfx에 대한 암호문(passphrase) 문자열
  • cert : PEM으로 암호화된 증명서를 담고 있는 문자열
  • ca : PEM으로 암호화된 믿을 수 있는 CA 증명서의 문자열이나 리스트
  • crl : PEM으로 암호화된 CRL(Certificate Revocation List)의 문자열 혹은 문자열의 리스트
  • ciphers: 사용하거나 제외할 암호(cipher)를 설명하는 문자열. 자세한 형식은 http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT를 참조해라.

'ca'에 대한 세부내용을 전달하지 않는다면 node.js는 공개적으로 신뢰할 수 있는 CA의 리스트를 기본으로

http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt에서 받아서 사용할 것이다.

crypto.createHash(algorithm)#

해시 객체를 생성하고 반환한다. 전달한 algorithm의 암호화 해시는 해시 다이제스트를 생성하는 데 사용할 수 있다.

algorithm은 플랫폼상의 OpenSSL 버전에서 사용할 수 있는 알고리즘에 의존한다. 'sha1', 'md5', 'sha256', 'sha512'등이 있다. 최근 릴리즈에서는 openssl list-message-digest-algorithms로 사용할 수 있는 다이제스트 알로리즘을 볼 수 있다.

예제: 이 프로그램은 파일의 sha1 합을 생성한다.

var filename = process.argv[2];
var crypto = require('crypto');
var fs = require('fs');

var shasum = crypto.createHash('sha1');

var s = fs.ReadStream(filename);
s.on('data', function(d) {
  shasum.update(d);
});

s.on('end', function() {
  var d = shasum.digest('hex');
  console.log(d + '  ' + filename);
});

Class: Hash#

데이터의 해시 다이제스트를 생성하는 클래스다.

crypto.createHash가 반환하는 클래스다.

hash.update(data, [input_encoding])#

전달한 input_encodingdata로 해시 내용을 갱신한다. 전달한 input_encoding 인코딩은 'utf8', 'ascii', 'binary'가 될 수 있다. 기본값은 'binary'이다. 이 함수는 스트림처럼 새로운 데이터가 올 때마다 여러 번 호출될 수 있다.

hash.digest([encoding])#

해시되어야 하는 전달한 데이터의 모든 다이제스트를 계산한다. encoding'hex', 'binary', 'base64'가 될 수 있다. 기본값은 'binary'이다.

Note: hash 객체는 digest() 메서드가 호출한 후에는 사용할 수 없다.

crypto.createHmac(algorithm, key)#

hmac 객체를 생성하고 반환한다. 전달한 algorithm과 key의 암호화 해시이다.

algorithm는 OpenSSL에서 사용할 수 있는 알고리즘에 의존한다. -- 위의 createHash를 봐라. key는 사용할 hmac 키이다.

Class: Hmac#

암호화 hmac 내용을 생성하는 클래스다.

crypto.createHmac가 리턴하는 클래스다.

hmac.update(data)#

전달한 data로 hmac 내용을 갱신한다. 이 함수는 스트림처럼 새로운 데이터가 올 때마다 여러번 호출될 수 있다.

hmac.digest([encoding])#

hmac이 되어야 하는 전달한 데이터의 모든 다이제스트를 계산한다. encoding'hex', 'binary', 'base64'가 될 수 있다. 기본값은 'binary'이다.

Note: hmac 객체는 digest() 메서드가 호출한 후에는 사용할 수 없다.

crypto.createCipher(algorithm, password)#

전달한 algorithm과 password로 암호화한 암호화 객체를 생성하고 반환한다.

algorithm는 OpenSSL에 의존적이다. 'aes192' 등이 있다. OpenSSL 최근 릴리즈에서는 openssl list-cipher-algorithms로 사용할 수 있는 암호화 알고리즘을 볼 수 있다. password는 key와 IV를 얻는데 사용하고 반드시 'binary'로 인코딩된 문자열이나 buffer이어야 한다.

crypto.createCipheriv(algorithm, key, iv)#

전달한 algorithm, key, iv로 암호화된 암호화 객체를 생성하고 반환한다.

algorithm은 createCipher()algorithm와 같다. key는 algorithm에서 사용하는 로우 키(raw key) 이다. iv`는 초기화 벡터(Initialization vector)이다.

keyiv는 반드시 'binary'로 인코딩된 문자열이나 buffers여야 한다.

Class: Cipher#

데이터를 암호화하는 클래스이다.

crypto.createCiphercrypto.createCipheriv가 반환하는 객체다.

cipher.update(data, [input_encoding], [output_encoding])#

전달한 input_encoding의 인코딩의 data로 cipher를 갱신한다. input_encoding'utf8', 'ascii', 'binary'가 될 수 있다. 기본값은 'binary'이다.

output_encoding는 암호화된 데이터의 출력형식을 지정하고 'binary', 'base64', 'hex'가 될 수 있다. 기본값은 'binary'이다.

암호화된 내용을 반환하고 스트림처럼 새로운 데이터로 여러번 호출할 수 있다.

cipher.final([output_encoding])#

'binary', 'base64', 'hex'중의 하나인 output_encoding로 남아있는 모든 암호화된 내용을 반환한다. 기본값은 'binary'이다.

Note: cipher 객체는 final() 메서드를 호출한 후에는 사용할 수 없다.

cipher.setAutoPadding(auto_padding=true)#

입력데이터의 자동 패딩을 사용하지 않고 블럭 크기를 사용한다. auto_padding가 false이면 전체 입력데이터의 길이는 cipher의 블럭 크기의 배수가 되어야 하고 그렇지 않으면 final는 실패할 것이다. 표준이 아닌 패딩은 유용한데 예를 들어 PKCS 패딩 대신 0x0를 사용할 수 있다. 이 함수는 반드시 cipher.final 이전에 호출해야 한다.

crypto.createDecipher(algorithm, password)#

전달한 algorithm와 key로 decipher 객체를 생성하고 반환한다. 이 함수는 위의 createCipher()의 반영이다.

crypto.createDecipheriv(algorithm, key, iv)#

전달한 algorithm, key, iv로 decipher 객체를 생성하고 반환한다. 이 함수는 위의 createCipheriv()의 반영이다.

Class: Decipher#

데이터를 복호화하는 클래스다.

crypto.createDeciphercrypto.createDecipheriv가 반환하는 클래스다.

decipher.update(data, [input_encoding], [output_encoding])#

'binary', 'base64', 'hex'로 인코딩된 data로 decipher를 갱신한다. 기본값은 'binary'`이다.

output_decoding는 반환할 복호화된 평문의 형식을 지정한다. 'binary', 'ascii', 'utf8'가 될 수 있고 기본값은 'binary'이다.

decipher.final([output_encoding])#

'binary', 'ascii', 'utf8'중에 하나가 될 수 있는 output_encoding로 남아있는 모든 복호화된 평문을 반환한다. 기본값은 'binary'이다.

Note: decipher 객체는 final() 메서드가 호출된 후에는 사용할 수 없다.

decipher.setAutoPadding(auto_padding=true)#

표준 블럭 패팅없이 암호화된 데이터를 decipher.final가 확인하고 제거하지 않도록 자동 패딩을 사용하지 않을 수 있다. 입력데이터의 길이가 cipher 블락 크기의 배수일 때만 동작한다. 이 함수는 반드시 데이터를 decipher.update로 스트리밍하기 전에 호출해야 한다.

crypto.createSign(algorithm)#

전달한 algorithm으로 서명된 객체를 생성하고 반환한다. 최근 OpenSSL 릴리즈에서 openssl list-public-key-algorithms로 사용할 수 있는 서명된 알고리즘을 볼 수 있다. 예를 들면 'RSA-SHA256'가 있다.

Class: Signer#

서명을 생성하는 클래스다.

crypto.createSign가 반환하는 클래스이다.

signer.update(data)#

data로 signer 객체를 갱신한다. 이 함수는 스트림처럼 새로운 데이터가 올 때마다 여러 번 호출할 수 있다.

signer.sign(private_key, [output_format])#

전달한 갱신 데이터 모두를 signer를 통해서 서명을 계산한다. private_key는 서명에 사용할 PEM 인코딩된 개인키를 담고 있는 문자열이다.

'binary', 'hex', 'base64'가 될 수 있는 output_format의 서명을 반환한다. 기본값은 'binary'이다.

Note: signer 객체는 sign() 메서드를 호출한 후에는 사용할 수 없다.

crypto.createVerify(algorithm)#

전달한 algorithm으로 검증 객체를 생성하고 반환한다. 이 함수는 위의 서명객체의 반영이다.

Class: Verify#

서명을 검증하는 클래스다.

crypto.createVerify가 반환하는 클래스이다.

verifier.update(data)#

data로 verifier 객체를 갱신한다. 이 함수는 스트림처럼 새로운 데이터가 올 때마다 여러 번 호출할 수 있다.

verifier.verify(object, signature, [signature_format])#

objectsignature를 사용해서 서명된 데이터를 검증한다. object는 RSA 공개키, DSA 공개키, X.509 인증서 중 하나가 될 수 있는 PEM으로 인코딩된 객체를 담고 있는 문자열이다. signature'binary', 'hex', 'base64'가 될 수 있는 signature_format의 데이터에 대해 이전에 계산한 서명이다. 기본값은 'binary'이다.

데이터와 공개키에 대한 서명의 유효성에 따라 true나 false를 반환한다.

Note: verifier 객체는 verify() 메서드를 호출한 뒤에는 사용할 수 없다.

crypto.createDiffieHellman(prime_length)#

Diffie-Hellman 키 교환 객체를 생성하고 전달한 비트 길이의 소수를 생성한다. 사용된 제너레이터는 2이다.

crypto.createDiffieHellman(prime, [encoding])#

제공된 소수를 사용해서 Diffie-Hellman 키 교환 객체를 생성한다. 사용된 제너레이터는 2이다. 인코딩은 'binary', 'hex', 'base64'가 될 수 있다. 기본값은 'binary'이다.

Class: DiffieHellman#

Diffie-Hellman 키 교환을 생성하는 클래스이다.

crypto.createDiffieHellman가 반환하는 클래스이다.

diffieHellman.generateKeys([encoding])#

개인 Diffie-Hellman 키값과 공개 Diffie-Hellman 키값을 생성하고 지정한 인코딩으로 공개키를 반환한다. 이 키는 다른 관련자에게 이동할 수 있다. 인코딩은 'binary', 'hex', 'base64'가 될 수 있다. 기본값은 'binary'이다.

diffieHellman.computeSecret(other_public_key, [input_encoding], [output_encoding])#

다른 관련자의 공개키로 other_public_key를 사용하는 공유 시크릿을 계산하고 계산된 공유 시크릿을 반환한다. 제공된 키는 지정한 input_encoding를 사용해서 해석된다. 시크릿은 지정한 output_encoding을 사용하서 인코딩된다. 인코딩은 'binary', 'hex', 'base64'가 될 수 있고 기본 입력인코딩은 'binary'이다. 출력인코딩을 지정하지 않으면 입력인코딩을 출력인코딩으로 사용한다.

diffieHellman.getPrime([encoding])#

'binary', 'hex', 'base64'가 될 수 있는 지정한 인코딩의 Diffie-Hellman 소수를 반환한다. 기본값은 'binary'이다.

diffieHellman.getGenerator([encoding])#

'binary', 'hex', 'base64'가 될 수 있는 지정한 인코딩의 Diffie-Hellman 제너레이터를 반환한다. 기본값은 'binary'이다.

diffieHellman.getPublicKey([encoding])#

'binary', 'hex', 'base64'가 될 수 있는 지정한 인코딩의 Diffie-Hellman 공개키를 반환한다. 기본값은 'binary'이다.

diffieHellman.getPrivateKey([encoding])#

'binary', 'hex', 'base64'가 될 수 있는 지정한 인코딩의 Diffie-Hellman 개인키를 반환한다. 기본값은 'binary'이다.

diffieHellman.setPublicKey(public_key, [encoding])#

Diffie-Hellman 공개키를 설정한다. 키 인코딩은 'binary', 'hex', 'base64'가 될 수 있다. 기본값은 'binary'이다.

diffieHellman.setPrivateKey(public_key, [encoding])#

Diffie-Hellman 개인키를 설정한다. 키 인코딩은 'binary', 'hex', 'base64'가 될 수 있다. 기본값은 'binary'이다.

crypto.getDiffieHellman(group_name)#

미리 정의된 Diffie-Hellman 키 교환 객체를 생성한다. 지원하는 그룹은 'modp1', 'modp2', 'modp5' (RFC 2412에 정의되어 있다.) 와 'modp14', 'modp15', 'modp16', 'modp17', 'modp18' (RFC 3526에 정의되어 있다.)이다. 반환된 객체는 위의 crypto.createDiffieHellman()가 생성한 객체의 인터페이스와 비슷하지만 키를 변경할 수 없다.(예를 들면 diffieHellman.setPublicKey()를 사용해서) 이 루틴을 사용했을 때의 이점은 관련자들이 미리 그룹 규칙을 생성하지 않고 교환하지도 않아서 프로세서와 통신 시간을 모두 아낄 수 있다.

공유된 비밀키를 얻는 예제:

var crypto = require('crypto');
var alice = crypto.getDiffieHellman('modp5');
var bob = crypto.getDiffieHellman('modp5');

alice.generateKeys();
bob.generateKeys();

var alice_secret = alice.computeSecret(bob.getPublicKey(), 'binary', 'hex');
var bob_secret = bob.computeSecret(alice.getPublicKey(), 'binary', 'hex');

/* alice_secret와 bob_secret는 같아야 한다. */
console.log(alice_secret == bob_secret);

crypto.pbkdf2(password, salt, iterations, keylen, callback)#

비동기적인 PBKDF2가 전달한 password, salt, iterations에서 전달한 길이의 키를 얻기 위해 의사난수의(pseudorandom) 함수 HMAC-SHA1를 적용한다. callback은 2개의 아규먼트 (err, derivedKey)를 받는다.

crypto.randomBytes(size, [callback])#

강력한 암호의 의사난수(pseudo-random) 데이터를 생성한다. 사용법:

// async
crypto.randomBytes(256, function(ex, buf) {
  if (ex) throw ex;
  console.log('Have %d bytes of random data: %s', buf.length, buf);
});

// sync
try {
  var buf = crypto.randomBytes(256);
  console.log('Have %d bytes of random data: %s', buf.length, buf);
} catch (ex) {
  // handle error
}

Node의 차기버전에 제안된 API 변경사항#

Crypto 모듈은 통일된 Stream API의 개념과 바이너리 데이터를 다루는 Buffer 객체가 존재하기 전에 Node에 추가되었다.

그래서 다른 Node 클래스들에는 있는 일반적인 메서드가 스트리밍 클래스에는 없고 많은 메서드들이 Buffers 대신 기본적으로 바이너리로 인코딩된 문자열을 받아들이고 반환한다.

node의 차기 버전에서는 Buffer를 기본 데이터 타입으로 사용할 것이다. 이 변경사항은 전부는 아니지만 일부에서는 호환성을 깨뜨릴 것이다.

예를 들어 현재 Sign 클래스에 기본 인자를 사용하고 그 결과를 Verify 클래스에 전달하고 데이터를 검사하지 않는다면 이전과 마차가지로 계속 동작할 것이다. 바이너리 문자열을 얻고 Verify 객체에 바이너리 문자열을 제공하는 곳에서 Buffer를 얻을 것이고 Verify 객체에 Buffer를 제공할 것이다.

하지만 Buffers에서 제대로 동작하지 않을 문자열 데이터로 어떤 작업을 한거나(문자열을 이어붙힌다거나 데이터베이스에 저장하는 등) 인코딩 인자없이 암호화함수에 바이너리 문자열을 전달한다면 사용하고자 하는 인코딩을 지정하는 인코딩 인자를 제공해야 할 것이다.

또한 스트리밍 API는 제공될 것이지만 레거시 API를 유지하는 방법으로 진행될 것이다.

TLS (SSL)#

Stability: 3 - Stable

이 모듈에 접근하려면 require('tls')를 사용해라.

tls 모듈은 TLS(Transport Layer Security)나 SSL(Secure Socket Layer)을 제공하는데 OpenSSL을 사용한다: 암호화된 스트림 통신

TLS/SSL는 공개키/개인키 기반이다. 각 클라이언트와 서버는 개인키를 반드시 가지고 있어야 한다. 개인키는 다음과 같이 생성한다.

openssl genrsa -out ryans-key.pem 1024

모든 서버와 몇몇 클라이언트는 인증서를 가질 필요가 있다. 인증서는 인증기관(Certificate Authority)이나 자체서명으로 서명된 공개키이다. "인증서 서명 요청(Certificate Signing Request)" (CSR) 파일을 생성하는 것이 인증서를 얻는 첫 단계이다. 다음과 같이 실행한다.

openssl req -new -key ryans-key.pem -out ryans-csr.pem

CSR로 자체서명 인증서를 생성하려면 다음과 같이 한다.

openssl x509 -req -in ryans-csr.pem -signkey ryans-key.pem -out ryans-cert.pem

아니면 서명을 위해서 인증기관(Certificate Authority)에 CSR을 보낼 수 있다.

(TODO: CA를 생성하는 문서에 관심이 있느 사용자들은 Node 소스크도의 test/fixtures/keys/Makefile를 봐야한다.)

다음과 같이 실행해서 .pfx나 .p12를 생성한다.

openssl pkcs12 -export -in agent5-cert.pem -inkey agent5-key.pem \
    -certfile ca-cert.pem -out agent5.pfx
  • in: 인증서
  • inkey: 개인키
  • certfile: cat ca1-cert.pem ca2-cert.pem > ca-cert.pem와 같이 모든 CA 인증서를 하나의 파일로 연결한다.

Client-initiated renegotiation attack mitigation#

TLS 프로토콜는 클라이언트가 TLS 세션의 어떤 관점을 재협상하게 한다. 불행히도 세션 재협상은 DoS(denial-of-service) 공격의 잠재적인 요소인 서버측 리소스의 양의 불균형을 야기시킨다.

이를 완화시키려면 재현상을 10분내에 3번으로 제한해야 한다. 이 한계를 넘어서면 CleartextStream 인스턴스에서 오류가 발생한다. 이 제한은 설정할 수 있다.

  • tls.CLIENT_RENEG_LIMIT: 재협상 제한, 기본값은 3이다.

  • tls.CLIENT_RENEG_WINDOW: 초단위의 재협상 시간, 기본값은

                           10 분이다.

무엇을 하는지 알지 못하면 기본값을 바꾸지 마라.

서버를 테스트하려면 openssl s_client -connect address:port로 서버에 연결한 뒤 R<CR> (R문자 뒤에 캐리지리턴)을 몇번 입력한다.

NPN and SNI#

NPN (Next Protocol Negotiation)와 SNI (Server Name Indication)는 TLS 핸드쉐이크 확장이다.

  • NPN - 다중 프로토콜(HTTP, SPDY)의 TLS 서버에 사용한다
  • SNI - 다른 종류의 SSL 인증서의 다중 호스트네임의 TLS 서버에 사용한다.

tls.createServer(options, [secureConnectionListener])#

새로운 tls.Server를 생성한다. connectionListener 아규먼트는 자동적으로 secureConnection 이벤트의 리스너로 설정된다. options 객체는 다음과 같은 선택사항이 있다.

  • pfx: PFX나 PKCS12 형식으로 개인키, 인증서, 서버의 CA 인증서를 담고 있는 문자열이나 Buffer다. (key, cert, ca 옵셩은 상호배타적이다.)

  • key: PEM 형식의 서버 개인키를 담고 있는 문자열이나 Buffer다. (필수사항)

  • passphrase: 개인키나 pfx에 대한 암호문의 문자열이다.

  • cert: PEM 형식의 서버 인증키를 담고 있는 문자열이나 Buffer다. (필수사항)

  • ca: 신뢰할 수 있는 인증서의 문자열이나 Buffer 배열이다. 이 옵션을 생락하면 각각 VeriSign처럼 잘 알려진 "루트" CA를 사용할 것이다. 연결에 권한을 부여하는 데 이것들을 사용한다.

  • crl : PEM으로 인코딩된 CRL(Certificate Revocation List)의 문자열이나 문자열의 리스트

  • ciphers: 사용하거나 제외할 암호(cipher)를 설명하는 문자열이다.

    BEAST attacks을 완화시키려면 CBC가 아닌 암호문을 우선시하도록 아래에서 설명할 honorCipherOrder을 이 옵션과 함께 사용하기를 권장한다.

    기본값은 ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH이다. 형식에 대해 자세히 알고 싶으면 OpenSSL cipher list format documentation를 참고해라.

    node.js가 OpenSSL 1.0.1이나 그 상위 버전과 연결되었을 때 ECDHE-RSA-AES128-SHA256AES128-GCM-SHA256를 사용하고 클라이언트는 TLS 1.2, RC4을 안전한 폴백(fallback)으로 사용한다.

    NOTE: 이 섹션의 이전 버전에서는 AES256-SHA를 괜찮은 암호문으로 제안했었다. 하지만 AES256-SHA는 CBC 암호문이므로 BEAST attacks을 받기 쉽다. AES256-SHA를 사용하지 말아라.

  • honorCipherOrder : 암호문을 선택할 때 클라이언트의 설정대신에 서버의 설정을 사용해라.

    SSLv2을 사용한다면 서버는 클라이언트로 설정리스트를 보낼 것이고 클라이언트가 암호문을 선택한다.

    이 옵션은 기본적으로는 사용안함으로 되어 있지만 BEAST attacks을 완화하려면 ciphers 옵션과 함께 이 옵션을 사용하기를 권장한다.

  • requestCert: true로 지정하면 연결할 때 서버가 클라이언트한테 인증서를 요청하고 인증서를 검증하는 데 사용할 것이다. 기본값: false.

  • rejectUnauthorized: true로 지정하면 제공된 CA 목록에서 인가되지 않은 모든 연결을 서버가 거절할 것이다. 이 옵션은 requestCerttrue일때만 유효하다. 기본값: false.

  • NPNProtocols: 사용가능한 NPN 프로토콜의 배열이나 Buffer다. (프로토콜은 우선순위에 따라 정렬되어야 한다.)

  • SNICallback: 클라이언트가 SNI TLS 확장을 지원하는 경우 호출될 함수다. 이 함수는 단 하나의 아규먼트인 servername만 받는다. 그리고 SNICallback는 SecureContext 인스턴스를 반환해야 한다. (적합한 SecureContext를 얻으려면 crypto.createCredentials(...).context를 사용할 수 있다.) SNICallback를 제공하지 않으면 고수준 API와 함께 기본 콜백을 사용할 것이다.(아래를 참고해라.)

  • sessionIdContext: 세션 회수를 위한 불투명한 식별자를 담고 있는 문자열이다. requestCerttrue이면 기본값은 커맨드라인에서 생성한 MD5 해시값이다. requestCerttrue가 아니면기반값은 제공하지 않는다.

간단한 에코(echo) 서버의 예제다.

var tls = require('tls');
var fs = require('fs');

var options = {
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem'),

  // This is necessary only if using the client certificate authentication.
  requestCert: true,

  // This is necessary only if the client uses the self-signed certificate.
  ca: [ fs.readFileSync('client-cert.pem') ]
};

var server = tls.createServer(options, function(cleartextStream) {
  console.log('server connected',
              cleartextStream.authorized ? 'authorized' : 'unauthorized');
  cleartextStream.write("welcome!\n");
  cleartextStream.setEncoding('utf8');
  cleartextStream.pipe(cleartextStream);
});
server.listen(8000, function() {
  console.log('server bound');
});

또는

var tls = require('tls');
var fs = require('fs');

var options = {
  pfx: fs.readFileSync('server.pfx'),

  // This is necessary only if using the client certificate authentication.
  requestCert: true,

};

var server = tls.createServer(options, function(cleartextStream) {
  console.log('server connected',
              cleartextStream.authorized ? 'authorized' : 'unauthorized');
  cleartextStream.write("welcome!\n");
  cleartextStream.setEncoding('utf8');
  cleartextStream.pipe(cleartextStream);
});
server.listen(8000, function() {
  console.log('server bound');
});

openssl s_client로 서버에 접속해서 테스트할 수 있다.

openssl s_client -connect 127.0.0.1:8000

tls.connect(options, [callback])#

tls.connect(port, [host], [options], [callback])#

전달한 porthost로(과거 API) 혹은 options.portoptions.host로 새로운 클라이언트 연결을 생성한다. (host를 지정하지 않으면 기본값은 localhost이다.) options는 지정된 객체여야 한다.

  • host: 클라이언트가 접속할 호스트

  • port: 클라이언트가 접속할 포트

  • socket: 새로운 소켓을 생성하는 대신 전달한 소켓으로 안전한 연결을 만든다. 이 옵션을 지정하면 hostport은 무시한다.

  • pfx: PFX나 PKCS12 형식으로 개인키, 인증서 서버의 CA 인증서를 담고 있는 문자열이나 Buffer다.

  • key: PEM 형식으로 클라이언트의 개인키를 담고 있는 문자열이나 Buffer다.

  • passphrase: 개인키나 pfx에 대한 암호문 문자열이다.

  • cert: PEM 형식으로 클라이언트의 인증서 키를 담고 있는 문자열이나 Buffer다.

  • ca: 신뢰할 수 있는 인증서의 문자열이나 Buffer 배열이다. 이 옵션을 생락하면 각각 VeriSign처럼 잘 알려진 "루트" CA를 사용할 것이다. 연결에 권한을 부여하는 데 이것들을 사용한다.

  • rejectUnauthorized: 이 값이 true이면 서버 인증서를 제공한 CA 리스트로 검증한다. 검증에 실패하면 'error' 이벤트가 발생한다. 기본값: false.

  • NPNProtocols: 지원하는 NPN 프로토콜의 배열이나 Buffer다. Buffer는 다음의 형식이어야 한다. 0x05hello0x05world, 첫 바이트는 next protocol name의 길이이다.(배열을 전달하는 것이 보통 훨씬 간단할 것이다: ['hello', 'world'])

  • servername: SNI (Server Name Indication) TLS 확장에 대한 Servername이다.

callback 파라미터는 'secureConnect' 이벤트의 리스너로 추가할 것이다.

tls.connect()CleartextStream 객체를 반환한다.

앞에서 설명한 에코 서버의 클라이언트 예제다.

var tls = require('tls');
var fs = require('fs');

var options = {
  // These are necessary only if using the client certificate authentication
  key: fs.readFileSync('client-key.pem'),
  cert: fs.readFileSync('client-cert.pem'),

  // This is necessary only if the server uses the self-signed certificate
  ca: [ fs.readFileSync('server-cert.pem') ]
};

var cleartextStream = tls.connect(8000, options, function() {
  console.log('client connected',
              cleartextStream.authorized ? 'authorized' : 'unauthorized');
  process.stdin.pipe(cleartextStream);
  process.stdin.resume();
});
cleartextStream.setEncoding('utf8');
cleartextStream.on('data', function(data) {
  console.log(data);
});
cleartextStream.on('end', function() {
  server.close();
});

또는

var tls = require('tls');
var fs = require('fs');

var options = {
  pfx: fs.readFileSync('client.pfx')
};

var cleartextStream = tls.connect(8000, options, function() {
  console.log('client connected',
              cleartextStream.authorized ? 'authorized' : 'unauthorized');
  process.stdin.pipe(cleartextStream);
  process.stdin.resume();
});
cleartextStream.setEncoding('utf8');
cleartextStream.on('data', function(data) {
  console.log(data);
});
cleartextStream.on('end', function() {
  server.close();
});

tls.createSecurePair([credentials], [isServer], [requestCert], [rejectUnauthorized])#

두 스트림으로 새 안전한 쌍(pair) 객체를 생성한다. 두 스트림 중 하나는 암호화된 데이터를 읽고 쓰고 다른 하나는 평문 데이터를 읽고 쓴다. 보통 암호화된 쪽은 들어오는 암호화된 데이터 스트림에 파이프로 연결되고 평문 쪽은 초기 암호화 스트림에 대한 대체용으로 사용된다.

  • credentials: crypto.createCredentials( ... )의 증명서 객체

  • isServer: 이 tls 연결이 서버나 클라이언트로써 열려야 하는지를 나타내는 불리언값이다.

  • requestCert: 서버가 연결하는 클라이언트에서 인증서를 요구해야 하는지 나타내는 불리언 값이다. 서버 연결에만 적용된다.

  • rejectUnauthorized: 서버가 유효하지 않은 인증서를 가진 클라이언트를 자동으로 거절할 것인지 나타내는 불리언 값이다. requestCert를 사용하는 서버에만 적용된다.

tls.createSecurePair()는 [cleartext][]와 encrypted 스트림 프로퍼티로 SecurePair 객체를 반환한다.

Class: SecurePair#

tls.createSecurePair가 반환하는 클래스다.

Event: 'secure'#

쌍이 성공적으로 안전한 연결을 수립했을 때 SecurePair가 발생시키는 이벤트이다.

서버의 'secureConnection' 이벤트를 확인하는 것과 비슷하게 pair.cleartext.authorized는 사용된 인증서가 제대로 권한을 부여받았는지 승인하기 위해 확인되어야 한다.

Class: tls.Server#

net.Server의 하위클래스이고 net.Server와 같은 메서드를 가진다. 그냥 로우(raw) TCP 연결을 받아들이는 대신 TLS나 SSL을 사용해서 암호화된 연결을 받아들인다.

Event: 'secureConnection'#

function (cleartextStream) {}

새로운 연결이 성공적으로 핸드쉐이크를 했을 때 발생하는 이벤트이다. 아규먼트는 CleartextStream 인스턴스다. 이 인스턴스는 공통 스트림 메서드와 이벤트를 모두 갖고 있다.

cleartextStream.authorized는 서버에 대해 제공된 인증서 권한 중 하나로 클라이언트가 검증되었는지 나타내는 불리언 값이다. cleartextStream.authorized가 false이면 cleartextStream.authorizationError는 어떻게 권한부여가 실패했는 지를 알려주기 위해 설정된다. TLS 서버의 설정에 따라 권한이 부여되지 않은 연결을 받아들일 수 있다는 점을 알아두어야 한다. cleartextStream.npnProtocol는 선택된 NPN 프로토콜을 담고 있는 문자열이다. cleartextStream.servername은 SNI로 요청된 servername을 담고 있는 문자열이다.

Event: 'clientError'#

function (exception) { }

안전한 연결이 이뤄지기 전에 클라이언트 연결이 'error' 이벤트를 발생시켰을 때 발생하는 이벤트다.

server.listen(port, [host], [callback])#

지정한 porthost로 연결을 받아들이기 시작한다. host를 생략하면 서버는 모든 IPv4 주소(INADDR_ANY)에서 직접 연결을 받아들일 것이다.

이 함수는 비동기함수이다. 마지막 파라미터인 callback는 서버의 바인딩이 완료되었을 때 호출될 것이다.

더 자세한 내용은 net.Server를 봐라.

server.close()#

서버가 새로운 연결을 받아들이는 것을 멈춤다. 이 함수는 비동기 함수이고 서버가 'close' 이벤트를 발생시켰을 때 결국 닫힌다.

server.address()#

운영체제에서 보고된 서버가 바인딩된 주소와 주소 패밀리이름과 포트를 반환한다. 더 자세한 내용은 net.Server.address()를 봐라.

server.addContext(hostname, credentials)#

클라이언트 요청의 SNI 호스트이름이 전달한 hostname(와일드카드도 사용할 수 있다.)와 일치하는 경우 사용할 안전한 컨텍스트를 추가한다. credentialskey, cert, ca를 포함할 수 있다.

server.maxConnections#

서버의 연결수가 많아졌을 때 연결을 거절하려면 이 프로퍼티를 설정해라.

server.connections#

서버의 현재 연결 수

Class: tls.CleartextStream#

평문 데이터와 같이 암호화된 데이터도 읽고 쓸 수 있도록 암호화된 스트림에 기반을 둔 스트림이다.

이 인스턴스는 이중 Stream 인터페이스를 구현한다. 이 인스턴스는 공통 스트림 메서드와 이벤트를 모두 가지고 있다.

ClearTextStream는 SecurePair 객체의 clear 멤버이다.

Event: 'secureConnect'#

새로운 연결이 성공적으로 핸드쉐이크를 했을 때 발생하는 이벤트이다. 리스너는 서버의 인증서가 권한 부여를 받았는 지에 상관없이 호출될 것이다. 이는 서버 인증서가 지정한 CA중의 하나로 서명되었는지를 보려고 cleartextStream.authorized를 검사하는 사용자에게 달려있다. cleartextStream.authorized === false인 경우 오류는 cleartextStream.authorizationError에 있을 것이다. 또한 NPN이 사용되었다면 협상 프로토콜을 위해 cleartextStream.npnProtocol를 확인할 수 있다.

cleartextStream.authorized#

피어(peer) 인증서가 지정한 CA중에 하나로 서명되었으면 true 그렇지 않으면 false인 불리언이다.

cleartextStream.authorizationError#

왜 피어(peer)의 인증서가 검증되지 못했는지에 대한 내용이다. 이 프로퍼티는 cleartextStream.authorized === false인 경우에만 사용할 수 있다.

cleartextStream.getPeerCertificate()#

피어(peer)의 인증서를 나타내는 객체를 반환한다. 반환된 객체는 인증서의 필드와 대응되는 몇몇 프로퍼티를 가지고 있다.

예제:

{ subject: 
   { C: 'UK',
     ST: 'Acknack Ltd',
     L: 'Rhys Jones',
     O: 'node.js',
     OU: 'Test TLS Certificate',
     CN: 'localhost' },
  issuer: 
   { C: 'UK',
     ST: 'Acknack Ltd',
     L: 'Rhys Jones',
     O: 'node.js',
     OU: 'Test TLS Certificate',
     CN: 'localhost' },
  valid_from: 'Nov 11 09:52:22 2009 GMT',
  valid_to: 'Nov  6 09:52:22 2029 GMT',
  fingerprint: '2A:7A:C2:DD:E5:F9:CC:53:72:35:99:7A:02:5A:71:38:52:EC:8A:DF' }

피어(peer)가 인증서를 제공하지 않는다면 null이나 비어있는 객체를 반환한다.

cleartextStream.getCipher()#

현재 연결의 암호문 이름과 SSL/TLS 프로토콜 버전을 나타내는 객체를 반환한다.

예제: { name: 'AES256-SHA', version: 'TLSv1/SSLv3' }

자세한 내용은 http://www.openssl.org/docs/ssl/ssl.html#DEALING_WITH_CIPHERS 에서 SSL_CIPHER_get_name()와 SSL_CIPHER_get_version()를 봐라.

cleartextStream.address()#

운영체제가 보고했듯이 기반하는 소켓의 바인딩된 주소와 주소 패밀리 이름과 포트를 반환한다. 다음과 같이 세 가지 프로퍼티를 가진 객체를 반환한다. { port: 12346, family: 'IPv4', address: '127.0.0.1' }

cleartextStream.remoteAddress#

원격 IP 주소를 나타내는 문자열이다. 예를 들면 '74.125.127.100''2001:4860:a005::68'이다.

cleartextStream.remotePort#

원격 포트르르 나태내는 숫자다. 예를 들면 443.

StringDecoder#

Stability: 3 - Stable

이 모듈은 require('string_decoder')로 사용한다. StringDecoder는 버퍼를 문자열로 디코딩한다. buffer.toString()의 간단한 인터페이스이지만 utf8에 대한 추가적인 지원을 한다.

var StringDecoder = require('string_decoder').StringDecoder;
var decoder = new StringDecoder('utf8');

var cent = new Buffer([0xC2, 0xA2]);
console.log(decoder.write(cent));

var euro = new Buffer([0xE2, 0x82, 0xAC]);
console.log(decoder.write(euro));

Class: StringDecoder#

기본값이 utf8encoding 아규먼트를 받는다.

StringDecoder.write(buffer)#

디코딩한 문자열을 반환한다.

File System#

Stability: 3 - Stable

파일 I/O는 표준 POSIX 함수의 랩퍼(wrapper)로 제공된다. 이 모듈을 사용하려면 require('fs')를 사용해라. 모든 함수는 비동기 방식과 동기방식이 있다.

비동기 방식은 항상 마지막 파라미터로 완료 콜백함수를 받는다. 완료 콜백에 전달되는 함수는 메서드에 따라 다르지만 첫 아규먼트는 항상 예외로 사용한다. 작업이 성공적으로 완료되면 첫 아규먼트는 null이나 undefined가 될 것이다.

동기형식을 사용할 때는 모든 예외가 즉시 던져진다. try/catch를 사용해서 예외를 다루거나 위로 버블링할 수 있다.

다음은 비동기 버전의 예제다.

var fs = require('fs');

fs.unlink('/tmp/hello', function (err) {
  if (err) throw err;
  console.log('successfully deleted /tmp/hello');
});

다음은 동기버전이다.

var fs = require('fs');

fs.unlinkSync('/tmp/hello')
console.log('successfully deleted /tmp/hello');

비동기 메서드를 사용할 때는 순서대로 실행된다는 보장을 하지 않는다. 그래서 다음 예제는 오류가 날 수 있다.

fs.rename('/tmp/hello', '/tmp/world', function (err) {
  if (err) throw err;
  console.log('renamed complete');
});
fs.stat('/tmp/world', function (err, stats) {
  if (err) throw err;
  console.log('stats: ' + JSON.stringify(stats));
});

fs.stat가 fs.rename`보다 먼저 실행될 수 있다. 이에 대한 올바른 방법은 콜백 체인으로 연결하는 것이다.

fs.rename('/tmp/hello', '/tmp/world', function (err) {
  if (err) throw err;
  fs.stat('/tmp/world', function (err, stats) {
    if (err) throw err;
    console.log('stats: ' + JSON.stringify(stats));
  });
});

프로세스가 바쁜 경우 프로그래머는 이러한 호출을 비동기 방식으로 사용하기를 강력히 추천한다. 동기방식은 모든 연결을 멈추고 작업이 완료될 때까지 전체 프로세스를 블락킹할 것이다.

파일명에 상대경로를 사용할 수 있지만 이 상대 경로는 process.cwd()이 대한 상대경로가 될 것이다.

fs.rename(oldPath, newPath, [callback])#

비동기 rename(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.renameSync(oldPath, newPath)#

동기 rename(2).

fs.truncate(fd, len, [callback])#

동기 ftruncate(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.truncateSync(fd, len)#

동기 ftruncate(2).

fs.chown(path, uid, gid, [callback])#

비동기 chown(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.chownSync(path, uid, gid)#

동기 chown(2).

fs.fchown(fd, uid, gid, [callback])#

비동기 fchown(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.fchownSync(fd, uid, gid)#

동기 fchown(2).

fs.lchown(path, uid, gid, [callback])#

비동기 lchown(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.lchownSync(path, uid, gid)#

동기 lchown(2).

fs.chmod(path, mode, [callback])#

비동기 chmod(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.chmodSync(path, mode)#

동기 chmod(2).

fs.fchmod(fd, mode, [callback])#

비동기 fchmod(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.fchmodSync(fd, mode)#

동기 fchmod(2).

fs.lchmod(path, mode, [callback])#

비동기 lchmod(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

Mac OS X에서만 사용할 수 있다.

fs.lchmodSync(path, mode)#

동기 lchmod(2).

fs.stat(path, [callback])#

비동기 stat(2). 콜백은 두 아규먼트 (err, stats)를 받고 statsfs.Stats 객체이다. 더 자세한 내용은 아래의 fs.Stats부분을 봐라.

fs.lstat(path, [callback])#

동기 lstat(2). 콜백은 두 아규먼트 (err, stats)를 받고 statsfs.Stats 객체다. lstat()path가 심볼릭 링크일 경우 참조하는 파일이 아닌 심볼릭 링크 자체의 상태라는 점만 빼면 stat()와 같다.

fs.fstat(fd, [callback])#

비동기 fstat(2). 콜백은 두 아규먼트 (err, stats)를 받고 statsfs.Stats 객체다. fstat()은 상태를 확인하는 파일이 파일 디스크립터 fd가 지정한 파일이라는 점만 빼면 stat()와 같다.

fs.statSync(path)#

동기 stat(2). fs.Stats 인스턴스를 반환한다.

fs.lstatSync(path)#

동기 lstat(2). fs.Stats 인스턴스를 반환한다.

fs.fstatSync(fd)#

동기 fstat(2). fs.Stats 인스턴스를 반환한다.

fs.link(srcpath, dstpath, [callback])#

비동기 link(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.linkSync(srcpath, dstpath)#

동기 link(2).

fs.symlink(srcpath, dstpath, [type], [callback])#

비동기 symlink(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다. type 아규먼트는 'dir'이나 'file', 'junction'이 가능하다.(기본값은 'file'이다) 이 옵션은 윈도우에서만 사용된다.(다른 플랫폼에서는 무시한다.) Windows의 junction에서는 목적지경로가 절대경로여야 한다. 'junction'을 사용하면 destination 아규먼트를 절대경로로 자동으로 정규화한다.

fs.symlinkSync(srcpath, dstpath, [type])#

동기 symlink(2).

fs.readlink(path, [callback])#

비동기 readlink(2). 콜백은 두 아규먼트 (err, linkString)를 받는다.

fs.readlinkSync(path)#

동기 readlink(2). 심볼릭 링크의 문자열 값을 반환한다.

fs.realpath(path, [cache], callback)#

비동기 realpath(2). callback은 두 개의 아규먼트 (err, resolvedPath)를 받는다. 상대경로를 처리하려면 process.cwd를 사용해야 할 것이다. cache는 실제 경로를 알기 위해 지정한 경로 처리를 강제하거나 추가적인 fs.stat 호출을 피하기 위해 사용할 수 있는 매핑된 경로의 객체리터럴이다.

예제:

var cache = {'/etc':'/private/etc'};
fs.realpath('/etc/passwd', cache, function (err, resolvedPath) {
  if (err) throw err;
  console.log(resolvedPath);
});

fs.realpathSync(path, [cache])#

동기 realpath(2). 처리된 경로를 반환한다.

fs.unlink(path, [callback])#

비동기 unlink(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.unlinkSync(path)#

동기 unlink(2).

fs.rmdir(path, [callback])#

비동기 rmdir(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.rmdirSync(path)#

동기 rmdir(2).

fs.mkdir(path, [mode], [callback])#

비동기 mkdir(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다. mode의 기본값은 0777이다.

fs.mkdirSync(path, [mode])#

동기 mkdir(2).

fs.readdir(path, [callback])#

비동기 readdir(3). 디렉토리의 내용을 읽는다. 콜백은 두 아규먼트 (err, files)를 받고 files는 디렉토리에서 '.''..'를 제외한 파일명들의 배열이다.

fs.readdirSync(path)#

동기 readdir(3). '.''..'를 제외한 파일명들의 배열을 반환한다.

fs.close(fd, [callback])#

비동기 close(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.closeSync(fd)#

동기 close(2).

fs.open(path, flags, [mode], [callback])#

비동기 파일 열기. open(2).를 봐라. flags는 다음의 값이 될 수 있다.

  • 'r' - 읽기모드로 파일을 연다. 파일이 존재하지 않으면 예외가 발생한다.

  • 'r+' - 읽기와 쓰기모드로 파일을 연다. (파일이 존재하지 않으면) 파일을 생성하거나 (파일이 존재하면) 새로 쓴다.

  • 'rs' - 동기방식으로 읽는 파일을 연다. 운영체제가 로컬 파일시스템 캐시를 우회하도록 한다.

    오래됐을 수도 있는 로컬 캐시를 무시하고 NFS 마운트에서 파일을 열 때 주고 유용하다. 이 모드는 I/O 성능에 실제로 큰 영향을 주기 때문에 필요한 경우가 아니면 사용하지 말아야 한다.

    이 모드는 fs.open()를 동기적인 블락킹 호출로 바꾸지 않는다. 동기적인 블락킹 호출이 필요하다면 fs.openSync()를 사용해야 한다.

  • 'rs+' - 운영체제가 동기방식으로 일기와 쓰기모드로 파일을 열도록 한다. 이 모드를 사용할 때의 주의점은 'rs'를 봐라.

  • 'w' - 쓰기모드로 파일을 연다. (파일이 존재하지 않으면) 파일을 생성하거나 (파일이 존재하면) 새로 쓴다.

  • 'wx' - 'w'와 비슷하지만 독점 모드로 파일을 연다.

  • 'w+' - 읽기와 쓰기모드로 파일을 연다. (파일이 존재하지 않으면) 파일을 생성하거나 (파일이 존재하면) 새로 쓴다.

  • 'a' - 추가모드로 파일을 연다. 파일이 존재하지 않으면 예외가 발생한다.

  • 'ax' - 'a'와 비슷하지만 독점 모드로 파일을 연다.

  • 'a+' - 읽기와 추가모드로 파일을 연다. 파일이 존재하지 않으면 예외가 발생한다.

  • 'ax+' - 'a+'와 비슷하지만 독점 모드로 파일을 연다.

mode와 기본값은 0666이다. 콜백은 두 아규먼트 (err, fd)를 받는다.

독점 모드(O_EXCL)는 새로 path를 생성한다는 것을 보장한다. 해당 파일명이 이미 존재하는 경우 fs.open()는 실패한다. POSIX 시스템에서 심볼릭링크는 포함되지 않는다. 네트워크 파일 시스템에서 독점 모드는 동작할 수도 있고 동작하지 않을 수도 있다.

fs.openSync(path, flags, [mode])#

동기 open(2).

fs.utimes(path, atime, mtime, [callback])#

fs.utimesSync(path, atime, mtime)#

전달한 경로가 참조하는 파일의 타임스탬프를 변경한다.

fs.futimes(fd, atime, mtime, [callback])#

fs.futimesSync(fd, atime, mtime)#

전달한 파일 디스크립터가 참조하는 파일의 타임스탬프를 변경한다.

fs.fsync(fd, [callback])#

비동기 fsync(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.fsyncSync(fd)#

동기 fsync(2).

fs.write(fd, buffer, offset, length, position, [callback])#

fd가 지정한 파일에 buffer를 작성한다.

offsetlength는 작성할 버퍼의 부분을 결정한다.

position은 이 데이터를 작성해야할 파일의 시작 위치부터의 오프셋을 참조한다. positionnull이면 데이터는 현재 위치에 작성할 것이다. pwrite(2)를 봐라.

콜백은 세 아규먼트 (err, written, buffer)를 받고 writtenbuffer에서 얼마나 많은 바이트가 작성되었는 지를 가리킨다.

콜백을 기다리지 않고 같은 파일에 여러번 fs.write를 사용하는 것은 안전하지 않다. 이 경우에 fs.createWriteStream를 사용하기를 강력하게 추천한다.

fs.writeSync(fd, buffer, offset, length, position)#

fs.write()의 동기 버전. 작성한 바이트 수를 반환한다.

fs.read(fd, buffer, offset, length, position, [callback])#

fd가 지정한 파일에서 데이터를 읽는다.

buffer는 데이터가 작성될 버퍼이다.

offset은 버퍼내에서 읽기 시작할 오프셋이다.

length는 읽어들일 바이트 수를 지정하는 정수이다.

position은 파일에서 읽어들이기 시작하는 위치를 지정하는 정수이다. positionnull이면 데이터는 파일의 현재 위치에서 읽을 것이다.

콜백은 세 아규먼트 (err, bytesRead, buffer)를 받는다.

fs.readSync(fd, buffer, offset, length, position)#

fs.read의 동기 버전이다. bytesRead의 수를 반환한다.

fs.readFile(filename, [encoding], [callback])#

파일의 전체 내용을 비동기로 읽는다. 예제:

fs.readFile('/etc/passwd', function (err, data) {
  if (err) throw err;
  console.log(data);
});

콜백에는 두 아규먼트 (err, data)를 전달하고 data는 파일의 내용이다.

인코딩을 지정하지 않으면 로우(raw) 버퍼를 반환한다.

fs.readFileSync(filename, [encoding])#

fs.readFile의 동기버전이다. filename의 내용을 반환한다.

encoding을 지정하면 이 함수는 문자열을 반환하고 encoding을 지정하지 않으면 버퍼를 반환한다.

fs.writeFile(filename, data, [encoding], [callback])#

비동기로 파일에 데이터를 작성하고 파일이 이미 존재하는 경우에는 파일을 대체한다. data는 문자열이나 버퍼가 될 수 있다. data가 버퍼일 경우 encoding 아규먼트는 무시된다. encoding의 기본값은 'utf8'이다.

예제:

fs.writeFile('message.txt', 'Hello Node', function (err) {
  if (err) throw err;
  console.log('It\'s saved!');
});

fs.writeFileSync(filename, data, [encoding])#

fs.writeFile의 동기버전이다.

fs.appendFile(filename, data, encoding='utf8', [callback])#

비동기로 파일에 데이터를 추가하고 파일이 존재하지 않는 경우 파일을 생성한다. data는 문자열이거나 버퍼다. data가 버퍼인 경우 encoding 아규먼트는 무시한다.

예제:

fs.appendFile('message.txt', 'data to append', function (err) {
  if (err) throw err;
  console.log('The "data to append" was appended to file!');
});

fs.appendFileSync(filename, data, encoding='utf8')#

fs.appendFile의 동기버전이다.

fs.watchFile(filename, [options], listener)#

Stability: 2 - Unstable.  가능하다면 대신 fs.watch를 사용해라.

filename의 변경사항을 감시한다. 콜백 listener는 파일이 접근될 때마다 호출될 것이다.

두번째 아규먼트는 선택사항이다. options을 전달하는 경우 options은 두 불리언값의 멤버변수 persistentinterval을 담고 있는 객체가 될 것이다. persistent는 파일을 감사하는 동안 계속해서 프로세스가 실행되어야 하는지를 나타낸다. interval은 얼마나 자주 대상을 확인해야 하는지를 밀리초로 나타낸다. 기본값은 { persistent: true, interval: 5007 }이다.

listener는 두 아규먼트 현재의 stat 객체와 이전의 stat 객체를 받는다.

fs.watchFile('message.text', function (curr, prev) {
  console.log('the current mtime is: ' + curr.mtime);
  console.log('the previous mtime was: ' + prev.mtime);
});

이 stat 객체들은 fs.Stat 인스턴스다.

그냥 파일에 접근만 했을 때가 아니라 파일이 수정되었을 때 알림을 받고 싶다면 curr.mtimeprev.mtime를 비교해야 한다.

fs.unwatchFile(filename, [listener])#

Stability: 2 - Unstable.  가능하다면 대신 fs.watch를 사용해라.

filename의 변경사항을 감시하는 것을 멈춘다. listener를 지정하면 해당 리스너만 제거한다. listener를 지정하지 않으면 모든 리스너를 제거하고 filename 감시를 효율적으로 멈춘다.

감시받지 않는 파일명으로 fs.unwatchFile()를 호출하면 오류는 발생하지 않고 아무 작업도 일어나지 않는다.

fs.watch(filename, [options], [listener])#

Stability: 2 - Unstable.

filename의 변경사항을 감시하고 filename은 파일이나 디렉토리가 될 수 있다. 반환객체는 fs.FSWatcher이다.

두번째 아규먼트는 선택사항이다. options을 전달한다면 options은 불리언값의 멤버변수 persistent를 담고 있는 객체여야 한다. persistent는 파일을 감사하는 동안 계속해서 프로세스가 실행되어야 하는지를 나타낸다. 기본값은 { persistent: true }이다.

리스너 콜백은 두 아규먼트 (event, filename)를 받는다. event는 'rename'나 'change'이고 filename은 이벤트를 발생시킨 파일의 이름이다.

Caveats#

fs.watch API는 모든 플랫폼에서 100% 일치하지 않고 몇몇 상황에서는 사용할 수 없다.

Availability#

이 기능은 의존 운영체제가 제공하는 파일시스템의 변경사항을 알리는 방법에 따라 다르다.

  • Linux 시스템에서 이 기능은 inotify를 사용한다.
  • BSD 시스템에서 (OS X 포함) 이 기능은 kqueue를 사용한다.
  • SunOS 시스템에서 (Solaris와 SmartOS 포함) 이 기능은 event ports를 사용한다.
  • Windows 시스템에서 이 기능은 ReadDirectoryChangesW에 달려있다.

몇가지 이유로 의존하는 기능을 사용할 수 없다면 fs.watch를 사용할 수 없을 것이다. 예를 들어 네트워크 파일시스템(NFS, SMB 등)에서 파일이나 디렉토리를 감시하면 종종 신뢰할 수 있을만큼 동작하지 않거나 전혀 작동하지 않는다.

stat 폴링(polling)을 사용하지만 더 느리고 덜 신뢰적인 fs.watchFile는 여전히 사용할 수 있다.

Filename Argument#

모든 클랫폼에서 filename 아규먼트를 콜백에 전달하는 것은 아니다. (현재는 Linux와 Windows에서만 지원한다.) 이를 지원하는 플랫폼에서 조차도 filename을 항상 제공한다고 보장하는 것은 아니다. 그러므로 콜백에 filename 아규먼트가 항상 전달된다고 가정하지 말고 null 일 경우를 위한 대체(fallback) 로직을 가지고 있어야 한다.

fs.watch('somedir', function (event, filename) {
  console.log('event is: ' + event);
  if (filename) {
    console.log('filename provided: ' + filename);
  } else {
    console.log('filename not provided');
  }
});

fs.exists(path, [callback])#

파일시스템을 확인해서 전달한 경로가 존재하는지 검사한다. 존재여부fmf true나 false로 callback을 호출한다. 예제:

fs.exists('/etc/passwd', function (exists) {
  util.debug(exists ? "it's there" : "no passwd!");
});

fs.existsSync(path)#

fs.exists의 동기버전이다.

Class: fs.Stats#

fs.stat(), fs.lstat(), fs.fstat()가 리턴하는 객체고 이 함수들의 동기함수들도 이 타입을 리턴한다.

  • stats.isFile()
  • stats.isDirectory()
  • stats.isBlockDevice()
  • stats.isCharacterDevice()
  • stats.isSymbolicLink() (only valid with fs.lstat())
  • stats.isFIFO()
  • stats.isSocket()

정규 파일에 대한 util.inspect(stats)는 다음과 유사한 문자열을 리턴할 것이다.

{ dev: 2114,
  ino: 48064969,
  mode: 33188,
  nlink: 1,
  uid: 85,
  gid: 100,
  rdev: 0,
  size: 527,
  blksize: 4096,
  blocks: 8,
  atime: Mon, 10 Oct 2011 23:24:11 GMT,
  mtime: Mon, 10 Oct 2011 23:24:11 GMT,
  ctime: Mon, 10 Oct 2011 23:24:11 GMT }

atime, mtime, ctimeDate 객체의 인스턴스이고 이 객체의 값들을 비교하려면 적절한 메서드를 사용해야한다. 대부분의 경우 getTime()는 1980년 1월 _1일부터 경과된 밀리초를 반환할 것이고 이 정수값은 비교하기에 충분하다. 하지만 명확하지 않은 정보를 보여주는데 사용할 수 있는 추가적인 메서드들이 있다. 더 자세한 내용은 MDN JavaScript Reference 페이지에 있다.

fs.createReadStream(path, [options])#

새로운 ReadStream 객체를 반환한다. (Readable Stream를 봐라)

options는 다음의 기본값을 가진 객체다.

{ flags: 'r',
  encoding: null,
  fd: null,
  mode: 0666,
  bufferSize: 64 * 1024
}

options는 전체 파일대신 읽어드릴 파일의 범위인 startend를 포함할 수 있다. startend 둘 다 포함하고 0 부터 시작한다. encoding'utf8', 'ascii', 'base64'가 될 수 있다.

100 바이트 길이인 파일의 마지막 10 바이트를 읽는 예제.

fs.createReadStream('sample.txt', {start: 90, end: 99});

Class: fs.ReadStream#

ReadStreamReadable Stream이다.

Event: 'open'#

  • fd Integer ReadStream이 사용하는 파일 디스크립터.

ReadStream의 파일이 열렸을 때 발생한다.

fs.createWriteStream(path, [options])#

새로운 WriteStream 객체를 반환한다. (Writable Stream를 봐라.)

options는 다음의 기본값을 갖는 객체다.

{ flags: 'w',
  encoding: null,
  mode: 0666 }

options도 파일의 시작위치가 아닌 다른 위치에 데이터를 작성하도록 start 옵션을 포함할 수도 있다. 파일을 교체하는 대신에 파일을 수정하려면 flags 모드를 기본값인 w 대신에 r+를 사용해야 한다.

fs.WriteStream#

WriteStreamWritable Stream이다.

Event: 'open'#

  • fd Integer WriteStream이 사용하는 파일 디스크립터.

WriteStream의 파일이 열렸을 때 발생한다.

file.bytesWritten#

지금까지 작성된 바이트의 수. 작성하기 위해 아직 큐에 있는 데이터는 포함하지 않는다.

Class: fs.FSWatcher#

fs.watch()가 반환하는 객체가 이 타입이다.

watcher.close()#

주어진 fs.FSWatcher에서 변경사항을 감시하는 것을 멈춘다.

Event: 'change'#

  • event String fs 변경사항의 타입
  • filename String 변경된 파일명 (적절하거나 사용가능하다면)

감시하는 디렉토리나 파일명에서 어떤 변경이 생겼을 때 발생한다. 더 자세한 내용은 fs.watch를 봐라.

Event: 'error'#

  • error Error object

오류가 생겼을 때 발생한다.

Path#

Stability: 3 - Stable

이 모듈에는 파일 경로를 다루고 변경하는 유틸리티가 포함되어 있다. 이 모듈 대부분의 메서드들은 문자열 변경만 수행한다. 경로가 유효한지 확인하는데 파일 시스템이 관여하지 않는다.

이 모듈을 사용하려면 require('path')를 사용해라. 다음의 메서드들이 제공된다.

path.normalize(p)#

'..''.' 부분을 처리해서 문자열 경로를 정규화한다.

슬래시가 여러 개 있는 경우 슬래시 하나로 교체하고 경로의 마지막에 슬래시가 있는 경우에는 유지한다. windows에서는 역슬래시를 사용한다.

예제:

path.normalize('/foo/bar//baz/asdf/quux/..')
// returns
'/foo/bar/baz/asdf'

path.join([path1], [path2], [...])#

모든 아규먼트를 합쳐서 최종 경로로 정규화한다. 문자열이 아닌 아규먼트는 무시한다.

예제:

path.join('/foo', 'bar', 'baz/asdf', 'quux', '..')
// returns
'/foo/bar/baz/asdf'

path.join('foo', {}, 'bar')
// returns
'foo/bar'

path.resolve([from ...], to)#

to를 절대경로로 변환한다.

to가 절대경로가 아니면 절대경로를 찾을 때까지 from 아규먼트들을 우측에서 좌측의 순서로 앞에 이어붙힌다.모든 from 경로를 사용한 후에도 절대경로를 찾지 못하면 현재 워킹 디렉토리를 사용한다. 최종 경로는 정규화되고 경로가 루트 디렉토리로 처리되지 않는한 마지막 슬래시는 제거한다. 문자열이 아닌 아규먼트는 무시한다.

이는 쉘에서 cd 명령어를 순서대로 실행한 것과 같다.

path.resolve('foo/bar', '/tmp/file/', '..', 'a/../subfile')

이는 다음과 비슷하다.:

cd foo/bar
cd /tmp/file/
cd ..
cd a/../subfile
pwd

다른 경로라 존재할 필요가 없거나 파일일 수도 있다는 점만이 다르다.

예제:

path.resolve('/foo/bar', './baz')
// returns
'/foo/bar/baz'

path.resolve('/foo/bar', '/tmp/file/')
// returns
'/tmp/file'

path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif')
// if currently in /home/myself/node, it returns
'/home/myself/node/wwwroot/static_files/gif/image.gif'

path.relative(from, to)#

from에서 to까지의 상대경로를 처리한다.

때로는 두 개의 절대경로를 가지고 있고 하나에서 다른 하나로의 상대경로를 얻어야 한다. 사실 이는 path.resolve의 반대 변환이다. 이 의미를 다음 예제에서 보자.

path.resolve(from, path.relative(from, to)) == path.resolve(to)

예제:

path.relative('C:\\orandea\\test\\aaa', 'C:\\orandea\\impl\\bbb')
// returns
'..\\..\\impl\\bbb'

path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb')
// returns
'../../impl/bbb'

path.dirname(p)#

경로의 디렉토리이름을 반환한다. Unix의 dirname 명령어와 비슷하다.

예제:

path.dirname('/foo/bar/baz/asdf/quux')
// returns
'/foo/bar/baz/asdf'

path.basename(p, [ext])#

경로의 마지막 부분을 반환한다. Unix의 basename 명령어와 비슷하다.

예제:

path.basename('/foo/bar/baz/asdf/quux.html')
// returns
'quux.html'

path.basename('/foo/bar/baz/asdf/quux.html', '.html')
// returns
'quux'

path.extname(p)#

경로의 마지막 부분의 문자열에서 마지막 '.'에서부터 경로의 확장자를 반환한다. 경로의 마지막 부분에 '.'가 없거나 첫 글자가 '.'이라면 빈 문자열을 반환한다. 예제:

path.extname('index.html')
// returns
'.html'

path.extname('index.')
// returns
'.'

path.extname('index')
// returns
''

path.sep#

플랫폼의 파일 구분자. '\\''/'이다.

리눅스의 예제:

'foo/bar/baz'.split(path.sep)
// returns
['foo', 'bar', 'baz']

윈도우즈의 예제:

'foo\\bar\\baz'.split(path.sep)
// returns
['foo', 'bar', 'baz']

net#

Stability: 3 - Stable

net 모듈은 비동기적인 네트워크 레퍼다. 서버와 클라이언트(스트림이라고 부른다.)를 모두 생성하는 메서드들이 포함되어 있다. require('net');로 이 모듈을 사용할 수 있다.

net.createServer([options], [connectionListener])#

새로운 TCP 서버를 생성한다. connectionListener 아규먼트는 자동적으로 'connection' 이벤트의 리스너로 설정한다.

options은 다음 객체가 기본값이다.

{ allowHalfOpen: false
}

allowHalfOpentrue이면 소켓은 반대쪽 소켓이 FIN 패킷을 보냈을 때 자동으로 FIN 패킷을 보내지 않는다. 소켓은 읽을 수 없는 상태가 되지만 여전히 쓰기는 가능하다. 명시적으로 end() 메서드를 호출해야 한다. 더 자세한 내용은 'end' 이벤트를 봐라.

다음은 8124 포트에 대한 연결을 받는 에코 서버의 예제다.

var net = require('net');
var server = net.createServer(function(c) { //'connection' listener
  console.log('server connected');
  c.on('end', function() {
    console.log('server disconnected');
  });
  c.write('hello\r\n');
  c.pipe(c);
});
server.listen(8124, function() { //'listening' listener
  console.log('server bound');
});

이 예제는 telnet를 사용해서 테스트한다.

telnet localhost 8124

/tmp/echo.sock 소켓에서 응답을 받으려면 마지막에서 세번째 줄을 다음과 같이 바꾼다.

server.listen('/tmp/echo.sock', function() { //'listening' listener

UNIX 도메인 소켓서버에 접속하려면 nc를 사용해라.

nc -U /tmp/echo.sock

net.connect(options, [connectionListener])#

net.createConnection(options, [connectionListener])#

새로운 소켓 객체를 생성하고 전달한 위치로 소켓을 연다. 소켓 구성이 완료되었을 때 'connect' 이벤트가 발생할 것이다.

TCP 소켓에서 options 아규먼트는 다음을 지정하는 객체여야 한다.

  • port: 클라이언트가 접속할 포트 (필수).

  • host: 클라이언트가 접속할 호스트. 기본값은 'localhost'다.

  • localAddress: 네트워크 연결에 바인딩할 로컬 인터페이스.

For UNIX domain sockets, options argument should be an object which specifies: UNIX계열 소켓에서는 options 아규먼트는 다음을 지정하는 객체여야 한다.

  • path: 클라이언트가 접속할 경로. (필수)

공통 옵션은 다음과 같다.

  • allowHalfOpen: 이 값이 true이면 소켓의 반대쪽에서 FIN 패킷을 보냈을 때 자동으로 FIN 패킷을 보내지 않는다. 더 자세한 내용은 'end'를 봐라.

connectListener 파라미터를 'connect' 이벤트의 리스터로 추가할 것이다.

다음은 이전에 설명한 에코서버의 클리이언트 예제다.

var net = require('net');
var client = net.connect({port: 8124},
    function() { //'connect' listener
  console.log('client connected');
  client.write('world!\r\n');
});
client.on('data', function(data) {
  console.log(data.toString());
  client.end();
});
client.on('end', function() {
  console.log('client disconnected');
});

/tmp/echo.sock 소켓에 연결하려면 두번째 줄을 다음과 같이 변경한다.

var client = net.connect({path: '/tmp/echo.sock'},

net.connect(port, [host], [connectListener])#

net.createConnection(port, [host], [connectListener])#

hostport로 TCP 연결을 생성한다. host를 생략하면 'localhost'라고 가정한다. connectListener 파라미터는 'connect' 이벤트의 리스너로 추가될 것이다.

net.connect(path, [connectListener])#

net.createConnection(path, [connectListener])#

path로 유닉스 소켓 연결을 생성한다. connectListener 파라미터는 'connect' 이벤트의 리스너로 추가될 것이다.

Class: net.Server#

이 클래스는 TCP나 UNIX 서버를 생성하는데 사용한다. 서버는 새로 들어오는 연결을 받을 수 있는 net.Socket이다.

server.listen(port, [host], [backlog], [callback])#

지정한 porthost에서 연결을 받아들이기 시작한다. host를 생략하면 모든 IPv4 주소(INADDR_ANY)에서 직접 들어오는 연결을 받아들일 것이다. 포트를 0으로 지정하면 임의의 포트를 사용할 것이다.

백로그는 대기하는 연결의 큐의 최대 길이이다. 실제 길이는 리눅스의 tcp_max_syn_backlogsomaxconn같은 sysctl 설정으로 OS가 결정한다. 백로그의 기본값은 511이다.(512가 아니다)

이 함수는 비동기함수이다. 서버가 바인딩되었을 때 'listening' 이벤트가 발생할 것이다. 마지막 파라미터 callback'listening' 이벤트의 리스터로 추가할 것이다.

몇몇 유저는 실행했을 때 EADDRINUSE 오류가 발생하는 이슈가 있다. 이는 다른 서버가 이미 요청한 포트에서 동작하고 있다는 것을 의미한다. 이 오류를 다루는 한가지 방법은 약간 기다린 후에 다시 시도하는 것이다. 다음과 같은 코드로 처리할 수 있다.

server.on('error', function (e) {
  if (e.code == 'EADDRINUSE') {
    console.log('Address in use, retrying...');
    setTimeout(function () {
      server.close();
      server.listen(PORT, HOST);
    }, 1000);
  }
});

(주의: Node의 모든 소켓은 이미 SO_REUSEADDR를 설정한다.)

server.listen(path, [callback])#

전달한 path에서 연결을 받아들이는 UNIX 소켓서버를 시작한다.

이 함수는 비동기 함수이다. 서버가 바인딩되었을 때 'listening' 이벤트가 발생할 것이다. 마지막 파라미터 callback'listening' 이벤트의 리스터로 추가될 것이다.

server.listen(handle, [callback])#

  • handle Object
  • callback Function

handle 객체는 서버나 소켓으로 설정되거나(의존하는 _handle 멤버를 가진 어떤 것이든) {fd: <n>} 객체가 될 수 있다.

이 함수는 서버가 지정한 핸들에서 연결받아들이도록 하지만 파일 디스크립터나 핸들이 이미 포트나 도메인 소켓에 바인딩되었다고 가정한다.

Windows에서는 파일스크립터에서 연결을 받아들이는 것을 지원하지 않는다.

이 함수는 비동기 함수이다. 서버가 바인딩되었을 때 'listening' 이벤트가 발생할 것이다. 마지막 파라미터 callback'listening' 이벤트의 리스터로 추가될 것이다.

server.close([callback])#

서버가 새로운 연결을 받아들이는 것과 현재 존재하는 연결을 유지하는 것을 멈춘다. 이 함수는 비동기 함수이고 모든 연결이 종료되고 서버에서 'close' 이벤트가 발생했을 때 완전히 닫힌다. 선택적으로 'close' 이벤트를 받는 콜백을 전달할 수 있다.

server.address()#

운영체제가 보고한대로 서버에 바인딩된 주소와 주소 패밀리 이름과 포트를 반환한다. 운영체제에 할당된 주소를 사용했을 때 할당된 포트를 찾는데 유용하다. 세 개의 프로퍼티를 가진 객체를 리턴한다. 예시: { port: 12346, family: 'IPv4', address: '127.0.0.1' }

예제:

var server = net.createServer(function (socket) {
  socket.end("goodbye\n");
});

// grab a random port.
server.listen(function() {
  address = server.address();
  console.log("opened server on %j", address);
});

'listening' 이벤트가 발생할 때까지 server.address()를 호출하지 마라.

server.maxConnections#

서버의 연결수가 많아졌을 때 연결을 거절하려면 이 프로퍼티를 설정해라.

child_process.fork()로 자식에 소켓을 보냈을 때 이 옵션을 사용하는 것은 권하지 않는다.

server.connections#

서버의 동시접속 수.

child_process.fork()로 자식 프로세스에 소켓을 보냈을 때 이 값은 null이 된다.

net.Server는 다음 이벤트를 가진 EventEmitter이다.

Event: 'listening'#

server.listen가 호출된 후 서버가 바인딩되었을 때 발생한다.

Event: 'connection'#

  • Socket object 연결 객체

새로운 연결이 생성되었을 때 발생한다. socketnet.Socket의 인스턴스다.

Event: 'close'#

서버를 닫을 때 발생한다. 연결이 존재하는 경우 모든 연결이 종료될 때까지 이 이벤트틑 발생하지 않는다.

Event: 'error'#

  • Error Object

오류가 생겼을 때 발생한다. 이 이벤트 후에 곧바로 'close' 이벤트가 호출될 것이다. server.listen 설명에서의 예제를 봐라.

Class: net.Socket#

이 객체는 TCP나 UNIX 소켓의 추상화 객체다. net.Socket 인스턴스는 이중 Stream 인스턴스를 구현한다. net.Socket 인스턴스는 사용자가 생성해서 클라이언트처럼 (connect()를 사용해서) 사용하거나 Node가 생성해서 서버의 'connection' 이벤트로 사용자에게 전달할 수도 있다.

new net.Socket([options])#

새로운 소켓 객체를 생성한다.

options은 다음의 기본값을 가진 객체다.

{ fd: null
  type: null
  allowHalfOpen: false
}

fd로 이미 존재하는 소켓의 파일 디스크립터를 지정할 수 있다. type은 의존하는 프로토콜들 지정한다. type'tcp4', 'tcp6', 'unix'가 될 수 있다. allowHalfOpen에 대해서는 createServer()'end' 이벤트를 참고해라.

socket.connect(port, [host], [connectListener])#

socket.connect(path, [connectListener])#

주어진 소켓에 대한 연결을 오픈한다. porthost를 절달하면 소켓은 TCP 소켓으로 열릴 것이고 host를 생략하면 localhost를 사용할 것이다. path를 전달하면 소켓은 해당 경로로의 유닉스 소켓으로 열릴 것이다.

net.createConnection이 소켓을 여는 한 이 메서드는 보통 필요하지 않다. 커스텀 Sokcet을 구현하거나 Socket이 닫혔는데 다른 서버로 연결할 때 이 닫힌 소켓을 다시 사용하고 싶을 때만 이 메서드를 사용해라.

이 함수는 비동기 함수이다. 'connect' 이벤트가 발생하면 소켓이 구성된 것이다. 연결하는데 문제가 있다면 'connect' 이벤트는 발생하지 않고 'error' 이벤트가 예외와 함께 발생할 것이다.

connectListener 파라미터는 'connect' 이벤트의 리스터로 추가된다.

socket.bufferSize#

net.Socketsocket.write()가 항상 동작하는 프로퍼티를 가지고 있다. 이 프로퍼티는 사용자가 빠르게 준비해서 실행하도록 도와준다. 컴퓨터는 소켓에 잘성된 데이터의 양을 항상 유지할 수는 없다. - 네트워크 연결은 쉽게 엄청 느려질 수도 있다. Node는 매부적으로 소켓에 작성된 데이터를 큐에 넣고 가능해 졌을 때 데이터를 보낼 것이다. (내부적으로 쓰기 가능한 상태를 유지하기 위해 소켓의 파일 디스크립트에서 폴링한다.)

이러한 내부적 버퍼링으로 인해서 메모리가 커질 수 있다. 이 프로퍼티는 쓰여지기 위해서 현재 버퍼된 글자의 수를 보여준다. (글자의 수는 대략적으로 쓰여질 바이트의 수와 같지만 버퍼는 문자열을 담고 있을 것이고 문자열은 지연 인코딩된다. 그래서 바이트의 정확한 수는 알지 못한다.)

엄청 크거나 커지는 bufferSize를 경험한 사용자들은 프로그램에서 pause()resume()로 데이터의 흐름을 "조절하는(throttle)" 시도를 해야한다.

socket.setEncoding([encoding])#

읽을 수 있는 스트림인 소켓의 인코딩을 설정한다. 더 자세한 내용은 stream.setEncoding()를 봐라.

socket.write(data, [encoding], [callback])#

소켓에 데이터를 보낸다. 두 번째 파라미터는 문자열인 경우에 인코딩을 지정한다. 기본 인코딩은 UTF8 인코딩이다.

전체 데이터를 커널 버퍼에 성공적으로 플러시(flush)했다면 true를 반환한다. 데이터의 일부나 전체가 사용자 메모리에 큐되었다면 false를 반환한다. 버퍼가 다시 비워졌을 때 'drain'이 발생할 것이다.

선택사항인 callback 파라미터는 데이터가 최종적으로 작성되었을 때 실행될 것이다. 이는 즉시 실행되는 것은 아니다.

socket.end([data], [encoding])#

소켓을 절반만 닫는다. 예를 들어 이 메서드는 FIN 패킷을 보낸다. 서버는 여전히 약간의 데이터를 보낼 수 있을 것이다.

data를 지정하면 socket.end()에 이어서 socket.write(data, encoding)를 호출한 것과 같다.

socket.destroy()#

이 소켓에는 더이상 I/O 작업이 없다는 것은 보증한다. 오류가 발생한 경우에만 필요하다. (오류를 파싱하는 등)

socket.pause()#

데이터를 읽는 것을 멈춘다. 즉, 'data' 이벤트가 발생하지 않는다. 업로드를 차단하는데 유용하다.

socket.resume()#

pause()를 호출한 후에 다시 읽기 시작한다.

socket.setTimeout(timeout, [callback])#

소켓에서 활동이 없는 timeout 밀리초 후의 타임아웃에 소켓을 설정한다. 기본적으로 net.Socket에는 타임아웃이 없다.

유휴시간(idle timeout)이 실행되었을 때 소켓은 'timeout' 이벤트를 받지만 연결은 끝기지 않는다. 사용자는 소켓을 수동으로 end()하거나 destroy()해야 한다.

timeout이 0이면 존재하는 유휴시간이 사용할 수 없게 된다.

선택사항인 callback 파라미터는 'timeout' 이벤트의 일회성 리스터로 추가될 것이다.

socket.setNoDelay([noDelay])#

Nagle 알고리즘을 사용하지 않는다. 기본적으로 TCP 연결은 Nagle 알고리즘을 사용해서 데이터를 보내기전에 데이터를 버퍼에 넣는다. noDelaytrue로 설정하면 socket.write()를 호출할 때마다 데이터를 즉시 보낼 것이다. noDelay의 기본값은 true이다.

socket.setKeepAlive([enable], [initialDelay])#

keep-alive 기능을 사용하거나 사용하지 않고 비어있는 소켓에 첫 keepalive 조사를 하기 전에 초기화 지연을 선택적으로 설정한다. enable의 기본값은 false다.

받은 마지막 데이터 패킷과 첫 keepalive 검사사이에 지연시간을 설정하는데 initialDelay를(밀리초로) 설정한다. initialDelay에 0을 설정하면 기본(혹은 이전의) 설정 값을 변경하지 않고 놔둘 것이다. 기본값은 0이다.

socket.address()#

운영체제가 보고한대로 소켓에 바인딩된 주소와 주소 패밀리 이름과 포트를 반환한다. 세 개의 프로퍼티를 가진 객체를 리턴한다. 예시: { port: 12346, family: 'IPv4', address: '127.0.0.1' }

socket.remoteAddress#

원격 IP 주소의 문자열 표현이다. 예를 들면 '74.125.127.100''2001:4860:a005::68'와 같은 식이다.

socket.remotePort#

원격 포트의 숫자 표현이다. 예를 들면 80이나 21이다.

socket.bytesRead#

받은 바이트의 양.

socket.bytesWritten#

보낸 바이트의 양.

net.Socket 인스턴스는 다음의 이벤트를 가진 EventEmitter이다.

Event: 'connect'#

소켓 연결이 성공적으로 이뤄졌을 때 발생한다. connect()를 봐라.

Event: 'data'#

  • Buffer object

데이터를 받았을 때 발생한다. data 아규먼트는 BufferString이 될 것이다. 데이터의 인코딩은 socket.setEncoding()로 설정한다. (더 자세한 내용은 Readable Stream를 봐라.)

Socket'data' 이벤트를 발생시켰을 때 리스너가 없으면 데이터를 잃어버릴 것이다.

Event: 'end'#

소켓의 반대쪽 끝에서 FIN 패킷을 보냈을 때 발생한다.

기본적으로 (allowHalfOpen == false) 소켓은 미뤄두었던 작성 큐를 다 소비하면 자신의 파일 디스크립터를 파괴할 것이다. 하지만 allowHalfOpen == true로 설정하면 소켓은 이제 반대쪽에 end()할 필요가 있다는 경고와 함께 사용자가 임의의 양의 데이터를 작성할 수 있도록 반대쪽에 end()를 자동으로 호출하지 않을 것이다.

Event: 'timeout'#

유휴상태에서 소켓의 시간이 만료되었을 때 발생한다. 이는 소켓이 아무일도 하지 않을 때만 알려준다. 사용자는 수동으로 연결을 닫아야 한다.

socket.setTimeout()도 봐라.

Event: 'drain'#

작성 버퍼가 비워졌을 때 발생한다. 업로드를 조절하는데 사용할 수 있다.

socket.write()의 반환값도 참고해라.

Event: 'error'#

  • Error object

오류가 생겼을 때 발생한다. 이 이벤트에 이어서 곧바로 'close' 이벤트가 호출될 것이다.

Event: 'close'#

  • had_error Boolean 소켓에 전송 오류가 있을 때 true 이다

소켓이 완전히 닫혔을 때 발생한다. had_error 아규먼트는 소켓이 전송 오류때문에 닫혔는 지를 알려주는 불리언 값이다.

net.isIP(input)#

input이 IP 주소인지 검사한다. 유효하지 않은 문자열이면 0을 반환하고 IP 버전 4 주소이면 4를 반환하고 IP 버전 6 주소이면 6을 반환한다.

net.isIPv4(input)#

input이 IP 버전 4 주소이면 true를 반환하고 IP 버전 4 주소가 아니면 false를 반환한다.

net.isIPv6(input)#

input이 IP 버전 6 주소이면 true를 반환하고 IP 버전 6 주소가 아니면 false를 반환한다.

UDP / Datagram Sockets#

Stability: 3 - Stable

데이터그램 소켓은 require('dgram')로 사용할 수 있다.

dgram.createSocket(type, [callback])#

  • type 문자열. 'udp4'나 'udp6'
  • callback 함수. message 이벤트에 리스너로 추가된다. 선택사항
  • 반환값: Socket 객체

지정한 타입의 데이터그램 소켓을 생성한다. 유효한 타입은 udp4udp6이다.

message 이벤트의 리스터로 추가되는 선택사항인 콜백을 받는다.

데이터그램을 받으려면 socket.bind를 호출해라. socket.bind()는 임의의 포트에서 "모든 인터페이스" 주소에 바인딩한다. (udp4udp6 소켓 둘다에 대해 맞는 것이다.) 그 다음 socket.address().addresssocket.address().port로 주소와 포트를 획득할 수 있다.

Class: Socket#

dgram Socket 클래스는 데이터그램 기능을 은닉화한다. 이 클래스는 dgram.createSocket(type, [callback])를 통해서 생성되어야 한다.

Event: 'message'#

  • msg Buffer 객체. 메시지
  • rinfo 객체. 원격 주소 정보

소켓에서 새로운 데이터그램을 사용할 수 있게 되었을 때 발생한다. msgBuffer이고 rinfo는 보내는 쪽의 주소정보와 데이터그램의 바이트 수를 담고 있는 객체다.

Event: 'listening'#

소켓이 데이트그램을 받기 시작했을 때 발생한다. 이는 UDP 소켓이 생성되자마자 발생한다.

Event: 'close'#

소켓이 close()로 닫혔을 때 발생한다. 소켓에서 더이상 새로운 message 이벤트가 발생하지 않는다.

Event: 'error'#

  • exception Error 객체

오류가 생겼을 때 발생한다.

dgram.send(buf, offset, length, port, address, [callback])#

  • buf Buffer 객체. 보내는 메시지다
  • offset 정수. 버퍼에서 메시지가 시작되는 오프셋.
  • length 정수. 메시지의 바이트 수
  • port 정수. 목적지 포트
  • address 문자열. 목적지 IP
  • callback 함수. 메시지가 배달완료되었을 때 호출될 콜백. 선택사항.

UDP 소켓에서 목적지 포트와 IP 주소는 반드시 지정해야 한다. address 파라미터에 문자열을 전달하고 이는 DNS로 처리될 것이다. 선택사항인 콜백은 DNS 오류와 buf가 다시 사용될 때를 탐지하기 위해 지정할 것이다. DNS 검색은 최소한 다음 tick까지 통신이 이뤄지는 시간동안 연기될 것이다. 통신이 콜백을 사용한다는 것을 확실하게 아는 유일한 방법이다.

소켓이 bind 호출로 이전에 바인딩되지 않았다면 임의의 포트번호를 할당받고 "모든 인터페이스" 주소에 바인딩 될 것이다. (udp4에는 0.0.0.0, udp6 소켓에는 ::0)

localhost의 임의의 포트로 UDP 패킷을 보내는 예제;

var dgram = require('dgram');
var message = new Buffer("Some bytes");
var client = dgram.createSocket("udp4");
client.send(message, 0, message.length, 41234, "localhost", function(err, bytes) {
  client.close();
});

UDP 데이터그램 크기에 대한 내용

IPv4/v6 데이터그램의 최대 크기는 MTU (Maximum Transmission Unit)와 Payload Length 필드 사이즈에 달려 있다.

  • Payload Length 필드는 16 bits 크기이다. 이는 일반적인 탑재량(payload)은 인터넷 헤더와 데이터를 포함해서 64K 옥텟보다 클 수가 없다는 의미이다 (65,507 bytes = 65,535 − 8 bytes UDP header − 20 bytes IP header); 이는 loopback 인터페이스에서 보통 true이지만 긴 데이터그램같은 것은 대부분의 호스트와 네트워크에서 비현실적이다.

  • MTU는 주어진 링크 계층 기술(link layer technology)이 데이터그램에 지원할 수 있는 최대 사이즈이다. 전체를 받든지 부분만 받든지 IPv4에 대해 추천되는 MTU576인 한 어떤 링크에서도 IPv468 옥텟의 최소 MTU를 지정한다. (보통 다이얼업(dial-up) 타입의 어플리케이션에 대해 MTU로 추천된다.)

    IPv6에서는 최소 MTU1280 옥텟이지만 강제적인 최소 단편 재조합 버퍼의 크기는 1500 옥텟이다. 가장 현대적인 링크계층 기술은 1500의 최소 MTU를 가지기 때문에(이더넷처럼) 68 옥텟의 값은 아주 작다.

패킷이 이동하는 각 링크의 MTU를 미리 아는 것은 불가능하고 보통 (수신자) MTU보다 큰 데이터그램을 전송하는 것은 동작하지 않는다. (데이터가 의도된 수신자에게 도달하지 않았다는 것을 소스에 알리지 않고 패킷을 경고없이 버린다.)

dgram.bind(port, [address])#

  • port 정수
  • address 문자열, 선택사항

UDP 소켓에서 port와 선택사항인 address에서 데이터그랩을 받는다. address를 지정하지 않으면 운영체제는 모든 주소에서 받을 것이다.

UDP 서버가 41234 포트에서 받는 예제:

var dgram = require("dgram");

var server = dgram.createSocket("udp4");

server.on("message", function (msg, rinfo) {
  console.log("server got: " + msg + " from " +
    rinfo.address + ":" + rinfo.port);
});

server.on("listening", function () {
  var address = server.address();
  console.log("server listening " +
      address.address + ":" + address.port);
});

server.bind(41234);
// server listening 0.0.0.0:41234

dgram.close()#

의존하는 소켓을 닫고 소켓에서 데이터를 받는 것을 멈춘다.

dgram.address()#

소켓에 대한 주소 정보를 담고 있는 객체를 반환한다. UDP 소켓에서 이 객체는 addressfamilyport를 담고 있을 것이다.

dgram.setBroadcast(flag)#

  • flag 불리언

SO_BROADCAST 소켓 옵션을 설정하거나 없앤다. 이 옵션을 설정하면 로컬 인터페이스의 브로드캐스트 주소로 UDP 패킷을 보낼 것이다.

dgram.setTTL(ttl)#

  • ttl 정수

IP_TTL 소켓 옵션을 설정한다. TTL은 "Time to Live"를 의미하지만 이 상황에서 TTL은 패킷이 거쳐가도 되는 IP 홉(hop)의 수를 지정한다. 패킷이 지나쳐가는 각 라우터나 게이트웨이는 TTL을 감소시킨다. TTL이 라우터에 의해서 0까지 줄어들면 더이상 전송되지 않을 것이다. TTL 값을 변경하는 것은 보통 네트워크 검사나 멀티캐스팅 될 때 이뤄진다.

setTTL()의 아규먼트는 1부터 255사이의 홉 수이다. 대부분의 시스템에서 기본값은 64이다.

dgram.setMulticastTTL(ttl)#

  • ttl 정수

IP_MULTICAST_TTL 소켓 옵션을 설정한다. TTL은 "Time to Live"를 의미하지만 이 상황에서 TTL은 특히 멀티캐스트 트래픽에 대해서 패킷이 거쳐가도 되는 IP 홉(hop)의 수를 지정한다. 패킷이 지나쳐가는 각 라우터나 게이트웨이는 TTL을 감소시킨다. TTL이 라우터에 의해서 0까지 줄어들면 더이상 전송되지 않을 것이다.

setMulticastTTL()의 아규먼트는 0부터 255사이의 홉(hop) 수이다. 대부분의 시스템에서 기본값은 1이다.

dgram.setMulticastLoopback(flag)#

  • flag 불리언

IP_MULTICAST_LOOP 소켓 옵션을 설정하거나 제거한다. 이 옵셩을 설정하면 멀티캐스트 패킷도 로컬 인터페이스에서 받을 것이다.

dgram.addMembership(multicastAddress, [multicastInterface])#

  • multicastAddress 문자열
  • multicastInterface 문자열, 선택사항

IP_ADD_MEMBERSHIP 소켓 옵션과 함께 멀티캐스트 그룹에 합류하도록 커널에 알려준다.

multicastInterface를 지정하지 않으면 운영체제는 회원을 유효한 모든 인터페이스에 추가하려고 할 것이다.

dgram.dropMembership(multicastAddress, [multicastInterface])#

  • multicastAddress 문자열
  • multicastInterface 문자열, 선택사항

addMembership와는 반대로 IP_DROP_MEMBERSHIP 소켓 옵션과 함께 멀티캐스트 그룹에서 나오도록 커널에 명령한다. 이는 소캣이 닫히거나 진행이 종료되었을 때 커널이 자동적으로 호출할 것이므로 대부분의 어플리케이션은 이 함수를 호출할 필요가 없을 것이다.

multicastInterface를 지정하지 않으면 운영체제는 회원을 유효한 모든 인터페이스에서 버리려고 할 것이다.

DNS#

Stability: 3 - Stable

이 모듈을 접근하려면 require('dns')를 사용해라. 쓰레드 풀에서 getaddrinfo(3)를 사용하는 dns.lookup를 제외한 dns 모듈의 모든 메서드들은 C-Ares를 사용한다. C-Ares가 getaddrinfo보다 훨신 더 빠르지만 시스템 리졸버는 다른 프로그램이 어떻게 동작하는 지에 더 관려이 있다. 사용자가 net.connect(80, 'google.com')http.get({ host: 'google.com' })를 사용하면 dns.lookup메서드가 사용된다. 빠르게 다량의 검색을 해야하는 사용자들은 C-Ares를 쓰는 메서드를 사용해야한다.

다음은 'www.google.com'를 처리하고 반대로 반환된 IP 주소를 도메인으로 처리하는 예제다.

var dns = require('dns');

dns.resolve4('www.google.com', function (err, addresses) {
  if (err) throw err;

  console.log('addresses: ' + JSON.stringify(addresses));

  addresses.forEach(function (a) {
    dns.reverse(a, function (err, domains) {
      if (err) {
        throw err;
      }

      console.log('reverse for ' + a + ': ' + JSON.stringify(domains));
    });
  });
});

dns.lookup(domain, [family], callback)#

도메인(예시: 'google.com')을 처음으로 찾은 A (IPv4)나 AAAA (IPv6) 레코드로 처리한다. family는 정수 46이 될 수 있다. 기본값은 Ip v4와 v6 주소 패밀리 둘 다를 의미하는 null이다.

콜백은 (err, address, family) 아규먼트를 받는다. address 아규먼트는 IP v4나 IP v6 주소의 문자열 표현이다. family 아규먼트는 정수 4나 6이고 address의 패밀리를 나타낸다. (이 값은 필수적으로 lookup에 전달해야 하는 값은 아니다.)

dns.resolve(domain, [rrtype], callback)#

도메인(예시: 'google.com')을 rrtype에 지정한 레코드 종류의 배열로 처리한다. 유효한 rrtype은 'A'(기본값인 IPV4 주소), 'AAAA' (IPV6 주소), 'MX' (메일 교환 레코드), 'TXT' (텍스트 레코드), 'SRV' (SRV 레코드), 'PTR' (반대로 IP 검색에 사용된다.), 'NS' (네임서버 레코드), 'CNAME' (공인된 이름(canonical name) 레코드)다.

콜백은 (err, addresses) 아규먼트다. addresses에서 각 아이템의 타입은 레코드 타입으로 결정되고 아래 대응하는 검색 메서드의 문서에서 설명한다.

오류가 있을 때 err는 아래 목록에 있는 오류코드 중 하나인 err.code가 있는 Error 객체다.

dns.resolve4(domain, callback)#

dns.resolve()와 같지만 IPv4 조회(A 레코드)에만 사용한다. addresses는 IPv4 주소의 배열이다. (예시: ['74.125.79.104', '74.125.79.105', '74.125.79.106'])

dns.resolve6(domain, callback)#

dns.resolve4()와 같지만 IPv6 조회(AAAA 레코드)에만 사용한다.

dns.resolveMx(domain, callback)#

dns.resolve()와 같지만 메일 교환 조회(MX 레코드)에만 사용한다.

addresses는 MX 레코드의 배열이다. 각 레코드는 우선순위와 교환속성을 가진다. (예시: [{'priority': 10, 'exchange': 'mx.example.com'},...])

dns.resolveTxt(domain, callback)#

dns.resolve()와 같지만 텍스트 조회(TXT 레코드)에만 사용한다. addressesdomain에서 사용가능한 텍스트 레코드의 배열이다. (예시: ['v=spf1 ip4:0.0.0.0 ~all'])

dns.resolveSrv(domain, callback)#

dns.resolve()와 같지만 서비스 레코드(SRV 레코드)에만 사용한다. addressesdomain에서 사용가능한 SRV 레코드의 배열이다. SRV 레코드의 프로퍼티들은 우선순위, 중요도, 포트, 이름이다. (예시: [{'priority': 10, {'weight': 5, 'port': 21223, 'name': 'service.example.com'}, ...])

dns.resolveNs(domain, callback)#

dns.resolve()와 같지만 네임서버 레코드(NS 레코드)에만 사용한다. addressesdomain에서 사용가능한 네임서버 레코드의 배열이다. (예시: ['ns1.example.com', 'ns2.example.com'])

dns.resolveCname(domain, callback)#

dns.resolve()와 같지만 공인된 이름(canonical name) 레코드(CNAME 레코드)에만 사용한다. addressesdomain에서 사용가능한 공인된 이름 레코드의 배열이다. (예시: ['bar.example.com'])

dns.reverse(ip, callback)#

반대로 ip 주소를 도메인명의 배열로 처리한다.

콜백은 (err, domains) 아규먼트를 받는다.

오류가 있을 때 err는 아래 목록에 있는 오류코드 중 하나인 err.code가 있는 Error 객체다.

Error codes#

각 DNS 조회는 다음 오류코드 중 하나를 반환할 수 있다.

  • dns.NODATA: DNS 서버가 데이터가 없다는 응답함.
  • dns.FORMERR: DNS 서버가 쿼리의 형식이 잘못되었다고 응답함.
  • dns.SERVFAIL: DNS 서버가 일반적인 실패를 반환함.
  • dns.NOTFOUND: 도메인 명을 못 찾음.
  • dns.NOTIMP: DNS 서버가 요청된 작업을 구현하지 않음.
  • dns.REFUSED: DNS 서버가 조회를 거절함.
  • dns.BADQUERY: 형식이 잘못된 DNS 조회.
  • dns.BADNAME: 형식이 잘못된 도메인명.
  • dns.BADFAMILY: 지원되지 않는 주소 패밀리.
  • dns.BADRESP: 형식이 잘못된 DNS 응답.
  • dns.CONNREFUSED: DNS 서버에 접속할 수 없음.
  • dns.TIMEOUT: DNS 서버에 접속하는 동안 타임아웃 발생.
  • dns.EOF: 파일의 끝.
  • dns.FILE: 파일을 읽는 중 오류발생.
  • dns.NOMEM: Out of memory.
  • dns.DESTRUCTION: 채널이 파괴됨.
  • dns.BADSTR: 형식이 잘못된 문자열.
  • dns.BADFLAGS: 허용되지 않는 플래그가 지정됨.
  • dns.NONAME: 주어진 호스트명이 숫자가 아님.
  • dns.BADHINTS: 허용되지 않는 힌트 플래그가 지정됨.
  • dns.NOTINITIALIZED: c-ares 라이브러리 초기화가 아직 수행되지 않음.
  • dns.LOADIPHLPAPI: iphlpapi.dll을 로딩하는 데 오류발생.
  • dns.ADDRGETNETWORKPARAMS: GetNetworkParams 함수를 찾을 수 없음.
  • dns.CANCELLED: DNS 조회가 취소됨.

HTTP#

Stability: 3 - Stable

HTTP 서버와 클라이언트를 사용하려면 require('http')를 사용해라.

Node의 HTTP 인터페이스는 전통적으로 다루기 어려웠던 프로토콜의 많은 기능들을 지원하려고 디자인되었다. 특히 크고 청크로 인코딩 될수 있는 메시지들이다. 인터페이스는 전체 요청이나 응답을 버퍼에 넣지 않는다. 사용자는 스트림 데이터를 버퍼에 넣을 수 있다.

HTTP 메시지 헤더는 다음과 같은 객체로 표현된다.

{ 'content-length': '123',
  'content-type': 'text/plain',
  'connection': 'keep-alive',
  'accept': '*/*' }

키는 소문자로 쓰고 값은 수정되지 않는다.

HTTP 어플리케이션이 가능한 전체 범위를 다 지원하기 위해서 Node의 HTTP API는 상당히 저수준의 API이다. API는 스트림 핸들링과 메시지 파싱만을 다룬다. 메시지를 헤더와 바디로 파싱하지만 실제 헤더와 바디는 파싱하지 않는다.

http.STATUS_CODES#

  • Object

모든 표준 HTTP 응답 상태코드와 짧은 설명의 모음이다. 예를 들어 http.STATUS_CODES[404] === 'Not Found'와 같이 할 수 있다.

http.createServer([requestListener])#

새로운 웹서버 객체를 반환한다.

requestListener는 자동으로 'request' 이벤트에 추가되는 함수다.

http.createClient([port], [host])#

이 함수는 폐기되었다. 대신에 http.request()를 사용해라. 새로운 HTTP 클라이언트를 구성한다. porthost는 연결할 서버를 가리킨다.

Class: http.Server#

이 클래스는 다음의 이벤트를 가진 EventEmitter다.

Event: 'request'#

function (request, response) { }

요청이 있을 때마다 발생한다. 연결마다 여러번의 요청이 있을 수 있다.(keep-alive 연결인 경우) requesthttp.ServerRequest의 인스턴스이고 responsehttp.ServerResponse의 인스턴스다.

Event: 'connection'#

function (socket) { }

새로운 TCP 스트림이 생성되었을 때 발생한다. socketnet.Socket 타입의 객체다. 보통 사용자들은 이 이벤트에 접근하지 않을 것이다. socketrequest.connection에서도 접근할 수 있다.

Event: 'close'#

function () { }

서버가 닫혔을 때 발생한다.

Event: 'checkContinue'#

function (request, response) { }

http Expect: 100-continue 헤더를 가진 요청을 받을 때마다 발생한다. 이 이벤트가 바인딩되지 않았다면 서버는 자동적으로 적절한 100 Continue로 응답할 것이다.

이 이벤트를 다루면 클라이언트가 계속해서 요청바디를 보내야 한다면 response.writeContinue 호출하고 클라이언트가 요청 바디를 계속 보내지 않는다면 적절한 HTTP 응답(예시: 400 Bad Request)을 생성한다.

Event: 'connect'#

function (request, socket, head) { }

클라이언트가 http CONNECT 메서드를 요청할 때마다 발생한다. 이 이벤트에 등록된 리스너가 없으면 CONNECT 메서드를 요청한 클라이언트의 연결이 닫힐 것이다.

  • request는 request 이벤트와 같이 http 요청의 아규먼트다.
  • socket는 서버와 클라이언트 간의 네트워크 소켓이다.
  • head는 터널링 스트림의 첫 패킷인 Buffer의 인스턴스다. 이 값은 비어있을 것이다.

이 이벤트가 발생한 후 요청의 소켓은 data 이벤트 리스너를 가지지 않을 것이다. 즉, 해당 소켓으로 서버에 보낸 데이터를 다루려면 리스너에 바인딩해야 한다.

Event: 'upgrade'#

function (request, socket, head) { }

클라이언트가 http 업그래이드를 요청할 때마다 발생한다. 이 이벤트가 바인딩되지 않았다면 업그래이드를 요청하는 클라이언트는 닫힌 연결을 가질 것이다.

  • request은 요청이벤트에 있는 것처럼 http 요청의 아규먼트다.
  • socket은 서버와 클라이언트 사이의 네트워크 소켓이다.
  • head는 Buffer의 인스턴스다. 업그래이드된 스트림의 첫 패킷이고 비어있을 수도 있다.

이 이벤트가 발생한 후에 요청의 소켓은 data 이벤트 리스너를 갖지 않을 것이다. 이는 해당 소켓으로 서버에 보내는 메시지를 다루기 위해서는 소켓에 바인딩할 필요가 있다는 의미이다.

function (request, socket, head) { }

Event: 'clientError'#

function (exception) { }

클라이언트 연결에서 'error' 이벤트가 발생하면 이 이벤트가 진행된다.

server.listen(port, [hostname], [backlog], [callback])#

지정한 hostname과 port에서 열결을 받아들이기 시작한다. hostname을 생락하면 서버는 IPv4 주소(INADDR_ANY)에서 들어오는 연결을 모두 받아들일 것이다.

유닉스 소켓에 바인딩하려면 port와 hostname 대신에 파일명을 전달한다.

백로그는 지연된는 연결 큐의 최대길이이다. 실제 길이는 리눅스의 tcp_max_syn_backlogsomaxconn같은 sysctl 설정을 통해 OS가 결정한다. backlog의 기본값은 511이다.(512가 아니다)

이 함수는 비동기 함수다. 마지막 파라미터 callback'listening' 이벤트의 리스터로 추가될 것이다. net.Server.listen(port)도 참고해 봐라.

server.listen(path, [callback])#

전달한 path에서 연결을 받아들이는 UNIX 소켓 서버를 시작한다.

이 함수는 비동기 함수다. 마지막 파라미터 callback'listening' 이벤트의 리스너로 추가될 것이다. net.Server.listen(path)도 참고해라.

server.listen(handle, [callback])#

  • handle Object
  • callback Function

handle 객체는 서버나 소켓(의존하는 _handle 멤버를 가진 어떤 것이든)으로 설정하거나 {fd: <n>} 객체로 설정할 수 있다.

이 함수는 서버가 지정한 핸들에서 연결을 받아들이도록 하지만 파일 디스크립터나 핸들이 이미 포트나 도메인 소켓에 바인딩되어 있다고 가정한다.

윈도우는 파일 디스크립터에서 요청을 받아들이는 것을 지원하지 않는다.

이 함수는 비동기 함수다. 마지막 파라미터 callback'listening' 이벤트의 리스너로 추가될 것이다. net.Server.listen()도 참고해라.

server.close([callback])#

Stops the server from accepting new connections. See net.Server.close(). 서버가 새로운 연결을 받아들이는 것을 멈춘다. net.Server.close()를 참고해라.

server.maxHeadersCount#

들어오는 헤더의 최대 수를 제한하고 기본값은 1000이다. 이 값을 0으로 설정하면 제한을 두지 않는다.

Class: http.ServerRequest#

사용자가 아니라 HTTP 서버 내부적으로 생성되는 객체다. 'request' 리스너의 첫번째 아규먼트로 전달한다.

요청은 Readable Stream를 구현했다. 이 클래스는 다음의 이벤트를 가지는 EventEmitter이다.

Event: 'data'#

function (chunk) { }

메시지 바디의 일부를 받았을 때 발생한다. request.setEncoding()로 인코딩을 설정한 경우 청크는 문자열이고 설정하지 않았으면 Buffer다.

ServerRequest'data' 이벤트를 했을 때 리스터가 없다면 데이터를 읽어버릴 것이다.

Event: 'end'#

function () { }

각 요청마다 정확히 한번씩만 발생한다. 이 이벤트 후에는 해당 요청에서 'data' 이벤트가 더이상 발생하지 않을 것이다.

Event: 'close'#

function () { }

response.end()가 호출되기 전이나 플러시할 수 있을 때 의존하는 연결을 종료했다는 것을 나타낸다.

'end'처럼 이 이벤트는 요청당 딱 한번만 발생하고 그 후에는 더 이상 'data' 이벤트가 발생하지 않을 것이다. 자세한 내용은 [http.ServerResponse][]의 'close' 이벤트를 참고해라.

Note: 'end'후에 'close'가 발생할 수 있지만 그 반대로는 안된다.

request.method#

요청 메서드의 문자열 표현. 읽기 전용이다. 예시: 'GET', 'DELETE'

request.url#

요청 URL 문자열. 이 값은 실제 HTTP 요청에 있는 URL만 담고 있다. 요청이 다음과 같다면,

GET /status?name=ryan HTTP/1.1\r\n
Accept: text/plain\r\n
\r\n

request.url는 다음의 값이 될 것이다.

'/status?name=ryan'

URL을 부분별로 파싱하고 싶다면 require('url').parse(request.url)를 사용할 수 있다. 예제:

node> require('url').parse('/status?name=ryan')
{ href: '/status?name=ryan',
  search: '?name=ryan',
  query: 'name=ryan',
  pathname: '/status' }

쿼리스트링에서 파라미터를 추출하려면 require('querystring').parse 함수를 사용하거나 require('url').parse의 두번째 아규먼트로 true를 전달할 수 있다. 예제:

node> require('url').parse('/status?name=ryan', true)
{ href: '/status?name=ryan',
  search: '?name=ryan',
  query: { name: 'ryan' },
  pathname: '/status' }

request.headers#

헤더의 이름과 값으로 이루어진 읽기전용 맵. 헤더의 이름은 소문자다. 예제:

// 다음과 같이 출력된다:
//
// { 'user-agent': 'curl/7.22.0',
//   host: '127.0.0.1:8000',
//   accept: '*/*' }
console.log(request.headers);

request.trailers#

읽기 전용. (존재하는 경우) HTTP trailers이다. 'end' 이벤트 후에만 존재한다.

request.httpVersion#

HTTP 프로토콜 버전의 문자열 표현이다. 읽기 전용. 예시: '1.1', '1.0' 또한 request.httpVersionMajor는 첫 정수이고 request.httpVersionMinor는 두번째 정수이다.

request.setEncoding([encoding])#

요청 바디의 인코딩을 설정한다. 자세한 내용은 stream.setEncoding()를 참고해라.

request.pause()#

요청이 이벤트를 발생키시는 것을 멈춘다. 흐름을 제어하는 데 유용하다.

request.resume()#

멈췄던 요청을 복구한다.

request.connection#

요청과 연결된 net.Socket 객체다.

HTTPS 지원과 함께 클라이언트의 인증 세부사항을 얻으려면 request.connection.verifyPeer()와 request.connection.getPeerCertificate()를 사용해라.

Class: http.ServerResponse#

사용자가 아니라 HTTP 서버가 내부적으로 생성하는 객체다. 'request' 이벤트의 두번째 파라미터로 전달된다.

응답은 Writable Stream 인터페이스를 구현했다. 이 클래스는 다음의 이벤트를 가지고 있는 EventEmitter이다.

Event: 'close'#

function () { }

response.end()가 호출되기 전이나 플러시할 수 있을 때 의존하는 연결을 종료했다는 것을 나타낸다.

response.writeContinue()#

클라이언트에 요청 바디가 보내질 것이라는 것을 나타내는 HTTP/1.1 100 Continue 메시지를 보낸다. Server'checkContinue' 이벤트를 봐라.

response.writeHead(statusCode, [reasonPhrase], [headers])#

요청에 응답 헤더를 보낸다. 상태코드는 404같은 3자리 수의 HTTP 상태코드이다. 마지막 파리미터인 headers는 응답 헤더다. 선택적으로 두번째 파라미터에 사람이 읽을 수 있는 reasonPhrase를 전달할 수 있다.

예제:

var body = 'hello world';
response.writeHead(200, {
  'Content-Length': body.length,
  'Content-Type': 'text/plain' });

이 메서드는 한 메시지에서 딱 한번만 호출되어야 하고 response.end()가 호출되기 전에 호출되어야 한다.

이 메서드가 호출되기 전에 response.write()response.end()를 호출한다면 암묵적이고 변할 가능성이 있는 헤더가 계산해서 이 함수를 호출할 것이다.

Note: 해당 Content-Length는 문자가 아니라 바이트로 주어진다. 문자열 'hello world'는 단일 바이트의 문자만 가지고 있기 때문에 위의 예제는 동작한다. 바디에 더 높은 코드의 문자가 있다면 주어진 인코딩으로 바이트의 수를 결정하는데 Buffer.byteLength()를 사용할 것이다. 그리고 Node는 전송된 바디의 길이와 Content-Length가 같은지 같지 않은지 확인하지 않는다.

response.statusCode#

(명시적으로 response.writeHead()를 호출하지 않고) 암묵적인 헤더를 사용하는 경우 헤더가 플러시됐을 때 클라이언트에 보낼 상태코드를 이 프로퍼티가 제어한다.

예제:

response.statusCode = 404;

응답해더가 클라이언트에 보내진 후 이 프로퍼티는 보낸 상태코드를 나타낸다.

response.setHeader(name, value)#

암묵적인 헤더에 단일 헤더값을 설정한다. 전송할 헤더에 이미 이 헤더가 존재한다면 해당 값은 덮어써질 것이다. 같은 이름을 가진 여러 헤더를 전송해야 한다면 여기에 문자열 배열을 사용해라.

예제:

response.setHeader("Content-Type", "text/html");

또는

response.setHeader("Set-Cookie", ["type=ninja", "language=javascript"]);

response.sendDate#

이 값이 ture이면 이미 헤더에 Date 헤더가 존재하지 않으며 자동으로 Date 헤더를 생성해서 응답에 보낼 것이다. 기본값은 true이다.

이 값은 테스트할 때만 사용하지 않도록 해야 한다. HTTP는 응답에 Date 헤더를 필요로 한다.

response.getHeader(name)#

이미 큐에 들어갔지만 아직 클라이언트에는 보내지 않은 헤더를 읽는다. 이름이 대소문자를 구분한다는 점에 주의해라. 이 함수는 헤더가 암묵적으로 플러시 되기 전에만 호출할 수 있다.

예제:

var contentType = response.getHeader('content-type');

response.removeHeader(name)#

암묵적으로 보내려고 큐에 있는 헤더를 제거한다.

예제:

var contentType = response.getHeader('content-type');

response.write(chunk, [encoding])#

이 메서드는 호출하고 response.writeHead()는 호출하지 않았다면 암묵적인 헤더 모드로 바꾸고 암묵적인 헤더를 플러시할 것이다.

이 메서드는 응답 바디의 청크를 전송한다. 바디의 연속적인 부분을 제공하기 위해 이 함수를 여러번 호출할 수 있다.

chunk는 문자열이나 버퍼가 될 수 있다. chunk는 문자열이면 두번째 파라미터로 chunk를 어떻게 바이트 스트림으로 인코딩할 것인지 지정한다. encoding의 기본값은 'utf8'이다.

Note: 이 메서드는 로우(raw) HTTP 바디이고 사용될 수도 있는 고수준의 multi-part 바디 인코딩에서는 아무것도 하지 않는다.

처음 response.write()를 호출하면 버퍼되어 있는 헤더 정보와 첫 바디를 클라이언트에 보낼 것이다. 두번째로 response.write()를 호출하면 Node는 데이터를 스트리밍 할 것이라고 가정하고 나눠서 전송한다. 즉, 응답은 바디의 첫번째 청크에 버퍼된다.

전체 데이터가 성공적으로 커널버퍼에 플러시되면 true를 반환한다. 전체 혹은 일부의 데이터가 사용자 메모리에 큐로 들어가면 false를 반환한다. 버퍼가 다시 여유가 생기면 'drain'가 발생할 것이다.

response.addTrailers(headers)#

이 메서드는 HTTP trailing headers(헤더이지만 메시지 끝에 오는 헤더)를 응답에 추가한다.

응답에 chunked 인코딩을 사용한 경우에만 Trailers가 발생할 것이다. chunked 인코딩이 아니라면(요청이 HTTP/1.0인 등등) 이 헤더는 경고없이 버려질 것이다.

헤더의 값에 헤더 필드들의 리스트로 trailers를 발생시키려면 HTTP는 Trailer를 필요로 한다.

response.writeHead(200, { 'Content-Type': 'text/plain',
                          'Trailer': 'Content-MD5' });
response.write(fileData);
response.addTrailers({'Content-MD5': "7895bf4b8828b55ceaf47747b4bca667"});
response.end();

response.end([data], [encoding])#

이 메서드는 모든 응답 헤더와 바디를 보냈다고 서버에 신호를 보낸다. 해당 서버는 이 메시지를 완료된 것으로 간주해야 한다. response.end() 메서드는 반드시 각 응답마다 호출되어야 한다.

data를 지정하면 response.write(data, encoding)를 호출한 다음에 response.end()를 호출한 것과 같다.

http.request(options, callback)#

Node는 HTTP 요청에 대한 연결을 서버당 여러 개 유지하고 있다. 이 함수는 투명하게 요청을 진행한다.

options은 객체나 문자열이 될 수 있다. options이 문자열이면 자동으로 url.parse()를 사용해서 파싱한다.

옵션:

  • host: 요청을 보낼 서버의 도메인 명이나 IP 주소. 기본값은 'localhost'이다.
  • hostname: url.parse()를 지원하려면 host보다 hostname가 낫다.
  • port: 원격서버의 포트. 기본포트는 80포트이다.
  • localAddress: 에트워크 연결에 바인딩할 로컬 인터페이스.
  • socketPath: Unix 도메인 소켓 (host:port난 socketPath 중 하나를 사용한다)
  • method: HTTP 요청 메서드를 지정하는 문자열. 기본값은 'GET'이다.
  • path: 요청 경로. 기본값은 '/'이다. 필요하다면 쿼리스트링도 포함시킨다. 예시. '/index.html?page=12'
  • headers: 요청 헤더를 담고 있는 객체.
  • auth: 기본 인증으롤 예를 들면 인증헤더를 계산하는 'user:password'이다.
  • agent: Agent 동작을 제어한다. 에이전트를 사용했을 때 요청은 기본적으로 Connection: keep-alive가 될 것이다. 가능한 값은 다음과 같다.
    • undefined (default): 이 호스트와 포트에 대한 global Agent를 사용한다.
    • Agent object: 명시적으로 Agent에 전달된 객체를 사용한다.
    • false: Agent와 함께 연결 풀을 사용하지 않는다. 기본값은 Connection: close에 요청한다.

http.request()http.ClientRequest 클래스의 인스턴스를 리턴한다. ClientRequest 인스턴스는 쓰기가 가능한 스트림이다. POST 요청으로 파일을 업로드해야 한다면 ClientRequest 객체에 작성한다.

예제:

var options = {
  hostname: 'www.google.com',
  port: 80,
  path: '/upload',
  method: 'POST'
};

var req = http.request(options, function(res) {
  console.log('STATUS: ' + res.statusCode);
  console.log('HEADERS: ' + JSON.stringify(res.headers));
  res.setEncoding('utf8');
  res.on('data', function (chunk) {
    console.log('BODY: ' + chunk);
  });
});

req.on('error', function(e) {
  console.log('problem with request: ' + e.message);
});

// write data to request body
req.write('data\n');
req.write('data\n');
req.end();

예제에서 req.end()를 호출했다. 요청 바디에 쓰여진 데이터가 없다고 하더라도 요청이 완료되었다는 의미로 http.request()에서 항상 req.end()를 반드시 호출해야 한다.

요청중에 어떤 오류가 있다면(DNS 처리나 TCP 레벨의 오류, 실제 HTTP 파싱 오류 등등) 반환된 요청 객체에서 'error' 이벤트가 발생한다.

알아야 할 몇가지 특별한 헤더가 있다.

  • 'Connection: keep-alive'를 전송하면 서버와의 연결을 다음 요청까지 유지해야 한다는 것을 Node에 알려줄 것이다.

  • 'Content-length' 헤더를 전송하면 기본 chunked 인코딩을 사용하지 않을 것이다.

  • 'Expect'헤더를 전송하면 즉시 요청헤더를 보낼 것이다. 'Expect: 100-continue'를 보낼 때는 보통 타임아웃과 continue 이벤트에 대한 linten을 둘 다 설정해야 한다. 더 자세한 내용은 RFC2616의 섹션 8.2.3을 봐라.

  • 인증헤더를 전송하면 기본 인증을 계산하는 auth 옵션을 덮어쓸 것이다.

http.get(options, callback)#

대부분의 요청은 바디가 없는 GET 요청이기 때문에 Node는 이 편리한 메서드를 제공한다. 이 메서드와 http.request()간의 유일한 차이점은 자동적으로 메서드를 GET으로 설정하고 req.end()를 호출한다는 점이다.

예제:

http.get("http://www.google.com/index.html", function(res) {
  console.log("Got response: " + res.statusCode);
}).on('error', function(e) {
  console.log("Got error: " + e.message);
});

Class: http.Agent#

node 0.5.3이상부터는 HTTP 클라이언트 요청의 풀링 소켓에 사용하는 HTTP 에이전트의 새로운 구현체가 있다.

그 이전에 단일 에이전트 인스턴스는 하나의 host+port에 대한 풀을 도왔다. 지금의 구현체는 많은 수의 호스트에 대한 소켓을 가지고 있다.

현재 HTTP 에이전트도 기본 클라이언트 요청은 Connection:keep-alive 를 사용한다. 소켓에서 대기하고 있는 지연된 HTTP 요청이 없다면 해당 소켓을 닫는다. 즉 node의 풀은 부하가 있을 때 keep-alive 이점을 가지지만 여전히 keep-alive를 사용하는 HTTP 클라이언트를 개발자가 수동으로 닫을 필요가 없다.

소켓이 "close" 이벤트나 특수한 "agentRemove" 이벤트를 발생시켰을 때 이에전트의 풀에서 소켓을 제거한다. 다시 말하면 HTTP 요청을 오랫동안 열어놓고 유지하고 HTTP 요청이 풀에 유지되기를 원하지 않는다면 다음과 같이 할 수 있다.

http.get(options, function(res) {
  // Do stuff
}).on("socket", function (socket) {
  socket.emit("agentRemove");
});

대신 agent:false를 사용해서 완전히 풀링을 사용하지 않을 수도 있다.

http.get({hostname:'localhost', port:80, path:'/', agent:false}, function (res) {
  // Do stuff
})

agent.maxSockets#

기본적으로 5로 설정되어 있다. 에이전트가 얼마나 많은 동시 소켓을 호스트당 열 수 있는지를 결정한다.

agent.sockets#

Agent가 현재 사용하고 있는 소켓의 배열을 담고 있는 객체다. 수정하면 안된다.

agent.requests#

소켓에 아직 할당되지 않은 요청의 큐를 담고 있는 객체다. 수정하면 안된다.

http.globalAgent#

모든 HTTP 클라이언트 요청에 기본적으로 사용되는 Agent의 전역 인스턴스다.

Class: http.ClientRequest#

내부적으로 생성하고 http.request()가 반환하는 객체다. 헤더는 이미 큐에 들어간 처리중인 요청을 나타낸다. setHeader(name, value), getHeader(name), removeHeader(name) API를 사용해서 헤더를 여전히 변경할 수 있다. 실제 헤더는 첫 데이터 청크와 함게 보내거나 연결이 닫힐 때 보낼 것이다.

응답을 받으려면 응답 객체에 'response'에 대한 리스너를 추가해라. 'response'는 응답 헤더를 받았을 때 요청 객체에서 발생할 것이다. 'response' 이벤트는 http.ClientResponse 인스턴스를 아규먼트로 받아서 실행된다.

'response' 이벤트 가운데 응답 객체에 리스너들을 추가할 수 있다. 특히 'data' 이벤트를 받기 위해 추가할 수 있다. 'response' 이벤트는 응답 바디를 받기 전에 실행되므로 바디의 첫 부분을 받기 위해 경쟁하는 것은 걱정할 필요가 없다. 'response' 이벤트 가운데 'data'에 대한 리스너가 추가되면 전체 바디를 받을 것이다.

// 좋은 사례
request.on('response', function (response) {
  response.on('data', function (chunk) {
    console.log('BODY: ' + chunk);
  });
});

// 나쁜 사례 - 바디 전체나 일부를 놓친다
request.on('response', function (response) {
  setTimeout(function () {
    response.on('data', function (chunk) {
      console.log('BODY: ' + chunk);
    });
  }, 10);
});

Note: Node는 Content-Length와 전송된 바디의 길이가 같은지 같지 않은지 확인하지 않는다.

요청은 Writable Stream 인터페이스를 구현했다. 이는 다음 이벤트를 가진 EventEmitter이다.

Event 'response'#

function (response) { }

해당 요청에 대한 응답을 받았을 때 발생한다. 이 이벤트는 딱 한번만 발생한다. response 아규먼트는 http.ClientResponse의 인스턴드가 될 것이다.

옵션:

  • host: 요청을 보낼 서버의 도메인명이나 IP 주소다.
  • port: 원격 서버의 포트.
  • socketPath: Unix 도메인 소켓 (host:port나 socketPath 중 하나를 사용한다)

Event: 'socket'#

function (socket) { }

해당 요청에 소켓이 할당된 후에 발생한다.

Event: 'connect'#

function (response, socket, head) { }

CONNECT 메서드의 요청에 서버가 응답할 때마다 발생한다. 이 이벤트에 등록된 리스너가 없으면 CONNECT 메서드를 받는 클라이언트의 연결을 닫힐 것이다.

클라이언트와 서버가 어떻게 connect 이벤트를 받는지 보여준다.

var http = require('http');
var net = require('net');
var url = require('url');

// HTTP 터널링 프록시를 생성한다
var proxy = http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('okay');
});
proxy.on('connect', function(req, cltSocket, head) {
  // 원래의 서버로 연결한다
  var srvUrl = url.parse('http://' + req.url);
  var srvSocket = net.connect(srvUrl.port, srvUrl.hostname, function() {
    cltSocket.write('HTTP/1.1 200 Connection Established\r\n' +
                    'Proxy-agent: Node-Proxy\r\n' +
                    '\r\n');
    srvSocket.write(head);
    srvSocket.pipe(cltSocket);
    cltSocket.pipe(srvSocket);
  });
});

// 이제 프록시서버가 동작한다
proxy.listen(1337, '127.0.0.1', function() {

  // 터널링 프록시에 요청을 만든다
  var options = {
    port: 1337,
    hostname: '127.0.0.1',
    method: 'CONNECT',
    path: 'www.google.com:80'
  };

  var req = http.request(options);
  req.end();

  req.on('connect', function(res, socket, head) {
    console.log('got connected!');

    // HTTP 터널을 통해 요청을 만든다
    socket.write('GET / HTTP/1.1\r\n' +
                 'Host: www.google.com:80\r\n' +
                 'Connection: close\r\n' +
                 '\r\n');
    socket.on('data', function(chunk) {
      console.log(chunk.toString());
    });
    socket.on('end', function() {
      proxy.close();
    });
  });
});

Event: 'upgrade'#

function (response, socket, head) { }

업그레이드 요청에 서버가 응답할 때마다 발생한다. 이 이벤트가 바인딩되어 있지 않으면 업그레이드 헤더를 받는 클라이언트는 연결이 닫힐 것이다.

upgrade 이벤트를 어떻게 바인딩하는 지 보여주는 클라이언트와 서버 쌍의 예제다:

var http = require('http');

// HTTP 서버 생성
var srv = http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('okay');
});
srv.on('upgrade', function(req, socket, head) {
  socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
               'Upgrade: WebSocket\r\n' +
               'Connection: Upgrade\r\n' +
               '\r\n');

  socket.pipe(socket); // echo back
});

// 이제 서버가 동작한다
srv.listen(1337, '127.0.0.1', function() {

  // 요청 생성
  var options = {
    port: 1337,
    hostname: '127.0.0.1',
    headers: {
      'Connection': 'Upgrade',
      'Upgrade': 'websocket'
    }
  };

  var req = http.request(options);
  req.end();

  req.on('upgrade', function(res, socket, upgradeHead) {
    console.log('got upgraded!');
    socket.end();
    process.exit(0);
  });
});

Event: 'continue'#

function () { }

보통 요청이 'Expect: 100-continue'를 답고 있기 때문에서버가 '100 Continue' HTTP 응답을 보냈을 때 발생한다. 이는 클라이언트가 요청 바디를 보내야 한다는 것을 알려준다.

request.write(chunk, [encoding])#

바디의 청크를 전송한다. 이 메서드를 여러번 호출해서 사용자는 요청 바디를 서버에 스트리밍할 수 있다. - 이 경우 요청을 생성할 때 ['Transfer-Encoding', 'chunked'] 헤더를 사용하기를 제안한다.

chunk 아규먼트는 Buffer나 문자열이 되어야 한다.

encoding 아규먼트는 선택사항이고 chunk가 문자열인 경우에만 적용된다. 기본값은 'utf8'이다.

request.end([data], [encoding])#

요청 전송을 종료한다. 바디의 일부를 보내지 않았다면 스트림으로 플러시할 것이다. 요청이 청크라면 종료하는 '0\r\n\r\n'를 보낼 것이다.

data를 지정하면 request.end() 다음에 request.write(data, encoding)를 호출한 것과 같다.

request.abort()#

요청을 중단한다. (v0.3.8부터 추가되었다.)

request.setTimeout(timeout, [callback])#

해당 요청에 소켓이 바인딩되고 소켓이 연결되면 socket.setTimeout()이 호출될 것이다.

request.setNoDelay([noDelay])#

해당 요청에 소켓이 바인딩되고 소켓이 연결되면 socket.setNoDelay()이 호출될 것이다.

request.setSocketKeepAlive([enable], [initialDelay])#

해당 요청에 소켓이 바인딩되고 소켓이 연결되면 socket.setKeepAlive()이 호출될 것이다.

http.ClientResponse#

http.request()로 요청했을 때 생성되는 객체다. 요청 객체의 'response' 이벤트에 전달된다.

응답은 Readable Stream 인터페이스를 구현한다. 이 객체는 다음 이벤트를 가지는 EventEmitter이다.

Event: 'data'#

function (chunk) { }

메시지 바디의 일부를 받았을 때 발생한다.

ClientResponse'data' 이벤트를 발생시켰을 때 리스너가 없으면 데이터를 잃을 수 있다는 것을 명심해라.

Event: 'end'#

function () { }

각 메시지마다 정확히 한번만 발생한다. 아규먼트는 없다. 이 이벤트가 발생한 후에는 어떤 메시지도 응답에 발생시키지 않을 것이다.

Event: 'close'#

function (err) { }

end 이벤트가 발생하기 전에 의존하는 연결이 종료되었다는 것을 나타낸다.

더 자세한 내용은 http.ServerRequest'close' 이벤트를 봐라.

response.statusCode#

3자리 수의 HTTP 응답 상태코드다. 예시: 404.

response.httpVersion#

서버에 연결된 HTTP 버전이다. 아마 '1.1''1.0'일 것이다. response.httpVersionMajor는 첫 숫자이고 response.httpVersionMinor는 두번째 숫자이다.

response.headers#

응답 헤더 객체다.

response.trailers#

응답의 trailers 객체다. 'end' 이벤트 후에만 존재한다.

response.setEncoding([encoding])#

응답 바디의 인코딩을 설정한다. 더 자세한 내용은 stream.setEncoding()를 봐라.

response.pause()#

발생하는 이벤트에서 응답을 멈춘다. 다운로드 트래픽을 조절하는데 유용하다.

response.resume()#

멈춘 응답을 복구한다.

HTTPS#

Stability: 3 - Stable

HTTPS는 TLS/SSL를 사용하는 HTTP 프로토콜이다. Node에서 HTTPS는 별도의 모듈로 구현되었다.

Class: https.Server#

이 클래스는 tls.Server의 하위 클래스로 http.Server와 같은 이벤트를 발생시킨다. 자세한 내용은 http.Server를 참고해라.

https.createServer(options, [requestListener])#

새로운 HTTPS 웹서버 객체를 반환한다. optionstls.createServer()와 유사하다. requestListener'request' 이벤트에 자동으로 추가되는 함수이다.

예제:

// curl -k https://localhost:8000/
var https = require('https');
var fs = require('fs');

var options = {
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(8000);

또는

var https = require('https');
var fs = require('fs');

var options = {
  pfx: fs.readFileSync('server.pfx')
};

https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(8000);

server.listen(port, [host], [backlog], [callback])#

server.listen(path, [callback])#

server.listen(handle, [callback])#

자세한 내용은 http.listen()를 참고해라.

server.close([callback])#

자세한 내용은 http.close()를 참고해라.

https.request(options, callback)#

안전한 웹서버로의 요청을 생성한다.

options은 객체거나 문자열이다. options이 문자열인 경우 자동적으로 url.parse()로 파싱한다.

http.request()의 모든 옵션이 유효하다.

예제:

var https = require('https');

var options = {
  hostname: 'encrypted.google.com',
  port: 443,
  path: '/',
  method: 'GET'
};

var req = https.request(options, function(res) {
  console.log("statusCode: ", res.statusCode);
  console.log("headers: ", res.headers);

  res.on('data', function(d) {
    process.stdout.write(d);
  });
});
req.end();

req.on('error', function(e) {
  console.error(e);
});

options 아규먼트는 다음과 같다.

  • host: 요청을 보낼 서버의 도메인명이나 IP 주소다. 기본값은 'localhost'이다.
  • hostname: url.parse()를 지원하기 위해 host보다 hostname를 선호한다.
  • port: 원격서버의 포트. 기본값은 443이다.
  • method: HTTP 요청 메서드를 지정하는 문자열이다. 기본값은 'GET'이다.
  • path: 요청 경로. 기본값은 '/'이다. 필요하다면 쿼리스트링도 포함해야 한다. 예시. '/index.html?page=12'
  • headers: 요청 헤더를 담고 있는 객체다.
  • auth: 기본 인증. 예글 들면 인증헤더를 계산하는 'user:password'
  • agent: Agent 동작을 제어한다. Agent를 사용했을 때 요청은 기본적으로 Connection: keep-alive가 될 것이다. 다음의 값들이 가능하다.
    • undefined (기본값): 해당 호스트와 포트에 globalAgent를 사용한다.
    • Agent 객체: Agent에 명시적으로 전달된 객체를 사용한다.
    • false: Agent를 연결 풀링에 참가시키지 않는다. 기본적으로 요청은 Connection: close가 된다.

tls.connect()의 다음 옵션들도 지정할 수 있다. 하지만 globalAgent는 경고없이 이러한 값들을 무시한다.

  • pfx: SSL에 사용할 인증서, 개인키, CA 인증서. 기본값은 null이다.
  • key: SSL에 사용할 개인키. 기본값은 null이다.
  • passphrase: 개인키나 pfx에 대한 암호문 문자열. 기본값은 null이다.
  • cert: 사용할 공개 x509 인증서. 기본값은 null이다.
  • ca: 원격 호스트에 대해 확인할 권한 인증이나 권한 인증의 배열이다.
  • ciphers: 사용하거나 배제할 암호문을 나타내는 문자열. 자세한 형식은 http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT 를 참고해라.
  • rejectUnauthorized: 이 값이 true이면 서버 인증서를 제공된 CA 리스트로 검증한다. 검증이 실패했을 때 'error' 이벤트가 발생한다. 검증은 HTTP 요청을 보내기 연결단계에서 이뤄진다. 기본값은 false이다.

이러한 옵션들을 지정하려면 커스텀 Agent를 사용해라.

예제:

var options = {
  hostname: 'encrypted.google.com',
  port: 443,
  path: '/',
  method: 'GET',
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
options.agent = new https.Agent(options);

var req = https.request(options, function(res) {
  ...
}

또는 Agent를 사용하지 마라.

예제:

var options = {
  hostname: 'encrypted.google.com',
  port: 443,
  path: '/',
  method: 'GET',
  key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
  cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'),
  agent: false
};

var req = https.request(options, function(res) {
  ...
}

https.get(options, callback)#

http.get()와 같지만 HTTPS다.

options은 객체거나 문자열이다. options이 문자열인 경우 자동적으로 url.parse()로 파싱한다.

예제:

var https = require('https');

https.get('https://encrypted.google.com/', function(res) {
  console.log("statusCode: ", res.statusCode);
  console.log("headers: ", res.headers);

  res.on('data', function(d) {
    process.stdout.write(d);
  });

}).on('error', function(e) {
  console.error(e);
});

Class: https.Agent#

http.Agent와 유사한 HTTPS의 Agent 객체. 자세한 내용은 [https.request()][]를 참고해라.

https.globalAgent#

모든 HTTPS 클라이언트 요청에 대한 https.Agent의 전역 인스턴스다.

URL#

Stability: 3 - Stable

이 모듈은 URL 처리와 파싱에 관한 유틸리티 모듈이다. 사용하려면 require('url')를 호출해라.

파싱된 URL 객체들은 다음의 필드들을 가지고 있다. 필드들은 URL 문자열에 존재여부에 따라 있을 수도 있고 없을 수도 있다. URL 문자열에 없는 부분은 파싱된 객체에도 없다. 다음은 URL에 대한 예제를 보여준다.

'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'

  • href: 파싱되기 전 원래의 전체 URL. 프로톨콜과 호스트가 모두 소문자이다.

    예제: 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'

  • protocol: 요청 프로토콜로 소문자다.

    예제: 'http:'

  • host: 포트정보를 포함해서 소문자로된 호스트 부분.

    예제: 'host.com:8080'

  • auth: URL의 인증정보 부분.

    예제: 'user:pass'

  • hostname: 호스트에서 소문자로 된 호스트명 부분.

    예제: 'host.com'

  • port: 호스트의 포트번호 부분.

    예제: '8080'

  • pathname: 존재한다면 첫 슬래시를 포함해서 호스트 이후부터 쿼리 이전까지의 URL의 경로부분.

    예제: '/p/a/t/h'

  • search: URL에서 물음표로 시작되는 'query string' 부분.

    예제: '?query=string'

  • path: pathnamesearch의 연결.

    예제: '/p/a/t/h?query=string'

  • query: 쿼리스트링의 'params' 부분이거나 쿼리스트링이 파싱된 객체다.

    예제: 'query=string' or {'query':'string'}

  • hash: 해시기호를 포함해서 URL의 'fragment' 부분.

    예제: '#hash'

URL 모듈에서 다음 메서드들을 제공한다.

url.parse(urlStr, [parseQueryString], [slashesDenoteHost])#

URL 문자열을 받아서 객체를 반환한다.

querystring 모듈을 사용해서 쿼리스트링을 파싱하려면 두번째 아규먼트로 true를 전달한다. 기본값은 false이다.

{ pathname: '//foo/bar' }보다는 { host: 'foo', pathname: '/bar' }와 같이 //foo/bar를 다루려면 세번째 아규먼트로 true를 전달한다. 기본값은 false이다.

url.format(urlObj)#

파싱된 URL 객체를 받아서 포매팅된 URL 문자열을 반환한다.

  • href는 무시된다.
  • protocol은 뒷 부분의 : (콜론)을 포함하거나 포함하지 않고 다룬다.
    • 프로토콜 http, https, ftp, gopher, file:// (콜론-슬래시-슬래시)로 접미사가 붙는다.
    • 모든 다른 프로토콜 mailto, xmpp, aim, sftp, foo 등은 : (콜론)로 접미사가 붙는다.
  • 존재하는 경우auth를 사용한다.
  • host가 없으면 hostname만 사용한다.
  • host가 없으면 port를 사용한다.
  • hosthostname, port 대신에 사용한다.
  • pathname/ (슬래시)로 시작되는 부분을 포함하거나 포함하지 않고 다룬다.
  • searchquery 대신에 사용한다.
  • search가 없으면 query (객체; querystring를 봐라)만 사용할 것이다.
  • search? (물음표)로 시작되는 부분을 포함하거나 포함하지 않고 다룬다.
  • hash# (해시기호, 앵커)로 시작하는 부분을 포함하거나 포함하지 않고 다룬다.

url.resolve(from, to)#

기준 URL과 href URL을 받아서 앵커테그를 위해서 브라우저처럼 처리한다.

Query String#

Stability: 3 - Stable

이 모듈은 쿼리스트링을 다루는 유틸리티 모듈이다. 다음의 메서드들을 제공한다.

querystring.stringify(obj, [sep], [eq])#

객체를 쿼리스트링으로 직렬화한다. 선택적으로 기본 구분기호('&')와 할당기호('=')를 오버라이드 할 수 있다.

예제:

querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' })
// returns
'foo=bar&baz=qux&baz=quux&corge='

querystring.stringify({foo: 'bar', baz: 'qux'}, ';', ':')
// returns
'foo:bar;baz:qux'

querystring.parse(str, [sep], [eq], [options])#

쿼리스트링을 객체로 역직렬화한다. 선택적으로 기본 구분기호('&')와 할당기호('=')를 오버라이드 할 수 있다.

options 객체는 maxKeys 프로퍼티를 가지고 있고(기본값은 1000이다) 이 프로퍼티는 처리되는 키를 제한하는데 사용한다. 키의 갯수 제한을 없애려면 0으로 설정한다.

예제:

querystring.parse('foo=bar&baz=qux&baz=quux&corge')
// returns
{ foo: 'bar', baz: ['qux', 'quux'], corge: '' }

querystring.escape#

querystring.stringify에서 사용하는 이스케이스 함수로 필요하다면 오버라이드 할 수 있다.

querystring.unescape#

querystring.parse에서 사용하는 역이스케이스(unescape) 함수로 필요하다면 오버라이드 할 수 있다.

punycode#

Stability: 2 - Unstable

Punycode.js는 Node.js v0.6.2+부터 포함되었고 require('punycode')로 접근한다.(다른 Node.js 버전에서 이 모듈을 사용하려면 npm으로 punycode모듈을 설치해야 한다.)

punycode.decode(string)#

ASCII 코드의 퓨니코드(Punycode) 문자열을 유니코드 문자열로 변환한다.

// 도메인명 부분을 디코드한다
punycode.decode('maana-pta'); // 'mañana'
punycode.decode('--dqo34k'); // '☃-⌘'

punycode.encode(string)#

유니코드 문자열을 ASCII 코드의 퓨니코드(Punycode) 문자열로 변환한다.

// 도메인명 부분을 인코딩한다
punycode.encode('mañana'); // 'maana-pta'
punycode.encode('☃-⌘'); // '--dqo34k'

punycode.toUnicode(domain)#

도메인명을 나타내는 퓨니코드 문자열을 유니코드로 변환한다. 도메인명에서 퓨니코드부분만 변환된다. 예를 들어 이미 유니코드로 변환된 문자열에서 이 함수를 호출해도 아무 문제가 없다.

// 도메인명을 디코드한다
punycode.toUnicode('xn--maana-pta.com'); // 'mañana.com'
punycode.toUnicode('xn----dqo34k.com'); // '☃-⌘.com'

punycode.toASCII(domain)#

도메인명을 나타내는 유니코드 문자열을 퓨니코드로 변환한다. 도메인명에서 ASCII가 아닌 부분만 변환한다. 이미 ASCII인 도메인에서 호출해도 괜찮다.

// 도메인명을 인코딩한다
punycode.toASCII('mañana.com'); // 'xn--maana-pta.com'
punycode.toASCII('☃-⌘.com'); // 'xn----dqo34k.com'

punycode.ucs2#

punycode.ucs2.decode(string)#

문자열의 각 유니코드 문자에 대한 십진수 코드를 담고 있는 배열을 생성한다. JavaScript 가 내부적으로 UCS-2를 사용하기때문에 이 함수는 서로게이트 반쪽(surrogate halves)의 쌍을(UCS-2의 각각은 분리된 코드로 나타난다) UTF-16에 맞는 단일 코드로 변환한다.

punycode.ucs2.decode('abc'); // [97, 98, 99]
// surrogate pair for U+1D306 tetragram for centre:
punycode.ucs2.decode('\uD834\uDF06'); // [0x1D306]

punycode.ucs2.encode(codePoints)#

십진수코드 배열에 기반한 문자열을 생성한다.

punycode.ucs2.encode([97, 98, 99]); // 'abc'
punycode.ucs2.encode([0x1D306]); // '\uD834\uDF06'

punycode.version#

현재 Punycode.js 버전번호를 나타내는 문자열.

Readline#

Stability: 2 - Unstable

이 모듈을 사용하려면 require('readline')를 실행해라. Readline은 줄마다 스트림(process.stdin같은)으로 읽을 수 있다.

이 모듈을 한번 호출하고 나면 인터페이스를 종료할 때까지 node 프로그램은 종료되지 않을 것이다. 어떻게 프로그램을 안전하게(gracefully) 종료할 수 있는 지 보여주는 예제가 있다.

var readline = require('readline');

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question("What do you think of node.js? ", function(answer) {
  // TODO: 데이터베이스에 답변을 로깅한다
  console.log("Thank you for your valuable feedback:", answer);

  rl.close();
});

readline.createInterface(options)#

readline의 Interface 인스턴스를 생성한다. 다음 값의 "options" 객체를 받는다.

  • input - 리스닝할 읽을 수 있는 스트림 (필수).

  • output - readline 데이터를 작성할 쓰기가 가능한 스트림 (필수).

  • completer - 탭 자동완성에 사용한 선택적인 함수. 아래 이 값을 사용하는 예제를 봐라.

  • terminal - input스트림과 output스트림을 TTY처럼 다뤄야 하고 작성된 코드가 ANSI/VT100로 이스케이프되었다면 true를 전달한다. 기본값은 인스턴스의 output스트림에서 isTTY를 확인하는 것이다.

completer 함수는 사용자가 입력하는 현재 라인을 받고 2가지 배열을 리턴한다.

  1. 자동완성을 위해 일치하는 값들의 배열

  2. 매칭에 사용되는 부분문자열.

그래서 다음과 같은 결과가 나올 것이다. [[substr1, substr2, ...], originalsubstring]

예제:

function completer(line) {
  var completions = '.help .error .exit .quit .q'.split(' ')
  var hits = completions.filter(function(c) { return c.indexOf(line) == 0 })
  // 아무것도 찾지 못하면 모든 자동완성을 보여준다
  return [hits.length ? hits : completions, line]
}

두 개의 아규먼트를 받으면 completer도 비동기로 실행할 수 있다.

function completer(linePartial, callback) {
  callback(null, [['123'], linePartial]);
}

createInterface는 보통 사용자 입력을 받기 위해 process.stdinprocess.stdout와 함께 사용한다.

var readline = require('readline');
var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

readline 인스턴스를 가지면 보통 "line" 이벤트를 리스닝한다.

해당 인스턴스의 terminal값이 true이면 output 스트림은 output.columns 프로퍼티를 정의하는 최적의 호환성을 가질 것이고 output은 컬럼이 변경될 때마다 "resize" 이벤트를 발생시킨다. (output이 TTY이면 process.stdout는 자동으로 이렇게 한다.)

Class: Interface#

클래스는 입력스트림과 출력 스트림을 가진 readline 인터페이스를 나타낸다.

rl.setPrompt(prompt, length)#

프롬프트를 설정한다. 예를 들어 커맨드라인에서 node를 실행하면 node의 프롬프트인 > 를 볼 수 있다.

rl.prompt([preserveCursor])#

사용자가 작성할 새로운 지점인 새로운 줄에서 현재의 setPrompt 옵션을 두어 사용자에게 입력을 받기 위해서 readline을 준비한다. 커서 위치를 0으로 초기화하는 것을 막으려면 preserveCursortrue으로 설정해라.

createInterface과 함께 사용한 input 스트림이 멈춰있다면 이 함수는 다시 시작할 것이다.

rl.question(query, callback)#

query앞에 프로프트를 붙히고 사용자의 응답으로 callback을 호출한다. 사용자에게 쿼리를 보여주고 사용자가 입력한 응답으로 callback을 호출한다.

createInterface과 함께 사용한 input 스트림이 멈춰있다면 이 함수는 다시 시작할 것이다.

예제:

interface.question('What is your favorite food?', function(answer) {
  console.log('Oh, so your favorite food is ' + answer);
});

rl.pause()#

나중에 필요할 때 다시 시작할 수 있도록 readline input 스트림을 멈춘다.

rl.resume()#

readline input 스트림을 다시 시작한다.

rl.close()#

inputoutput 스트림의 제어권을 포기하고 Interface 인스턴스를 닫는다. "close" 이벤트도 발생할 것이다.

rl.write(data, [key])#

output 스트림에 data를 작성한다. key는 키의 순서를 나타내는 객체 리터럴로 터미널이 TTY일 때 사용할 수 있다.

input 스트림이 멈춰있다면 이 함수는 다시 시작할 것이다.

예제:

rl.write('Delete me!');
// 이전에 작성한 라인을 삭제하는 ctrl+u를 시뮬레이트한다
rl.write(null, {ctrl: true, name: 'u'});

Events#

Event: 'line'#

function (line) {}

in 스트림이 \n를 받을 때마다 발생한다. 보통 사용자가 엔터를 쳤을 때 받게 된다. 이 이벤트는 사용자 입력을 받기 위한 좋은 후크(hook)이다.

line 이벤트를 받는 예제다.

rl.on('line', function (cmd) {
  console.log('You just typed: '+cmd);
});

Event: 'pause'#

function () {}

input 스트림이 멈출 때마다 발생한다.

input 스트림이 멈춰있지 않고 SIGCONT 이벤트를 받을 때도 발생한다. (SIGTSTP이벤트와 SIGCONT이벤트를 참고해라.)

pause 이벤트를 리스닝하는 예제:

rl.on('pause', function() {
  console.log('Readline paused.');
});

Event: 'resume'#

function () {}

input스트림이 재시작 할때마다 발생한다.

resume 이벤트를 리스닝하는 예제:

rl.on('resume', function() {
  console.log('Readline resumed.');
});

Event: 'close'#

function () {}

close()를 호출할 때 발생한다.

input 스트림이 "end" 이벤트를 받을 때도 발생한다. Interface 인터페이스는 "finished" 이벤트가 발생했다고 간주한다. 예를 들어 input 스트림이 EOT^D를 받은 경우이다.

input 스트림이 SIGINT의 의미인 ^C를 받았을 때 SIGINT 이벤트에 등록된 리스너가 없으면 이 이벤트가 호출된다.

Event: 'SIGINT'#

function () {}

input 스트림이 SIGINT의 의미인 ^C를 받을 때마다 발생한다. input 스트림이 SIGINT를 받을 때 SIGINT 이벤트에 등록된 리스너가 없으면 pause 이벤트가 발생한다.

SIGINT를 리스닝하는 예제:

rl.on('SIGINT', function() {
  rl.question('Are you sure you want to exit?', function(answer) {
    if (answer.match(/^y(es)?$/i)) rl.pause();
  });
});

Event: 'SIGTSTP'#

function () {}

이 이벤트는 Windows에서는 동작하지 않는다.

input 스트림이 SIGTSTP로 알려진 ^Z를 받을 때마다 발생한다. input 스트림이 SIGTSTP을 받았을 때 SIGTSTP이벤트에 등록된 리스너가 없으면 프로그램은 백그라운드로 보내진다.

프로그램이 fg로 복귀했을 때 pauseSIGCONT 이벤트가 실행될 것이다. 스트림으로 복귀하는데 사용할 수도 있다.

프로그램이 백그라운드로 보내지기 전에 스트림이 멈춰있다면 pauseSIGCONT 이벤트는 발생하지 않을 것이다.

SIGTSTP 이벤트를 리스닝하는 예제:

rl.on('SIGTSTP', function() {
  // 이는 SIGTSTP를 오버라이드 하고 프로그램이 백그라운드로 가는 것을 막는다.
  console.log('Caught SIGTSTP.');
});

Event: 'SIGCONT'#

function () {}

이 이벤트는 Windows에서는 동작하지 않는다.

input 스트림이 SIGTSTP로 알려진 ^Z를 통해 백그라운드로 보내질 때마다 발생하고 이는 fg(1)로 다시 복귀할 수 있다. 이 이벤트는 프로그램을 백그라운드로 보내기 전에 스트림이 멈춰있지 않은 경우에만 발생한다.

SIGCONT이벤트를 리스닝하는 예제:

rl.on('SIGCONT', function() {
  // `prompt`는 자동으로 스트림을 복구시킬 것이다
  rl.prompt();
});

Example: Tiny CLI#

이 모두를 사용해서 어떻게 작은 명령행 인터페이스를 만드는지 보여주는 예제가 있다.

var readline = require('readline'),
    rl = readline.createInterface(process.stdin, process.stdout);

rl.setPrompt('OHAI> ');
rl.prompt();

rl.on('line', function(line) {
  switch(line.trim()) {
    case 'hello':
      console.log('world!');
      break;
    default:
      console.log('Say what? I might have heard `' + line.trim() + '`');
      break;
  }
  rl.prompt();
}).on('close', function() {
  console.log('Have a great day!');
  process.exit(0);
});

REPL#

Read-Eval-Print-Loop (REPL)는 단독 프로그램과 다른 프로그램에 쉽게 포함해서 사용할 수 있다. REPL은 자바스크립트를 실행하고 결과를 보는 대화식 방법을 제공한다. 이는 디버깅, 테스팅이나 그냥 간단한 것을 시도해 볼 때 사용할 수 있다.

명령행에서 아규먼트없이 node를 실행하면 REPL에 들어간다. REPL은 극도로 단순한 emacs 라인수정을 가진다.

mjr:~$ node
Type '.help' for options.
> a = [ 1, 2, 3];
[ 1, 2, 3 ]
> a.forEach(function (v) {
...   console.log(v);
...   });
1
2
3

향상된 라인에디터를 위해서는 환경변수 NODE_NO_READLINE=1로 node를 시작해라. 이는 rlwrap를 사용할 수 있도록 인정된 터미널 설정에서 메인 REPL과 디버거 REPL을 시작한다.

예를 들어 bashrc 파일에 다음을 추가할 수 있다.

alias node="env NODE_NO_READLINE=1 rlwrap node"

repl.start(options)#

REPLServer 인스턴스를 시작하고 반환한다. 다음 값의 "options" 객체를 받는다.

  • prompt - 모든 I/O에 대한 프롬프트와 stream. 기본값은 > 이다.

  • input - 리스닝할 읽을 수 있는 스트림. 기본값은 process.stdin이다.

  • output - readline 데이터를 작성할 쓰기가능한 스트림. 기본값은 process.stdout이다.

  • terminal - stream을 TTY처럼 다뤄야 하고 작성된 데이터가 ANSI/VT100로 이스케이프되어 있다면 true를 전달해라. 기본값은 인스턴스의 output 스트림에서 isTTY를 확인하는 것이다.

  • eval - 각 주어진 라인을 평가(eval)하는데 사용할 함수. 기본값은 eval()에 대한 비동기 래퍼이다. 아래의 커스텀 eval의 예제를 참조해라.

  • useColors - writer 함수가 색상을 출력해야 하는지 아닌지를 지정하는 불리언값. 다른 writer 함수를 설정하면 이 값은 아무것도 하지 않는다. 기본값은 repl의 terminal값이다.

  • useGlobal - true로 설정하면 repl이 분리된 컨텍스트에서 스크립트를 실행하는 대신에 global 객체를 사용한다. 기본값은 false이다.

  • ignoreUndefined - true로 설정하면 repl은 명령의 결과값이 undefined인 경우 출력하지 않는다. 기본값은 false이다.

  • writer - 화면에 표시하기 위해서 포매팅(컬러링 포함)된 값을 평가하려고 명령마다 호출할 함수이다. 기본값은 util.inspect이다.

다음과 같은 시그니처를 가진 자신만의 eval함수를 사용할 수 있다.

function eval(cmd, context, filename, callback) {
  callback(null, result);
}

다중 REPL은 node에서 실행되는 같은 인스턴스에서 시작될 것이다. 각 REPL은 같은 전역객체를 공유하지만 각각 유일한 I/O를 가질 것인다.

stdin, Unix 소켓, TCP 소켓에서 REPL을 시작하는 예제는 다음과 같다.

var net = require("net"),
    repl = require("repl");

connections = 0;

repl.start({
  prompt: "node via stdin> ",
  input: process.stdin,
  output: process.stdout
});

net.createServer(function (socket) {
  connections += 1;
  repl.start({
    prompt: "node via Unix socket> ",
    input: socket,
    output: socket
  }).on('exit', function() {
    socket.end();
  })
}).listen("/tmp/node-repl-sock");

net.createServer(function (socket) {
  connections += 1;
  repl.start({
    prompt: "node via TCP socket> ",
    input: socket,
    output: socket
  }).on('exit', function() {
    socket.end();
  });
}).listen(5001);

명령행에서 이 프로그램을 실행하면 stdin에서 REPL을 시작할 것이다. 다른 REPL 클라이언트는 Unix 소켓이나 TCP 소켓을 통해서 연결할 것이다. telnet은 TCP 소켓에 연결하는 데 유용하고 socat은 Unix와 TCP 소켓에 연결하는 데 사용할 수 있다.

stdin 대신 Unix 소켓에 기반한 서버에서 REPL을 시작하면 재시작 없이 오랫동안 실행되는 node 프로세스에 연결할 수 있다.

net.Servernet.Socket 인스턴스에서 실행되는 "완전한 기능의" (terminal) REPL의 예제는 https://gist.github.com/2209310 를 참고해라.

curl(1)을 실행하는 REPL 인스턴스의 예제는 https://gist.github.com/2053342 를 참고해라.

Event: 'exit'#

function () {}

사용자가 어떤 방법으로든 REPL을 종료했을 때 발생한다. 즉 repl에서 .exit를 입력하거나 SIGINT 신호를 위해 Ctrl+C를 두번 입력하거나 input 스트림에서 "end" 신호를 위해 Ctrl+D를 입력하는 등이다.

exit 이벤트를 리스닝하는 예제:

r.on('exit', function () {
  console.log('Got "exit" event from repl!');
  process.exit();
});

REPL Features#

REPL내에서 Control+D를 누르면 종료될 것이다. 다중라인 포현식은 입력이 될 수 있다. 전역 변수와 지역 변수에 모두 탭 자동완성을 지원한다.

특수한 변수 _ (언더스코어)는 마지막 표현식의 결과를 담고 있다.

> [ "a", "b", "c" ]
[ 'a', 'b', 'c' ]
> _.length
3
> _ += 1
4

REPL은 전역범위의 어떤 변수라도 접근할 수 있다. 각 REPLServer과 연결된 context 객체에 할당해서 명시적으로 REPL에 변수를 노출할 수 있다. 예를 들어

// repl_test.js
var repl = require("repl"),
    msg = "message";

repl.start().context.m = msg;

REPL내 context 객체에서 지역변수로 나타나는 변수들이 있다.

mjr:~$ node repl_test.js
> m
'message'

몇몇 REPL 명령어가 있다.

  • .break - 다중 라인 표현식을 입력하는 동안 종종 멈추거나 표현식을 완성하기를 신경쓰지 않을 때 사용한다. .break은 다시 시작할 것이다.
  • .clear - context 객체를 비어있는 객체로 리셋하고 모든 다중라인 표현식을 정리한다.
  • .exit - REPL이 종료되도록 I/O 스트림을 닫는다.
  • .help - 이 특수한 명령어 리스트를 보여준다.
  • .save - 현재 REPL 세션을 파일로 저장한다.

    .save ./file/to/save.js

  • .load - 파일에서 현재 REPL 세션으로 로드한다.

    .load ./file/to/load.js

REPL에서 다음의 키 조합은 다음과 같은 특수한 효가가 있다.

  • <ctrl>C - .break 키워드와 유사하다. 현재 명령어를 종료한다. 비어있는 라인에서 두 번 입력하면 강제적으로 종료한다.
  • <ctrl>D - .exit 키워드와 유사하다.

Executing JavaScript#

Stability: 2 - Unstable. 아래의 주의사항(Caveats) 참고

이 모듈은 다음과 같이 접근할 수 있다.

var vm = require('vm');

자바스크립트 코드는 컴파일하고 바로 실행하거나 컴파일하고 저장한 후에 나중에 실행할 수 있다.

Caveats#

vm 모듈은 많은 이슈와 엣지 케이스(edge cases)가 있다. 어떤 이슈나 기대하지 않은 결과를 만난다면 the open issues on GitHub 를 참고해라. 가장 큰 문제들은 아래에 설명했다.

Sandboxes#

vm.runInNewContextvm.createContextsandbox 아규먼트와 vm.createContextinitSandbox 아규먼트는 Node의 다양한 버전에서 보통 한가지 동작으로 기대할 수 없고 다양하게 동작한다.

알려진 핵심이슈는 컨텍스트내에서 사용한 전역객체를 직접 제어할 방법을 V8이 제공하지 않는다는 것이다. 그 결과 sandbox 객체의 프로퍼티를 컨텍스트에서 이용할 수 있더라도 sandboxprototype에서 어떤 프로퍼티도 이용할 수 없다. 게다가 컨텍스트의 전역범위내에서 this 표현식은 sandbox 대신 빈 객체({})가 된다.

실행할 때 컨텍스트로 복사되고 실행후에 변경사항을 전파하기 위해 다시 복사된다.

Globals#

ArrayString같은 전역객체의 프로퍼티들은 컨텍스트내에서 다른 값들을 가진다. 이는 vm 모듈을 통해서 실행한 스트립트내에서는 [] instanceof ArrayObject.getPrototypeOf([]) === Array.prototype같은 일반적인 표현식이 기대한 결과가 되지 않는다는 의미이다.

이러한 문제들 중 일부는 Github의 vm에 대한 이슈에 우회방법이 리스트되어 있다. 예를 들어 Array.isArrayArray 문제로 우회할 수 있다.

vm.runInThisContext(code, [filename])#

vm.runInThisContext()code를 컴파일하고 실행해서 결과를 반환한다. 실행되는 코드는 지역범위에 접근하지 않는다. filename은 선택사항이며 스택트레이스에서만 사용된다.

같은 코드를 실행하는데 vm.runInThisContexteval을 사용한 예제

var localVar = 123,
    usingscript, evaled,
    vm = require('vm');

usingscript = vm.runInThisContext('localVar = 1;',
  'myfile.vm');
console.log('localVar: ' + localVar + ', usingscript: ' +
  usingscript);
evaled = eval('localVar = 1;');
console.log('localVar: ' + localVar + ', evaled: ' +
  evaled);

// localVar: 123, usingscript: 1
// localVar: 1, evaled: 1

vm.runInThisContext가 지역범위에 접근하지 않기 때문에 localVar는 변경되지 않았다. eval은 지역범위에 접근하기 때문에 localVar가 변경되었다.

code에 문법오류가 있는 경우 vm.runInThisContext는 stderr에 문법오류를 발생시키고 예외를 던진다.

vm.runInNewContext(code, [sandbox], [filename])#

vm.runInNewContextcode를 컴파일하고 sandbox에서 실행한 뒤 결과를 반환한다. 실행되는 코드는 지역범위에 접근하지 않는다. sandbox 객체는 code의 전역객체로 사용될 것이다. sandboxfilename는 선택사항이고 filename는 스택트레이스에서만 사용된다.

예제: 전역변수를 증가시키고 새로운 전역변수를 설정하는 코드를 컴파일하고 실행한다. 이 전역변수들은 sandbox안에 포함되어 있다.

var util = require('util'),
    vm = require('vm'),
    sandbox = {
      animal: 'cat',
      count: 2
    };

vm.runInNewContext('count += 1; name = "kitty"', sandbox, 'myfile.vm');
console.log(util.inspect(sandbox));

// { animal: 'cat', count: 3, name: 'kitty' }

신뢰할 수 없는 코드를 실행하는 것은 아주 신중을 요하는 복잡한 작업이다. 의도치않은 전역변수의 누수를 막는데 vm.runInNewContext가 아주 유용하지만 신뢰하지 못하는 코드를 안전하게 실행하려면 프로세스를 분리할 필요가 있다.

code에서 문법오류가 있는 경우 vm.runInNewContext는 stderr에 문법오류를 발생시키고 예외를 던진다.

vm.runInContext(code, context, [filename])#

vm.runInContextcode를 컴파일하고 context에서 실행한 뒤 결과를 반환한다. (V8) 컨텍스트는 내장된 객체 및 기능들과 함께 하나의 전역객체로 이루어져 있다. 실행되는 코드는 지역범위에 접근하지 않고 전역객체는 code의 전역객체로 사용될 context내에 있다. filename는 선택사항이고 스택트레이스에서만 사용된다.

예제: 존재하는 컨텍스트에서 코드를 컴파일하고 실행한다.

var util = require('util'),
    vm = require('vm'),
    initSandbox = {
      animal: 'cat',
      count: 2
    },
    context = vm.createContext(initSandbox);

vm.runInContext('count += 1; name = "CATT"', context, 'myfile.vm');
console.log(util.inspect(context));

// { animal: 'cat', count: 3, name: 'CATT' }

createContext는 새로 생성된 컨텍스트의 전역객체를 초기화하기 위해서 제공된 샌드박스 객체의 얕은 복제를 수행할 것이다.

신뢰할 수 없는 코드를 실행하는 것은 아주 신중을 요하는 복잡한 작업이다. 의도치않은 전역변수의 누수를 막는데 vm.runInContext가 아주 유용하지만 신뢰하지 못하는 코드를 안전하게 실행하려면 프로세스를 분리할 필요가 있다.

code에서 문법오류가 있는 경우 vm.runInContext는 stderr에 문법오류를 발생시키고 예외를 던진다.

vm.createContext([initSandbox])#

vm.createContext는 뒤이은 vm.runInContext 호출의 두 번째 파라미터로 사용하기에 적합한 새로운 컨텍스트를 생성한다. (V8) 컨텍스트트는 내장된 객체 및 기능들과 함께 하나의 전역객체로 이루어져 있다. 선택적인 아규먼트인 initSandbox는 컨텍스트가 사용할 전역객체의 초기 내용을 생성하려고 얕은 복사가 될 것이다.

vm.createScript(code, [filename])#

createScriptcode를 컴파일하지만 실행하지는 않는다. 대신 컴파일된 코드를 나타내는 vm.Script 객체를 반환한다. 이 스크립트는 아래의 메서드들을 사용해서 나중에 여려 번 실행할 수 있다. 반환된 스크립트는 어떤 전역객체에도 바인딩되지 않는다. 반환된 스크립트는 매번 실행되기 전에 해당 실행에 대해서 바인딩된다. filename는 선택사항이고 스택트레이스에서만 사용될 것이다.

code에 문법오류가 있는 경우 createScript는 stderr에 문법오류를 출력하고 예외를 던진다.

Class: Script#

스크립트를 실행하는 클래스. vm.createScript가 반환하는 클래스다.

script.runInThisContext()#

vm.runInThisContext와 유사하지만 미리 컴파일된 Script 객체의 메서드이다. script.runInThisContextscript의 코드를 실행하고 결과를 반환한다. 실행되는 코드는 지역범위에 접근하지 않지만 global 객체에는 접근한다. (v8: 실제 컨텍스트에서)

한번 코드를 컴파일하고 여러 번 실행하는 script.runInThisContext를 사용하는 예제

var vm = require('vm');

globalVar = 0;

var script = vm.createScript('globalVar += 1', 'myfile.vm');

for (var i = 0; i < 1000 ; i += 1) {
  script.runInThisContext();
}

console.log(globalVar);

// 1000

script.runInNewContext([sandbox])#

vm.runInNewContext와 유사하지만 미리 컴파일된 Script 객체의 메서드이다. script.runInNewContext는 전역객체인 sandbox와 함께 script의 코드를 실행하고 결과를 반환한다. 실행되는 코드는 지역범위에 접근하지 않는다. sandbox는 선택사항이다.

예제: 전역변수가 증가하고 전역변수를 설정하는 코드를 컴파일하고 이 코드를 여러 번 실행한다. 이 전역변수들은 sandbox안에 있다.

var util = require('util'),
    vm = require('vm'),
    sandbox = {
      animal: 'cat',
      count: 2
    };

var script = vm.createScript('count += 1; name = "kitty"', 'myfile.vm');

for (var i = 0; i < 10 ; i += 1) {
  script.runInNewContext(sandbox);
}

console.log(util.inspect(sandbox));

// { animal: 'cat', count: 12, name: 'kitty' }

신뢰할 수 없는 코드를 실행하는 것은 아주 신중을 요하는 복잡한 작업이다. 의도치않은 전역변수의 누수를 막는데 script.runInNewContext가 아주 유용하지만 신뢰하지 못하는 코드를 안전하게 실행하려면 프로세스를 분리할 필요가 있다.

Child Process#

Stability: 3 - Stable

Node는 child_process 모듈로 세 방향의 popen(3) 기능을 제공한다.

완전한 넌블락킹 방법으로 자식 프로세스의 stdin, stdout, stderr에 데이터를 스트리밍하는 것이 가능하다.

자식 프로세스를 생성하려면 require('child_process').spawn()require('child_process').fork()를 사용해라. 각각의 의미는 약간 다른데 아래에서 설명한다.

Class: ChildProcess#

ChildProcessEventEmitter이다.

자식 프로세스들은 자신들과 연관된 세 가지 스트림 child.stdin, child.stdout, child.stderr를 항상 가진다. 이 세 스트림은 부모 프로세스의 stdio 스트림을 공유하거나 파이프로 연결될 수 있는 스크림을 구분할 것이다.

ChildProcess 클래스는 직접 사용하도록 만들어 진 것이 아니다. 자식 프로세스의 인스턴스를 생성하려면 spawn()fork()를 사용해라.

Event: 'exit'#

  • code 숫자 정상적으로 종료되는 경우의 종료코드.
  • signal String 부모가 자식프로세스를 죽일 때 자식 프로세스에 전달하는 신호.

이 이벤트는 자식프로세스가 종료된 후에 발생한다. 프로세스가 정상적으로 종료된다면 code는 프로세스의 최종 종료코드이고 정상적으로 종료되지 않았다면 null이다. 프로세스가 신호를 받아서 종료되었다면 signal는 문자열로 된 신호의 이름이고 신호를 받아서 종료되지 않았다면 null이다.

자식 프로세스의 stdio 스트림은 여전히 열려있을 것이다.

waitpid(2)를 봐라.

Event: 'close'#

이 이벤트는 자식 프로세스의 stdio 스트림이 모두 종료되었을 때 발생한다. 이 이벤트는 다중 프로세스가 같은 stdio 스트림을 공유할 수도 있으므로 'exit'와는 다르다.

Event: 'disconnect'#

이 이벤트는 부모나 자식의 .disconnect() 메서드를 사용한 수에 발생한다. 연결이 끊긴 후에는 더이상 메시지를 보낼 수 없다. child.connected 프로퍼티가 true인지 확인하는 것이 메시지를 보낼 수 있는지 검사하는 또 다른 방법이다.

Event: 'message'#

  • message Object 파싱된 JSON 객체나 프리미티브 값
  • sendHandle Handle object Socket이나 Server 객체

.send(message, [sendHandle])로 보낸 메시지는 message 이벤트로 받는다.

child.stdin#

  • Stream 객체

자식 프로세스의 stdin를 나타내는 Writable Stream이다. end()로 이 스트림을 닫으면 종종 자식 프로세스가 종료되기도 한다.

자식 프로세스의 stdio 스트림이 부모 프로세스와 공유한다면 이 값은 설정되지 않을 것이다.

child.stdout#

  • Stream 객체

자식 프로세스의 stdout를 나타내는 Readable Stream이다.

자식 프로세스의 stdio 스트림이 부모 프로세스와 공유한다면 이 값은 설정되지 않을 것이다.

child.stderr#

  • Stream 객체

자식 프로세스의 stderr를 나타내는 Readable Stream이다.

자식 프로세스의 stdio 스트림이 부모 프로세스와 공유한다면 이 값은 설정되지 않을 것이다.

child.pid#

  • 정수

자식 프로세스의 PID.

예제:

var spawn = require('child_process').spawn,
    grep  = spawn('grep', ['ssh']);

console.log('Spawned child pid: ' + grep.pid);
grep.stdin.end();

child.kill([signal])#

  • signal 문자열

자식 프로세스에 신호를 보낸다. 아규먼트를 전달하지 않으면 프로세스는 'SIGTERM'를 보낼 것이다. 사용할 수 있는 신호 목록은 signal(7)를 참고해라.

var spawn = require('child_process').spawn,
    grep  = spawn('grep', ['ssh']);

grep.on('exit', function (code, signal) {
  console.log('child process terminated due to receipt of signal '+signal);
});

// 프로세스에 SIGHUP를 보낸다
grep.kill('SIGHUP');

함수의 이름이 kill이기는 하지만 자식 프로세스에 전달된 신호가 실제로 자식 프로세스를 죽이지는 않을 것이다. kill은 프로세스에 단지 신호를 보낼 뿐이다.

kill(2)를 참고해라.

child.send(message, [sendHandle])#

  • message 객체
  • sendHandle Handle 객체

child_process.fork()를 사용했을 때 child.send(message, [sendHandle])를 사용해서 자식에 작성할 수 있고 자식에서는 'message' 이벤트로 메시지를 받는다.

예를 들어:

var cp = require('child_process');

var n = cp.fork(__dirname + '/sub.js');

n.on('message', function(m) {
  console.log('PARENT got message:', m);
});

n.send({ hello: 'world' });

그리고 자식 스크립트인 'sub.js'는 다음과 같을 것이다.

process.on('message', function(m) {
  console.log('CHILD got message:', m);
});

process.send({ foo: 'bar' });

자식에서 process 객체는 send() 메시지를 가질 것이고 process는 채널에서 메시지를 받을 때마다 객체를 발생시킬 것이다.

{cmd: 'NODE_foo'} 메시지를 보냈을 때 특별한 경우가 있다. cmd 프로퍼티에 NODE_ 접두사가 있는 모든 메시지는 node 코어에서 사용되는 내부 메시지이므로 message 이벤트에서 발생하지 않을 것이다. 접두사가 있는 메시지들은 internalMessage 이벤트를 발생시킨다. 이는 별도의 공지없이 변경되므로 이 기능을 사용하지 말아야 한다는 것을 의미한다.

The sendHandle option to child.send() is for sending a TCP server or socket object to another process. The child will receive the object as its second argument to the message event. child.send()sendHandle 옵션은 TCP 서버나 소켓 객체를 다른 프로세스에 보내는 용도이다. 자식 프로세스는 message 이벤트의 두번째 아규먼트로 이 객체를 받을 것이다. 자식 프로세스에 메시지(선택적으로 핸들 객체와 함께)를 보낸다.

서버 객체 전송

다음은 서버를 전송하는 예제다.

var child = require('child_process').fork('child.js');

// 서버객체를 열고 handle을 전송한다.
var server = require('net').createServer();
server.on('connection', function (socket) {
  socket.end('handled by parent');
});
server.listen(1337, function() {
  child.send('server', server);
});

자식프로세스는 다음과 같이 서버 객체를 받는다.

process.on('message', function(m, server) {
  if (m === 'server') {
    server.on('connection', function (socket) {
      socket.end('handled by child');
    });
  }
});

서버는 이제 부모와 자식사이에서 공유된다. 이는 연결들이 부모와 자식에서 모두 다룰 수 있다는 의미이다.

소켓 객체 전송

다음은 소켓을 전송하는 예제다. 이 예제는 두 자식 프로세스를 생성하고 "특별한" 자식 프로세스에 소켓을 전송해서 VIP인 원격주소 "special"의 연결을 다룬다. 다른 소켓들은 "보통의" 프로세스로 갈 것이다.

var normal = require('child_process').fork('child.js', ['normal']);
var special = require('child_process').fork('child.js', ['special']);

// 서버를 열고 자식 프로세스에 소켓을 전송한다
var server = require('net').createServer();
server.on('connection', function (socket) {

  // VIP 이라면
  if (socket.remoteAddress === '74.125.127.100') {
    special.send('socket', socket);
    return;
  }
  // 그냥 일반적인 소켓들
  normal.send('socket', socket);
});
server.listen(1337);

child.js는 다음과 같다.

process.on('message', function(m, socket) {
  if (m === 'socket') {
    socket.end('You were handled as a ' + process.argv[2] + ' person');
  }
});

일단 하나의 소켓을 자식 프로세스에 보내면 부모 프로세서는 소켓이 소멸된 것을 더이상 추적할 수 없다. 소켓이 소멸된 것을 나타내기 위해 .connections 프로퍼티가 null이 된다. 이 경우에 .maxConnections를 사용하지 않기를 권장한다.

child.disconnect()#

부모와 자식간의 IPC 연결을 닫으려면 child.disconnect() 메서드를 사용해라. 이 메서드는 살아있는 IPC 채널이 없기 때문에 자식프로세스가 안전적으로 종료할 수 있게 한다. 이 메서드를 호출했을 때 부모와 자식 모두에서 disconnect 이벤트가 발생할 것이고 connected 플래그는 false가 된다. 자식 프로세스에서 process.disconnect()를 호출할 수 있다는 것을 기억해라.

child_process.spawn(command, [args], [options])#

  • command 문자열 실행할 명령어
  • args 배열 문자열 아규먼트의 리스트
  • options 객체
    • cwd 문자열 자식 프로세스의 현재 워킹 디렉토리
    • stdio 배열|문자열 자식의 stdio 설정. (하단을 참고)
    • customFds 배열 폐기됨 stdio에 사용할 자식 프로세스의 파일 디스크립터 (하단을 참고)
    • env 객체 환경변수 키-밸류 쌍
    • detached 불리언 자식이 프로세스 그룹의 리더가 될 것이다. (하단을 참고)
    • uid 숫자 프로세스의 사용자 id를 설정한다. (setuid(2) 참고.)
    • gid 숫자 프로세스의 그룹 id를 설정한다. (setgid(2) 참고.)
  • return: ChildProcess 객체

명령행 아규먼트 args와 함께 주어진 command로 새로운 프로세스를 실행한다. 생략할 경우 args의 기본값은 비어있는 배열이다.

세 번째 아규먼트는 선택적으로 옵션을 지정하기 위해서 사용하면 기본값은 다음과 같다.

{ cwd: undefined,
  env: process.env
}

cwd로 프로세스가 생성되는(spawn)가 생성되는 워킹 디렉토리를 지정할 수 있다. env를 사용해서 새로운 프로세스에서 볼 수 있는 환경 변수를 지정한다.

다음은 ls -lh /usr를 실행하고 stdout, stderr와 종료 코드를 잡는 예제다.

var spawn = require('child_process').spawn,
    ls    = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', function (data) {
  console.log('stdout: ' + data);
});

ls.stderr.on('data', function (data) {
  console.log('stderr: ' + data);
});

ls.on('exit', function (code) {
  console.log('child process exited with code ' + code);
});

예제: 'ps ax | grep ssh'를 실행하는 아주 정교한 방법.

var spawn = require('child_process').spawn,
    ps    = spawn('ps', ['ax']),
    grep  = spawn('grep', ['ssh']);

ps.stdout.on('data', function (data) {
  grep.stdin.write(data);
});

ps.stderr.on('data', function (data) {
  console.log('ps stderr: ' + data);
});

ps.on('exit', function (code) {
  if (code !== 0) {
    console.log('ps process exited with code ' + code);
  }
  grep.stdin.end();
});

grep.stdout.on('data', function (data) {
  console.log('' + data);
});

grep.stderr.on('data', function (data) {
  console.log('grep stderr: ' + data);
});

grep.on('exit', function (code) {
  if (code !== 0) {
    console.log('grep process exited with code ' + code);
  }
});

실패한 실행을 확인하는 예제.

var spawn = require('child_process').spawn,
    child = spawn('bad_command');

child.stderr.setEncoding('utf8');
child.stderr.on('data', function (data) {
  if (/^execvp\(\)/.test(data)) {
    console.log('Failed to start child process.');
  }
});

spawn이 비어 있는 옵션 객체를 받으면 process.env를 사용하는 대신 비어있는 환경변수를 가진 프로세스를 생성(spawn)할 것이다. 이는 폐기된 API와 관련된 하위 호환성때문에 존재하는 것이다.

child_process.spawn()의 'stdio' 옵션은 자식 프로세스의 fd에 대응하는 각 인덱스 배열이다. 값은 다음 중 하나이다.

  1. 'pipe' - 자식프로세스와 부모프로세스간의 파이프를 생성한다. 파이프의 부모 쪽은 ChildProcess.stdio[fd]처럼 child_process 객체의 프로퍼티로 부모에게 노출된다. fd 0 - 2에 대해 생성된 파이프들은 각각 ChildProcess.stdin, ChildProcess.stdout, ChildProcess.stderr로 사용할 수도 있다.
  2. 'ipc' - 부모와 자식간에 메시지/파일 디스크립터를 전달하는 IPC 채널을 생성한다. ChildProcess는 많아야 하나의 IPC stdio 파일 디스크립터를 가진다. 이 옵션을 설정하면 ChildProcess.send() 메서드가 활성화된다. 자식 프로세스가 이 파일 디스크립터에 JSON 메시지를 작성한다면 파일디스크립터는 ChildProcess.on('message')를 실행한다. 자식 프로세스가 Node.js 프로그램이면서 IPC 채널이 있으면 process.send()와 process.on('message') 를 활성화한다.
  3. 'ignore' - 자식 프로세스에서 이 파일디스크립터를 설정하지 마라. Node는 생성한 프로세스에 대해 항상 fd 0 - 2를 연다. 이 중에 무시되는 것이 있다면 node는 /dev/null를 열고 자식의 fd에 붙힐 것이다.
  4. Stream 객체 - tty, 파일, 소켓, 자식프로세스에 연결된 파이프를 참조하는 읽거나 쓰기가 가능한 스트림을 공유한다. 스트림이 의존하는 파일 디스크립터는 자식 프로세스에서 stdio 비열의 인덱스에 대응하는 fd로 복제된다.
  5. 양의 정수 - 이 정수값은 부모 프로세스에서 현재 열려있는 파일 디스크립터로 변환된다. Stream 객체가 공유되는 방법과 유사하게 자식 프로세스와 공유된다.
  6. null, undefined - 기본값을 사용한다. stdio fd 0, 1, 2(즉 stdin, stdout, stderr)에 대한 파이프를 생성한다. fd 3 이상에 대한 기본값은 'ignore'이다.

간단하게 stdio 아규먼트는 배열보다는 다음 문자열중의 하나가 될 수도 있다.

  • ignore - ['ignore', 'ignore', 'ignore']
  • pipe - ['pipe', 'pipe', 'pipe']
  • inherit - [process.stdin, process.stdout, process.stderr][0,1,2]

예제:

var spawn = require('child_process').spawn;

// 자식은 부모의 stdio를 사용할 것이다
spawn('prg', [], { stdio: 'inherit' });

// stderr만 공유하는 자식프로세스를 생성한다
spawn('prg', [], { stdio: ['pipe', 'pipe', process.stderr] });

// startd 방식의 인터페이스를 제공하는 프로그램과 상호작용하기 위해 
// 여분의 fd=4를 연다
spawn('prg', [], { stdio: ['pipe', null, null, null, 'pipe'] });

detached 옵션을 설정하면 자식 프로세스가 새로운 프로세스 그룹의 리더가 될 것이다. 이는 부모가 종료된 후에도 자식 프로세스가 계속해서 동작할 수 있게 한다.

기본적으로 부모는 분리된(detached) 자식프로세스가 종료되길 기다릴 것이다. 부모가 child을 기다리지 않게 하려면 child.unref() 메서드를 사용해라. 부모의 이벤트루프는 참조수에 자식을 포함시키지 않을 것이다.

오랫동안 실행되는 프로세스를 분리하고 출력을 파일로 보내는 예제.

 var fs = require('fs'),
     spawn = require('child_process').spawn,
     out = fs.openSync('./out.log', 'a'),
     err = fs.openSync('./out.log', 'a');

 var child = spawn('prg', [], {
   detached: true,
   stdio: [ 'ignore', out, err ]
 });

 child.unref();

오랫동안 동작하는 프로세스를 시작하려고 detached 옵션을 사용해도 stdio 설정을 부모에 접속하지 않도록 하지 않으면 프로세스는 백그라운드에서 동작하지 않을 것이다. 부모의 stdio를 상속받았다면 자식은 제어하는 터미널에 연결된 채로 유지될 것이다.

자식 프로세스의 stdio에 특정 파일 디스크립터를 지정하는 폐기된 옵션인 customFds가 있다. 이 API는 모든 플랫폼에서 사용할 수 있는 것이 아니라서 삭제되었다. customFds를 사용해서 새로운 프로세스의 [stdin, stdout, stderr]를 존재하는 스트림으로 후킹할 수 있다. -1은 새로운 스트림이 생성되어야 한다는 것을 의미한다. 사용할 때는 위험을 감수해라.

여러가지 내부 옵션이 존재한다. 특히 stdinStream, stdoutStream, stderrStream가 있다. 이 옵션들은 내부적으로만 사용된다. Node에서 문서화되지 않은 모든 API는 사용하지 말아야 한다.

child_process.exec()child_process.fork()도 참고해라.

child_process.exec(command, [options], callback)#

  • command 문자열 실행할 명령어로 전달할 아규먼트는 공백으로 구분한다
  • options 객체
    • cwd 문자열 자식 프로세스의 현재 워킹 디렉토리
    • stdio 배열|문자열 자식의 stdio 설정. (상단 참조)
    • customFds 배열 폐기됨 stdio에 사용할 자식 프로세스의 파일 디스크립터 (상단 참고)
    • env 객체 환경변수 키-밸류 쌍
    • encoding 문자열 (기본값: 'utf8')
    • timeout 숫자 (기본값: 0)
    • maxBuffer 숫자 (기본값: 200*1024)
    • killSignal 문자열 (기본값: 'SIGTERM')
  • callback 함수 프로세스가 종료되었을 대 출력과 함께 호출된다
    • error Error
    • stdout Buffer
    • stderr Buffer
  • Return: ChildProcess 객체

쉘에서 명령어를 실행하고 출력을 버퍼에 넣는다.

var exec = require('child_process').exec,
    child;

child = exec('cat *.js bad_file | wc -l',
  function (error, stdout, stderr) {
    console.log('stdout: ' + stdout);
    console.log('stderr: ' + stderr);
    if (error !== null) {
      console.log('exec error: ' + error);
    }
});

콜백은 (error, stdout, stderr) 아규먼트를 받는다. 성공했을 때 errornull이 된다. 오류가 있을 경우 errorError의 인스턴스가 되고 err.code는 자식 프로세스의 종료코드가 되고 err.signal은 프로세스를 종료하는 신호로 설정될 것이다.

두번째 선택적인 아규먼트는 여러가지 옵션을 지정한다. 기본 옵션은 다음과 같다.

{ encoding: 'utf8',
  timeout: 0,
  maxBuffer: 200*1024,
  killSignal: 'SIGTERM',
  cwd: null,
  env: null }

timeout이 0보다 크면 timeout 밀리초보다 오래 실행되는 경우 자식 프로세스를 죽일 것이다. 자식 프로세스는 killSignal (기본값: 'SIGTERM') 신호로 죽는다. maxBuffer는 stdout이나 stderr에서 사용할 수 있는 가장 큰 데이터의 양을 지정한다. maxBuffer갑을 초과하면 자식 프로세스는 죽을 것이다.

child_process.execFile(file, args, options, callback)#

  • file 문자열 실행할 프로그램의 파일명
  • args 배열 문자열 아규먼트의 목록
  • options 객체
    • cwd 문자열 자식 프로세스의 현재 워킹 디렉토리
    • stdio 배열|문자열 자식의 stdio 설정. (상단 참조)
    • customFds 배열 폐기됨 stdio에 사용할 자식 프로세스의 파일 디스크립터 (상단 참고)
    • env 객체 환경변수의 키-밸류 쌍
    • encoding 문자열 (기본값: 'utf8')
    • timeout 숫자 (기본값: 0)
    • maxBuffer 숫자 (기본값: 200*1024)
    • killSignal 문자열 (기본값: 'SIGTERM')
  • callback 함수 프로세스가 종료되었을 때 출력과 함께 호출된다.
    • error Error
    • stdout Buffer
    • stderr Buffer
  • Return: ChildProcess 객체

하위 쉘을 실행하는 대신에 지정한 파일을 직접 실행한다는 점을 제외하면 child_process.exec()와 유사하다. child_process.exec보다 약간 의존적이게 만든다. 이는 같은 옵션을 가진다.

child_process.fork(modulePath, [args], [options])#

  • modulePath 문자열 자식프로세스에서 실행될 모듈
  • args 배열 문자열 아규먼트의 목록
  • options 객체
    • cwd 문자열 자식프로세스의 현재 워킹 디렉토리
    • env 객체 환경변수의 키-밸류 쌍
    • encoding 문자열 (기본값: 'utf8')
  • Return: ChildProcess 객체

이는 Node 프로세스를 생성하기(spawn) 위해 spawn() 기능의 특별한 경우이다. 게다가 보통의 ChildProcess 인스턴스에서 모든 메서드를 가지려고 반환된 객체는 내장된 통신 채널을 가진다. 자세한 내용은 child.send(message, [sendHandle])를 참고해라.

기본적으로 생성된(spawned) Node 프로세서는 부모의 stdout, stderr와 연관된 stdout, stderr를 가질 것이다. 이 동작을 변경하려면 options 객체의 silent 프로퍼티를 true로 설정해라.

자식 프로세스들은 완료되었다고 자동으로 종료되지 않으므로 명시적으로 process.exit()를 호출해야 한다. 차후에는 이 제약사항이 없어질 것이다.

이러한 자식 노드들도 V8의 완전한 새 인스턴스이다. 새로운 각 노드마다 최소한 30ms의 구동시간과 10mb의 메모리를 가정해보자. 즉, 수천 개의 노드를 생성할 수 없다.

Assert#

Stability: 5 - Locked

이 모듈을 어플리케이션에서 유닛 테스트를 작성하는데 사용하고 require('assert')로 이 모듈을 사용할 수 있다.

assert.fail(actual, expected, message, operator)#

actualexpected의 값을 보여주는 예외를 던진다. operator는 두 값을 무엇으로 비교했는지 표시하는 것이다. (역주, operator는 결국 Error.captureStackTrace로 넘겨지는데 무슨 역활인지 이해할 수 없음. 하지만 assert.js의 코드를 보면 모두 스트링이다. 비교하는데 사용한 함수나 Operator 이름임)

assert(value, message), assert.ok(value, [message])#

value가 참인지 검사한다. 이는 assert.equal(true, !!value, message);와 같다.

assert.equal(actual, expected, [message])#

== 오퍼레이터로 같음(shallow, coercive equality)을 검사한다.

assert.notEqual(actual, expected, [message])#

!= 오퍼레이터로 다름(shallow, coercive non-equality)을 검사한다.

assert.deepEqual(actual, expected, [message])#

깊은 동등성을 검사한다. (역주, deep equality는 한마디로 설명하기 어렵다. 타입마다 다른 방법으로 같음을 테스트한다. 예를 들어, Date의 경우 actual.getTime() === expected.getTime()라고 비교한다. 필요하다면 assert.js을 일독하기를 권한다)

assert.notDeepEqual(actual, expected, [message])#

다름(deep inequality)를 검사한다.

assert.strictEqual(actual, expected, [message])#

=== 오퍼레이터로 같음(strict equality)을 테스트한다.

assert.notStrictEqual(actual, expected, [message])#

!== 오퍼레이터로 다름(strict non-equality)을 테스트한다.

assert.throws(block, [error], [message])#

block이 오류는 던지기를 기대한다. error는 생성자, 정규표현식, 유효성검사 함수가 될 수 있다.

생성자의 인스턴스인지 테스트한다.

assert.throws(
  function() {
    throw new Error("Wrong value");
  },
  Error
);

RegExp를 사용해서 오류 메시지를 검증한다.

assert.throws(
  function() {
    throw new Error("Wrong value");
  },
  /value/
);

자신만의 방법으로 오류를 검증한다.

assert.throws(
  function() {
    throw new Error("Wrong value");
  },
  function(err) {
    if ( (err instanceof Error) && /value/.test(err) ) {
      return true;
    }
  },
  "unexpected error"
);

assert.doesNotThrow(block, [error], [message])#

block이 오류를 던지지 않기를 기대한다. 자세한 내용은 assert.throws를 참고해라.

assert.ifError(value)#

value가 true인지 테스트하고 true이면 그 value를 던진다. 콜백에서 첫 아규먼트 error를 검사하는 데 유용하다. (역주, 예를 들면 fs.readFile('notfound.js', assert.ifError);라고 사용할 수 있어서 유용하다는 것이다)

TTY#

Stability: 2 - Unstable

tty 모듈에는 tty.ReadStreamtty.WriteStream 클래스가 들어 있다. 보통 이 모듈을 직접 사용할 일은 별로 없다.

TTY 컨텍스트에서 실행되면 자동으로 process.stdintty.ReadStream 인스턴스로 만들고 process.stdouttty.WriteStream 인스턴스로 만든다. process.stdout.isTTY를 검사하면 node가 TTY 컨텍스트에서 실행되는지 아닌지 알 수 있다.

$ node -p -e "Boolean(process.stdout.isTTY)"
true
$ node -p -e "Boolean(process.stdout.isTTY)" | cat
false

tty.isatty(fd)#

fd가 터미널 파일 디스크립터인지에 따라 truefalse를 리턴한다.

tty.setRawMode(mode)#

Deprecated 됐다. tty.ReadStream#setRawMode()를 사용하라(i.e. process.stdin.setRawMode()).

Class: ReadStream#

TTY 읽기를 담당하는 net.Socket 서브클래스이다. 보통 node 프로그램의 process.stdintty.ReadStream 인스턴스이다(isatty(0)가 true일 때만).

rs.isRaw#

이 프로퍼티는 false로 초기화된다. tty.ReadStream 인스턴스가 현재 "raw" 상태임을 나타낸다.

rs.setRawMode(mode)#

mode에는 truefalse를 넘긴다. mode가 true이면 tty.ReadStream 프로퍼티가 raw 디바이스 처럼 동작하도록 설정하고 아니면 기본 값이 설정된다. isRaw 프로퍼티로 어떻게 설정했는지 알 수 있다.

Class WriteStream#

TTY 쓰기를 담당하는 net.Socket 서브클래스이다. 보통 node 프로그램의 process.stdouttty.ReadStream 인스턴스이다(isatty(1)가 true일 때만).

ws.columns#

이 프로퍼티는 현 TTY의 컬럼 수이다. "resize" 이벤트 시 업데이트된다.

ws.rows#

이 프로퍼티는 현 TTY의 로우 수이다. "resize" 이벤트 시 업데이트된다.

Event: 'resize'#

function () {}

columnsrows 프로퍼티가 변경될 때 발생한다.

process.stdout.on('resize', function() {
  console.log('screen size has changed!');
  console.log(process.stdout.columns + 'x' + process.stdout.rows);
});

Zlib#

Stability: 3 - Stable

이 모듈은 다음과 같은 방법으로 접근한다:

var zlib = require('zlib');

이 모듈은 Gzip/Uunzip, Deflate/Inflate, DeflateRaw/InflateRaw 클래스에 대한 바인딩이다. 각 클래스는 읽기/쓰기 가능한 스트림이고 옵션은 모두 동일하다.

Examples#

파일을 압축하거나 압축을 푸는 일은 fs.ReadStream()으로 파일을 읽어서 zlib 스트림으로 보내고 나서(pipe) 다시 fs.WriteStream에 보내는(pipe) 것으로 이루어진다.

var gzip = zlib.createGzip();
var fs = require('fs');
var inp = fs.createReadStream('input.txt');
var out = fs.createWriteStream('input.txt.gz');

inp.pipe(gzip).pipe(out);

데이터를 압축하고 압축을 푸는 일은 간단하게 단축 메소드로(convenience method) 한방에 할 수 있다:

var input = '.................................';
zlib.deflate(input, function(err, buffer) {
  if (!err) {
    console.log(buffer.toString('base64'));
  }
});

var buffer = new Buffer('eJzT0yMAAGTvBe8=', 'base64');
zlib.unzip(buffer, function(err, buffer) {
  if (!err) {
    console.log(buffer.toString());
  }
});

HTTP client나 server에서 이 모듈를 사용하려면 request에서 사용할 수 있는 accept-encoding을 보고 response에서는 content-encoding를 보면 된다.

Note: 이 예제는 기본 개념을 보여주기 위해 극도로 단순화한 것임. Zlib 인코딩은 비싸서 결과물을 캐시하는게 좋다. Memory Usage Tuning을 보면 zlib 튜닝시 speed/memory/compression에 대해 고려해야 하는 점이 나와 있다.

// 클라이언트 요청 예제
var zlib = require('zlib');
var http = require('http');
var fs = require('fs');
var request = http.get({ host: 'izs.me',
                         path: '/',
                         port: 80,
                         headers: { 'accept-encoding': 'gzip,deflate' } });
request.on('response', function(response) {
  var output = fs.createWriteStream('izs.me_index.html');

  switch (response.headers['content-encoding']) {
    // 또는 두 경우를 모두 다루기 위해서 그냥 zlib.createUnzip()를 사용해라.
    case 'gzip':
      response.pipe(zlib.createGunzip()).pipe(output);
      break;
    case 'deflate':
      response.pipe(zlib.createInflate()).pipe(output);
      break;
    default:
      response.pipe(output);
      break;
  }
});

// 서버 예제
// 요청마다 gzip을 수행하는 것은 비용이 큰 작업이다.
// 압축된 버퍼를 캐시해서 훨씬 효율적이 될 수 있다.
var zlib = require('zlib');
var http = require('http');
var fs = require('fs');
http.createServer(function(request, response) {
  var raw = fs.createReadStream('index.html');
  var acceptEncoding = request.headers['accept-encoding'];
  if (!acceptEncoding) {
    acceptEncoding = '';
  }

  // Note: 이는 적합한 accept-encoding 파서는 아니다.
  // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 를 봐라.
  if (acceptEncoding.match(/\bdeflate\b/)) {
    response.writeHead(200, { 'content-encoding': 'deflate' });
    raw.pipe(zlib.createDeflate()).pipe(response);
  } else if (acceptEncoding.match(/\bgzip\b/)) {
    response.writeHead(200, { 'content-encoding': 'gzip' });
    raw.pipe(zlib.createGzip()).pipe(response);
  } else {
    response.writeHead(200, {});
    raw.pipe(response);
  }
}).listen(1337);

zlib.createGzip([options])#

options으로 Gzip 객채를 새로 만들어 리턴한다.

zlib.createGunzip([options])#

options으로 Gunzip 객채를 새로 만들어 리턴한다.

zlib.createDeflate([options])#

options으로 Deflate 객채를 새로 만들어 리턴한다.

zlib.createInflate([options])#

options으로 Inflate 객채를 새로 만들어 리턴한다.

zlib.createDeflateRaw([options])#

options으로 DeflateRaw 객채를 새로 만들어 리턴한다.

zlib.createInflateRaw([options])#

options으로 InflateRaw 객채를 새로 만들어 리턴한다.

zlib.createUnzip([options])#

options으로 Unzip 객채를 새로 만들어 리턴한다.

Class: zlib.Gzip#

gzip으로 데이터를 압축한다.

Class: zlib.Gunzip#

gzip 스트림의 압축을 푼다.

Class: zlib.Deflate#

deflate로 데이터를 압축한다.

Class: zlib.Inflate#

deflate 스트림의 압축을 푼다.

Class: zlib.DeflateRaw#

deflate로 데이터를 압축하지만 zlib 헤더는 넣지 않는다.

Class: zlib.InflateRaw#

raw deflate 스트림의 압축을 푼다.

Class: zlib.Unzip#

Gzip-이나 Deflate-로 압축한 스트림의 헤더를 자동으로 찾아서 압축을 푼다.

Convenience Methods#

여기에 있는 모든 메소드는 첫번째 아규먼트로 버퍼나 스트링을 받는다. 그리고 콜백도 callback(error, result) 형식으로 호출한다. 압축/압축해제 엔진은 기본 설정으로 생성하고 다른 옵션으로 생성하고 싶으면 zlib 클래스를 직접사용해야 한다.

zlib.deflate(buf, callback)#

Deflate로 스트링을 압축한다.

zlib.deflateRaw(buf, callback)#

DeflateRaw로 스트링을 압축한다.

zlib.gzip(buf, callback)#

Gzip으로 스트링을 압축한다.

zlib.gunzip(buf, callback)#

Gunzip으로 Buffer의 압축을 푼다.

zlib.inflate(buf, callback)#

Inflate로 Buffer의 압축을 푼다.

zlib.inflateRaw(buf, callback)#

InflateRaw로 Buffer의 압축을 푼다.

zlib.unzip(buf, callback)#

Unzip으로 Buffer의 압축을 푼다.

Options#

모든 클래스는 옵션 객체를 아규먼트로 받고 생략 가능하다(단축 메소드는 기본값을 사용한다).

어떤 옵션은 압축 클래스에만 사용하고 압축을 푸는 클래스에서는 무시한다.

  • chunkSize (default: 16*1024)
  • windowBits
  • level (compression only)
  • memLevel (compression only)
  • strategy (compression only)
  • dictionary (deflate/inflate only, 기본값은 빈 dictionary)

deflateInit2inflateInit2의 설명은 http://zlib.net/manual.html#Advanced 페이지에서 보라.

Memory Usage Tuning#

zlib/zconf.h에 있는 설정을 node에 맞게 수정하는 법:

deflate할 때 필요한 메모리(바이트 단위):

(1 << (windowBits+2)) +  (1 << (memLevel+9))

이 표현은 windowBits=15일 때 128K가 필요하고 memLevel=8일 때 128k가 더 필요하다는 뜻이다(기본값임). 그리고 객체에 필요한 몇 킬로 바이트가 더 든다.

만약 필요한 메모리를 256K에서 128K로 줄이고 싶으면 옵션을 다음과 같이 주면된다:

{ windowBits: 14, memLevel: 7 }

물론 이 설정은 압축 성능을 떨어뜨린다(공짜 점심은 없다).

inflate에 필요한 메모리(바이트단위):

1 << windowBits

이 표현은 windowBits=15일 때 32K가 필요하다는 말이다(기본값임). 그리고 객체에 필요한 몇 킬로바이트가 더 든다.

그리고 내부에 결과물을 위한 버퍼가 하나 있다. chunkSize의 값이 버퍼의 크기인데 기본 값은 16K이다.

zlib 압축의 속도는 level 설정이 가장 큰 영향을 끼친다. 레벨을 높이면 압축률은 높아지지만 더 오래 걸린다. 레벨을 낮추면 압축률은 낮아지지만 더 빨라진다.

보통 메모리를 크게 잡으면 write 오퍼레이션을 한번 할 때 데이터를 더 많이 처리하기 때문에 Node가 zlib을 더 적게 호출한다. 그래서 이 요소도 속도와 메모리 사용 효율에 영향을 준다.

Constants#

zlib.h에 정의된 상수는 require('zlib')에도 정의돼 있다. 보통은 세세한 설정이 필요하지 않지만, 여기에서는 어떤 상수들이 있는지 설명한다. 이 절의 내용은 zlib documentation에서 거의 그대로 베껴왔다. 자세한 내용은 http://zlib.net/manual.html#Constants를 보라.

허용되는 flush 옵션.

  • zlib.Z_NO_FLUSH
  • zlib.Z_PARTIAL_FLUSH
  • zlib.Z_SYNC_FLUSH
  • zlib.Z_FULL_FLUSH
  • zlib.Z_FINISH
  • zlib.Z_BLOCK
  • zlib.Z_TREES

압축/압축해제 함수가 리턴하는 코드. 에러일 경우 코드값이 음수 값이고 성공 시에는 양수 값이다.

  • zlib.Z_OK
  • zlib.Z_STREAM_END
  • zlib.Z_NEED_DICT
  • zlib.Z_ERRNO
  • zlib.Z_STREAM_ERROR
  • zlib.Z_DATA_ERROR
  • zlib.Z_MEM_ERROR
  • zlib.Z_BUF_ERROR
  • zlib.Z_VERSION_ERROR

압축 레벨.

  • zlib.Z_NO_COMPRESSION
  • zlib.Z_BEST_SPEED
  • zlib.Z_BEST_COMPRESSION
  • zlib.Z_DEFAULT_COMPRESSION

압축 전략.

  • zlib.Z_FILTERED
  • zlib.Z_HUFFMAN_ONLY
  • zlib.Z_RLE
  • zlib.Z_FIXED
  • zlib.Z_DEFAULT_STRATEGY

data_type 필드에서 허용하는 값.

  • zlib.Z_BINARY
  • zlib.Z_TEXT
  • zlib.Z_ASCII
  • zlib.Z_UNKNOWN

deflate 압축 방법(이 버전에서만 지원한다).

  • zlib.Z_DEFLATED

zalloc, zfree, opaque를 초기화를 위해 사용한다.

  • zlib.Z_NULL

os#

Stability: 4 - API Frozen

OS 유틸리티 함수를 몇 개 제공한다.

이 모듈은 require('os')로 접근한다.

os.tmpDir()#

OS의 임시 파일 디렉토리를 리턴한다.

os.hostname()#

OS의 호스트 이름을 리턴한다.

os.type()#

OS 이름을 리턴한다(역주, 'Linux').

os.platform()#

OS 플랫폼을 리턴한다(역주, 'linux').

os.arch()#

OS CPU 아키텍처를 리턴한다(역주, 'x64').

os.release()#

OS 버전을 리턴한다(역주, '3.2.0-26-generic').

os.uptime()#

시스템 구동시간을 초 단위로 리턴한다(역주, 50515.673530518).

os.loadavg()#

1, 5, 15 분 로드 평균값을 배열에 담아 리턴한다.

os.totalmem()#

시스템 메모리의 총량을 바이트 단위로 리턴한다.

os.freemem()#

시스템의 여유 메모리를 바이트 단위로 리턴한다.

os.cpus()#

모든 CPU/코어에 대한 정보를 배열에 담아서 그 배열을 리턴한다. CPU/코어에 대한 정보는 model, speed(MHz 단위), times(user, nide, sys, idle, irq로 분류해서 각각 사용한 CPU 타임(CPU 틱의 수)이다.

os.cpus의 결과:

[ { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times:
     { user: 252020,
       nice: 0,
       sys: 30340,
       idle: 1070356870,
       irq: 0 } },
  { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times:
     { user: 306960,
       nice: 0,
       sys: 26980,
       idle: 1071569080,
       irq: 0 } },
  { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times:
     { user: 248450,
       nice: 0,
       sys: 21750,
       idle: 1070919370,
       irq: 0 } },
  { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times:
     { user: 256880,
       nice: 0,
       sys: 19430,
       idle: 1070905480,
       irq: 20 } },
  { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times:
     { user: 511580,
       nice: 20,
       sys: 40900,
       idle: 1070842510,
       irq: 0 } },
  { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times:
     { user: 291660,
       nice: 0,
       sys: 34360,
       idle: 1070888000,
       irq: 10 } },
  { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times:
     { user: 308260,
       nice: 0,
       sys: 55410,
       idle: 1071129970,
       irq: 880 } },
  { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
    speed: 2926,
    times:
     { user: 266450,
       nice: 1480,
       sys: 34920,
       idle: 1072572010,
       irq: 30 } } ]

os.networkInterfaces()#

네트워크 인터페이스의 목록을 리턴한다:

{ lo0: 
   [ { address: '::1', family: 'IPv6', internal: true },
     { address: 'fe80::1', family: 'IPv6', internal: true },
     { address: '127.0.0.1', family: 'IPv4', internal: true } ],
  en1: 
   [ { address: 'fe80::cabc:c8ff:feef:f996', family: 'IPv6',
       internal: false },
     { address: '10.0.1.123', family: 'IPv4', internal: false } ],
  vmnet1: [ { address: '10.99.99.254', family: 'IPv4', internal: false } ],
  vmnet8: [ { address: '10.88.88.1', family: 'IPv4', internal: false } ],
  ppp0: [ { address: '10.2.0.231', family: 'IPv4', internal: false } ] }

os.EOL#

Node를 실행하는 OS용 End-of-line 상수.

Debugger#

Stability: 3 - Stable

V8에는 확장 가능한 디버거가 들어 있다. 이 디버거는 간단한 TCP 프로토콜을 통해서 프로세스 외부에서 접근할 수 있다. 그리고 Node에는 이 디버거를 사용하는 빌트인 디버거 클라이언트가 들어 있다. Node를 실행할 때 debug 인자를 주면 디버거를 사용할 수 있다:

% node debug myscript.js
< debugger listening on port 5858
connecting... ok
break in /home/indutny/Code/git/indutny/myscript.js:1
  1 x = 5;
  2 setTimeout(function () {
  3   debugger;
debug>

이 빌트인 클라이언트는 명령어를 다 지원하지 않지만 step과 inspection을 간단히 해볼 수 있다. 소스코드에 debugger;를 집어 넣으면 그 부분이 breakpoint가 된다.

예를 들어, 다음과 같은 myscript.js를 보자:

// myscript.js
x = 5;
setTimeout(function () {
  debugger;
  console.log("world");
}, 1000);
console.log("hello");

그리고 나서 디버거를 실행하면 4번째 줄에서 멈춘다.

% node debug myscript.js
< debugger listening on port 5858
connecting... ok
break in /home/indutny/Code/git/indutny/myscript.js:1
  1 x = 5;
  2 setTimeout(function () {
  3   debugger;
debug> cont
< hello
break in /home/indutny/Code/git/indutny/myscript.js:3
  1 x = 5;
  2 setTimeout(function () {
  3   debugger;
  4   console.log("world");
  5 }, 1000);
debug> next
break in /home/indutny/Code/git/indutny/myscript.js:4
  2 setTimeout(function () {
  3   debugger;
  4   console.log("world");
  5 }, 1000);
  6 console.log("hello");
debug> repl
Press Ctrl + C to leave debug repl
> x
5
> 2+2
4
debug> next
< world
break in /home/indutny/Code/git/indutny/myscript.js:5
  3   debugger;
  4   console.log("world");
  5 }, 1000);
  6 console.log("hello");
  7
debug> quit
%

repl 명령어는 원격에서 코드를 실행해볼 수 있도록 해준다. next 명령어를 실행하면 다음 줄로 넘어간다(step over). 그외에도 사용할 수 있는 명령어가 꽤 있다. help를 입력하면 뭐가 있는지 보여준다.

Watchers#

디버깅하는 동안 표현식(expression)과 변수의 값을 watch할 수 있다. breakpoint마다 watcher에 등록된 표현을 현 컨텍스트에서 실행해서 보여준다. 그 다음에 breakpoint가 있는 소스코드가 출력된다.

watch("my_expression")으로 watcher를 등록하고 watchers 명령으로 등록된 watcher들을 확인할 수 있다. 그리고 unwatch("my_expression")으로 등록된 watcher를 제거할 수 있다.

Commands reference#

Stepping#

  • cont, c - 계속 실행
  • next, n - Step next
  • step, s - Step in
  • out, o - Step out
  • pause - 실행을 멈춘다(크롬 개발자 도구에서의 pause 버튼처럼)

Breakpoints#

  • setBreakpoint(), sb() - 현 라인에 breakpoint를 설정한다.
  • setBreakpoint(line), sb(line) - 특정 라인에 breakpoint를 설정한다.
  • setBreakpoint('fn()'), sb(...) - 함수 바디의 첫 라인에 breakpoint를 설정한다.
  • setBreakpoint('script.js', 1), sb(...) - script.js의 첫 라인에 breakpoint를 설정한다.
  • clearBreakpoint, cb(...) - breakpoint를 제거

Info#

  • backtrace, bt - 현 execution의 backtrace를 출력한다.
  • list(5) - 스크립트 소스코드를 다섯 라인 나열한다. 다섯 라인은 현재 멈춘 컨텍스트 전후에 있는 라인을 의미한다.
  • watch(expr) - watch 목록에 표현식을 넣는다.
  • unwatch(expr) - watch 목록에서 표현식을 제거한다.
  • watchers - 등록된 watcher와 그 값을 함께 보여준다(breakpoint에 멈출 때에도 자동으로 보여준다).
  • repl - 디버그하는 스크립트의 컨텍스트에서 실행되는 repl을 연다.

Execution control#

  • run - 스크립트 실행 (디버거를 실행하면 자동으로 이 명령이 수행된다)
  • restart - 스크립트 재시작
  • kill - 스크립트 끝내기

Various#

  • scripts - 열린 스크립트를 모두 보여준다.
  • version - v8의 버전을 보여준다.

Advanced Usage#

커맨드 라인에서 --debug 플래그 주고 Node를 실행하면 V8 디버거가 켜진다. 또 이미 실행중인 Node 프로세스에 SIGUSR1 시그널을 보내면 V8 디버거가 켜지고 접근해서 사용할 수 있다.

Cluster#

Stability: 1 - Experimental

Node 프로세스 하나는 쓰레드 하나로 동작한다. 멀티 코어 시스템을 이용해서 부하를 처리하려면 Node 프로세스를 여러 개 띄울 필요가 있다.

이 cluster 모듈은 서버 포트를 공유하는 프로세스 다발을 쉽게 만들 수 있게 해준다.

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  // 워커를 포크한다.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
  // 워커는 TCP 연결을 공유할 수 있다.
  // 이 예제는 HTTP 서버이다.
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}

이 프로그램에서 워커는 8000번 포트를 공유한다:

% node server.js
Worker 2438 online
Worker 2437 online

이 기능은 최근에 추가돼서 앞으로 변경될 수 있다. 해보고 안되면 피드백을 주길 바란다.

Windows에서는 네임드 파이프 서버를 만들 수 없으므로 주의해야 한다.

How It Works#

워커 프로세스는 child_process.fork 메소드로 생성(spawn)하기 때문에 IPC로 부모 프로세스와 통신하고 서버 핸들을 서로 주고받을 수 있다.

워커에서 server.listen(...)를 호출하면 아규먼트를 직열화하고 요청을 마스터 프로세스에 보낸다. 마스터 프로세스에 있는 리스닝 서버가 워커의 요구사항에 맞으면 그 핸들을 워커에 넘긴다. 만약 워커에 필요한 리스닝 서버가 없으면 하나 만들어서 자식 프로세스에 핸들을 넘긴다.

그래서 극단적인 상황에서(edge case) 다음과 같은 행동을 보인다.:

  1. server.listen({fd: 7}) 메시지를 마스터에 보내기 때문에 부모에서 7번 파일 디스크립터를 Listen하기 시작하고 그 핸들이 워커로 넘겨진다. 7번 파일 디스크립터가 무엇인지 워커에 묻거나 하는 것이 아니다.
  2. server.listen(handle) 핸들에 직접 Listen하면 워커는 넘긴 핸들을 사용한다. 마스터에 메시지를 보내지 않는다. 워커가 이미 핸들을 가지고 있으면 이렇게 호출하는 사람은 자신이 무슨짓을 하는 것인지 알고 있다고 간주된다.
  3. server.listen(0) 이렇게 호출하면 서버는 랜덤 포트에 Listen한다. 하지만, cluster에서는 워커가 listen(0)를 호출할 때마다 동일한 랜덤 포트를 받는다. 사실 처음에만 랜덤이지 그다음부터는 예측 가능하다. 워커마다 고유한(unique) 포트를 사용하려면 워커 ID를 이용해서 포트 넘버를 직접 생성하라.

같은 리소스에 accept()하는 프로세스가 여러 개이면 운영체제는 매우 효율적으로 Load-balance를 한다. Node.js에는 라우팅 로직이 없고 워커끼리 상태 정보도 공유하지도 않는다. 그러므로 로그인이나 세션 정보 같은 것을 메모리에 너무 과도하게 상주하지 않도록 프로그램을 설계하는 것이 중요하다.

워커는 서로 독립적인 프로세스이므로 프로그램의 필요에 따라 죽이거나(kill) 재생성(re-spawn)할 수 있다. 워커가 살아 있는 한 새 연결을 계속 수락한다. Node는 워커를 관리해주지 않는다. 애플리케이션에 필요한대로 워커 풀을 관리하는 것은 개발자 책임이다.

cluster.settings#

  • Object
  • exec String 워커 파일의 경로. (Default=__filename)
    • args Array 워커에 넘겨지는 스트링 아규먼트. (Default=process.argv.slice(2))
    • silent Boolean 워커의 output을 부모의 stdio로 보낼지 말지. (Default=false)

.setupMaster() 메소드로 설정하면 이 settings 객체에 저장된다. 이 객체를 직접 수정하지 말아야 한다.

cluster.isMaster#

  • Boolean

프로세스가 마스터이면 true를 리턴한다. 이 프로퍼티는 process.env.NODE_UNIQUE_ID 값을 이용하는데 process.env.NODE_UNIQUE_ID이 undefined이면 isMaster는 true이다.

cluster.isWorker#

  • Boolean

해당 프로세스가 워커면 true를 리턴한다. 이 프로퍼티는 process.env.NODE_UNIQUE_ID 값을 이용하는데 process.env.NODE_UNIQUE_ID 프로퍼티에 값이 할당돼 있으면 true이다.

Event: 'fork'#

  • worker Worker 객체

워커가 하나 새로 포크되면 cluster 모듈은 'fork' 이벤트를 발생(emit)시킨다. 워커의 액티비티 로그를 남기거나 타임아웃을 생성하는 데 활용된다.

var timeouts = [];
function errorMsg() {
  console.error("Something must be wrong with the connection ...");
}

cluster.on('fork', function(worker) {
  timeouts[worker.id] = setTimeout(errorMsg, 2000);
});
cluster.on('listening', function(worker, address) {
  clearTimeout(timeouts[worker.id]);
});
cluster.on('exit', function(worker, code, signal) {
  clearTimeout(timeouts[worker.id]);
  errorMsg();
});

Event: 'online'#

  • worker Worker 객체

워커를 포크하면 워커는 '온라인' 메시지를 보낸다. 마스터가 그 '온라인' 메시지를 받으면 이 이벤트를 발생한다. 'fork' 이벤트와 'online' 이벤트의 차이는 간단하다. 'fork' 이벤트는 마스터가 워커 프로세스를 포크할 때 발생하는 것이고 'online' 이벤트는 워커가 실행되면 발생한다.

cluster.on('online', function(worker) {
  console.log("Yay, the worker responded after it was forked");
});

Event: 'listening'#

  • worker Worker 객체
  • address Object

워커에서 listen()을 호출하면 자동으로 'listening' 이벤트가 서버 인스턴스에 발생한다. 서버가 listening 중이면 listening' 이벤트가 발생한 마스터는 메시지를 하나 받는다.

이벤트 핸들러의 아규먼트는 두 개다. worker에는 해당 워커 객체가 넘어오고 address에는 address, port, addressType 프로퍼티가 있는 address 객체가 넘어온다. 이 이벤트는 워커가 하나 이상의 주소를 Listen할 때 유용하다.

cluster.on('listening', function(worker, address) {
  console.log("A worker is now connected to " + address.address + ":" + address.port);
});

Event: 'disconnect'#

  • worker Worker 객체

워커의 IPC 채널이 끊기면 이 이벤트가 발생한다. 워커가 죽을 때도 이 이벤트가 발생한다. .destory()를 호출해서 워커가 죽을 때도 발생한다.

.disconnect()를 호출하면 disconnectexit 이벤트 사이에 약간의 딜레이가 있을 수 있다. 이 이벤트는 아직 살아있는 연결이 있는지 확인하거나 프로세스가 소거 중인지 확인할 때 유용하다:

cluster.on('disconnect', function(worker) {
  console.log('The worker #' + worker.id + ' has disconnected');
});

Event: 'exit'#

  • worker Worker 객체
  • code Number 워커의 exit 코드
  • signal String 프로세스를 죽게 만든 시그널의 이름(eg. 'SIGHUP')

워커가 죽으면 cluster 모듈에 'exit' 이벤트가 발생한다. 그래서 워커가 죽으면 fork()를 호출해서 워커를 다시 띄울 수 있다.

cluster.on('exit', function(worker, code, signal) {
  var exitCode = worker.process.exitCode;
  console.log('worker ' + worker.process.pid + ' died ('+exitCode+'). restarting...');
  cluster.fork();
});

Event: 'setup'#

  • worker Worker 객체

.setupMaster() 함수를 실행하면 이 이벤트가 발생한다. fork()를 호출하기 전에 .setupMaster()가 한 번도 실행된 적이 없으면 fork()를 실행할 때 아규먼트 없이 .setupMaster()가 한번 호출된다.

cluster.setupMaster([settings])#

  • settings Object
    • exec String 워커 파일의 경로. (Default=__filename)
    • args Array 워커에 넘겨지는 스트링 아규먼트. (Default=process.argv.slice(2))
    • silent Boolean 워커의 output을 부모의 stdio로 보낼지 말지. (Default=false)

setupMaster는 'fork'의 기본 행동을 수정하는데 사용한다. 사실상 새로운 설정은 즉각적이고 영구적이라서 나중에 이를 수정할 수 없다.

예제:

var cluster = require("cluster");
cluster.setupMaster({
  exec : "worker.js",
  args : ["--use", "https"],
  silent : true
});
cluster.fork();

cluster.fork([env])#

  • env Object 자식 프로세스의 환경변수, Key/value
  • return Worker 객체

워커 프로세스를 하나 만든다(spawn). 이 함수는 마스터 프로세스에서만 호출할 수 있다.

cluster.disconnect([callback])#

  • callback Function 모든 워커가 Disconnect되고 핸들러가 닫히면 호출되는 함수

이 메소드를 호출하면 워커가 전부 정상(graceful) 종료한다. 워커가 종료하면서 내부 핸들러도 닫힌다. 그래서 마스터 프로세스는 이벤트를 기다리는 것 없이 정상(graceful) 종료될 수 있다.

콜백을 아규먼트로 넘기면 끝날 때 호출된다.

cluster.worker#

  • Object

현재 워커 객체에 대한 참조. 마스터 프로세스에서는 사용할 수 없다.

var cluster = require('cluster');

if (cluster.isMaster) {
  console.log('I am master');
  cluster.fork();
  cluster.fork();
} else if (cluster.isWorker) {
  console.log('I am worker #' + cluster.worker.id);
}

cluster.workers#

  • Object

살아있는 워커 객체가 저장되는 해쉬로 id필드가 키다. 모든 워커를 쉽게 순회할 수 있다. 이는 마스터 프로세스에서만 사용할 수 있다.

// 모든 워커에 적용한다.
function eachWorker(callback) {
  for (var id in cluster.workers) {
    callback(cluster.workers[id]);
  }
}
eachWorker(function(worker) {
  worker.send('big announcement to all workers');
});

통신으로 워커 레퍼런스를 주고받아야 하는 상황이라면 워커 id를 주고받는 것이 가장 좋다.

socket.on('data', function(id) {
  var worker = cluster.workers[id];
});

Class: Worker#

워커에 대한 Public 정보와 메소드는 워커 객체에 들어 있다. 마스터에서는 cluster.workers로 워커 객체에 접근하고 워커에서는 cluster.worker로 접근한다.

worker.id#

  • String

모든 워커에는 고유한 id가 부여되고 그 값은 id 프로퍼티에 저장된다.

이 id가 clsuter.workers 프로퍼티에서 해당 워커 객체의 인덱스다. 워커가 살아 있는 동안에만 사용할 수 있다.

worker.process#

  • ChildProcess 객체

워커 프로세스는 child_process.fork()로 생성하는 데 이 함수가 리턴한 객체가 process 프로퍼티에 저장된다.

See: Child Process module

worker.suicide#

  • Boolean

.destroy()를 호출하고 나서 해당 워커가 죽으면 true가 할당되고 .disconnect()를 호출하면 즉시 true가 할당된다. 그때까지는 undefined이다.

worker.send(message, [sendHandle])#

  • message Object
  • sendHandle Handle 객체

이 함수는 child_process.fork()로 생기는 send 메소드와 동일하다. 마스터에서 워커에 메시지를 보낼 때는 이 함수로 보내고 워커에서는 process.send(message)로 보내지만, 이 둘은 같은 함수다.

다음은 마스터가 워커에 보낸 매시지를 다시 그대로 리턴하는 echo 예제다:

if (cluster.isMaster) {
  var worker = cluster.fork();
  worker.send('hi there');

} else if (cluster.isWorker) {
  process.on('message', function(msg) {
    process.send(msg);
  });
}

worker.destroy()#

이 함수로 워커가 죽어고 워커를 다시 생성하지 말라고 마스터에게 알릴 수 있다. suicide 프로퍼티를 이용하면 워커가 죽은 게 계획적인지 예외적인지 구분할 수 있다.

cluster.on('exit', function(worker, code, signal) {
  if (worker.suicide === true) {
    console.log('Oh, it was just suicide\' – no need to worry').
  }
});

// 워커를 파괴
worker.destroy();

worker.disconnect()#

이 함수를 호출하면 해당 워커는 더는 연결을 수락하지 않는다(하지만, 다른 워커는 여전히 연결을 수락한다). 이미 맺어진 연결도 종료할 수 있다. 맺어진 연결이 없으면 IPC 연결이 닫히고 워커가 정상적으로(graceful) 죽는다. IPC 채널이 닫히면 disconnect 이벤트가 발생하고 이어서 워커가 죽으면서 exit 이벤트가 발생한다.

바로 끊기지 않는 연결이 있을 수도 있기 때문에 타임아웃을 사용하는 게 좋다. 먼저 워커를 Disconnect시키고 2초 후에 서버를 죽인다(destroy). 2초 후에 server.destroy() 대신 worker.destroy() 메소드를 실행할 수도 있지만, 워커가 충분히 소거하지 못할 수 있다.

if (cluster.isMaster) {
  var worker = cluster.fork();
  var timeout;

  worker.on('listening', function(address) {
    worker.disconnect();
    timeout = setTimeout(function() {
      worker.send('force kill');
    }, 2000);
  });

  worker.on('disconnect', function() {
    clearTimeout(timeout);
  });

} else if (cluster.isWorker) {
  var net = require('net');
  var server = net.createServer(function(socket) {
    // 연결이 끝나지 않는다.
  });

  server.listen(8000);

  server.on('close', function() {
    // 마무리
  });

  process.on('message', function(msg) {
    if (msg === 'force kill') {
      server.destroy();
    }
  });
}

Event: 'message'#

  • message Object

이 이벤트는 child_process.fork()의 것과 같다. 마스터에서 이 이벤트를 사용하고 워커에서는 process.on('message')를 사용한다.

다음은 마스터 프로세스에서 총 요청 수를 세는 예제다. 메시지 시스템을 사용해서 구현한다:

var cluster = require('cluster');
var http = require('http');

if (cluster.isMaster) {

  // HTTP 요청 수를 저장한다.
  var numReqs = 0;
  setInterval(function() {
    console.log("numReqs =", numReqs);
  }, 1000);

  // 요청을 센다.
  function messageHandler(msg) {
    if (msg.cmd && msg.cmd == 'notifyRequest') {
      numReqs += 1;
    }
  }

  // 워커를 생성하고 메시지를 Listen한다.
  var numCPUs = require('os').cpus().length;
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  Object.keys(cluster.workers).forEach(function(id) {
    cluster.workers[id].on('message', messageHandler);
  });

} else {

  // 워커 프로세스에 HTTP 서버가 있다..
  http.Server(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");

    // 마스터에 'notifyRequest' 메시지를 보낸다.
    process.send({ cmd: 'notifyRequest' });
  }).listen(8000);
}

Event: 'online'#

cluster.on('online') 이벤트와 같지만, 해당 워커의 상태가 변경됐을 때만 발생한다.

cluster.fork().on('online', function() {
  // Worker is online
};

Event: 'listening'#

  • address Object

cluster.on('listening') 이벤트와 같지만, 해당 워커의 상태가 변경됐을 때만 발생한다.

cluster.fork().on('listening', function(address) {
  // Worker is listening
};

Event: 'disconnect'#

cluster.on('disconnect') 이벤트와 같지만, 해당 워커의 상태가 변경됐을 때만 발생한다.

cluster.fork().on('disconnect', function() {
  // Worker has disconnected
};

Event: 'exit'#

  • code Number 워커의 exit 코드
  • signal String 프로세스를 죽게 만든 시그널의 이름(eg. 'SIGHUP')

워커 프로세스가 종료할 때 발생한다. 자세한 건 child_process Event: 'exit'을 봐라.

var worker = cluster.fork();
worker.on('exit', function(code, signal) {
  if( signal ) {
    console.log("worker was killed by signal: "+signal);
  } else if( code !== 0 ) {
    console.log("worker exited with error code: "+code);
  } else {
    console.log("worker success!");
  }
};