Node.js v0.10.25 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: 0 - Deprecated
이 기능은 문제의 소지가 있는 것으로 알려졌고 변경될 예정이다.
여기에 의존하지 마라. 이 기능을 사용하면 경고나 나올 것이다.
하위호환성을 기대하지 말아야 한다.
Stability: 1 - Experimental
이 기능은 최근에 도입되었고 차기 버전에서 변경되거나 제거될 것이다.
이 기능을 시험해보고 피드백을 주기 바란다. 이 기능이 당신에게 중요한 유즈케이스를 제공한다면
노드 코어 팀에게 말해줘라.
Stability: 2 - Unstable
API가 안정화되는 과정에 있지만, 아직 안정적이라고 고려될 만큼 충분한
실세계의 테스트를 거치지 않았다. 합당하다면 하위호환성은 유지될 것이다.
Stability: 3 - Stable
API가 충분히 검증되었지만, 기반이 되는 코드의 정리 때문에 마이너한 변경이
있을 수 있다. 하위호환성이 보장된다.
+Stability: 4 - API Frozen
이 API는 프로덕션레벨에서 광범위하게 테스트 되었고
변경될 여지가 거의 없다.
+Stability: 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에 출력하는 데 사용한다. console부분을 봐라.

Class: Buffer#

  • {Function}

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

require()#

  • {Function}

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

require.resolve()#

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

require.cache#

  • Object

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

require.extensions#

Stability: 0 - Deprecated
  • Object

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

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

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

Deprecated 과거에 이 항목은 요청에 따라 컴파일해서 Node에 자바스크립트가 아닌 모듈을 로드하는데 사용했다. 하지만 실제로 다른 몇몇 Node 프로그램을 통해서 모듈을 로딩하거나 ahead of time으로 자바스크립트로 컴파일하는 등의 이를 위한 훨씬 더 좋은 방법이 존재한다.

Module 시스템이 locked 상태이므로 이 기능은 없어지지 않을 것이다. 하지만 미묘한 버그나 건드릴 수 없는 복잡성을 가질 수 있다.

__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.exports는 모듈이 무엇을 외부에 노출해서 require()로 사용할 수 있게 할 것인지 정의하는 데 사용한다.

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

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

exports#

module.exports에 대한 더 간략화된 참조다. 언제 exports를 사용하고 언제 module.exports를 사용하는지에 대한 자세한 내용은 module system documentation를 참고해라.

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 기능은 동기적이고 파이프인 경우(장시간 블락킹하는 것을 피하려고)에는 비동기이다.

즉, 다음 예제에서 stdout은 넌블락킹이고 stderr은 블락킹이다.

$ node script.js 2> error.log | tee info.log

일반으로는 엄청난 양의 데이터를 로깅하지 않는한 블락킹과 넌블락킹을 걱정할 필요가 없다.

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)#

인터벌의 실행을 멈춘다.

unref()#

setTimeoutsetInterval이 반환한 불투명한(opaque) 값도 활성화된 타이머이기는 하지만 이 타이머가 이벤트 루프에서 유일하게 남은 것일 경우에는 프로그램이 동작하도록 유지하지는 못하는 타이머를 생성할 수 있도록 할 timer.unref() 메서드를 가진다. 타이머에 이미 unref를 호출했다면 unref를 다시 호출해도 아무런 영향이 없을 것이다.

setTimeout의 경우 unref하면 이벤트 루프를 깨울 분리된 타이머를 생성한다. 이 타이머를 너무 많이 생성하면 이벤트 루프 성능에 역효과를 줄 수 있다. -- 잘 사용해라.

ref()#

이전에 unref()된 타이머가 있다면 타이머가 가진 프로그램을 열도록 명시적으로 요청하는 ref()를 호출할 수 있다. 타이머에 이미 ref를 호출했다면 ref를 다시 호출해도 아무런 영향이 없을 것이다.

setImmediate(callback, [arg], [...])#

I/O 이벤트 콜백 후와 setTimeoutsetInterval 이전에 callback을 "즉시" 실행하도록 스케줄링한다. clearImmediate()와 사용할 수 있는 immediateId를 반환한다. 선택적으로 콜백에 인자를 전달할 수도 있다.

Immediate는 생성한 순서대로 큐에 들어가고 루프 이터레이션마나 하나씩 큐에서 빠진다. 이 부분이 이터레이션마다 process.maxTickDepth 큐에 있는 콜백을 실행하는 process.nextTick와 다른 점이다. setImmediate는 큐에 있는 콜백을 실행한 후에 I/O가 실행되도록 보장하기 위해 이벤트 루프에 양보할 것이다. 실행하는 동안 순서가 유지되지만 다른 I/O 이벤트는 스케쥴된 두 immediate 콜백사이에서 실행될 것이다.

clearImmediate(immediateId)#

immediate의 실행을 멈춘다.

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()를 익스포트했다. 모듈 루트에 함수와 객체를 추가하려면 exports 객체에 추가하면 된다.

모듈 내의 로컬 변수는 모듈이 함수로 감싸진 것처럼 외부에 노출되지 않는다.(private) 이 예제에서 PI 변수는 circle.js의 private이다.

모듈 전체가 하나의 함수로 익스포트하고 싶거나(생성자처럼) 한 번에 하나의 프로퍼티를 구성하는 대신 한 번의 할당으로 전체 객체를 익스포트하고 싶다면 exports말고 module.exports에 할당해라.

아래 bar.js는 생성자를 익스포트하는 square 모듈을 사용하고 있다.

var square = require('./square.js');
var mySquare = square(2);
console.log('The area of my square is ' + mySquare.area());

square 모듈은 square.js에 정의되어 있다.

// exports에 할당하는 것이 모듈을 수정하지 않는다. 반드시 module.exports를 사용해라.
module.exports = function(width) {
  return {
    area: function() {
      return width * width;
    }
  };
}

이 모듈 시스템은 require("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.jsexports 객체를 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-global)로 편리하게 접근할 수 있다. module은 글로벌 변수가 아니라 모듈마다 다른 객체를 가리키는 로컬 변수다.

module.exports#

  • Object

module.exports 객체는 Module 시스템이 자동으로 만들어 준다. Export하려는 객체를 module.exports에 할당해서 직접 만든 객체가 반환되게 할 수도 있다. 원하는 객체를 exports에 할당하면 원하는 것과는 달리 로컬 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);

exports alias#

모듈 내에서 사용 가능한 exports 변수는 처음에는 module.exports에 대한 참조이다. 일반적인 변수와 마찬가지로 새로운 값을 exports에 할당하면 이전 값에 대한 바인딩을 사라진다.

이 동작을 설명하기 위해 다음의 require()에 대한 가상 구현체를 생각해 보자.

function require(...) {
  // ...
  function (module, exports) {
    // Your module code here
    exports = some_func;        // exports를 재할당한다. exports는 더는 단축키가
                                // 아니고 아무것도 익스포트하지 않는다.
    module.exports = some_func; // 모듈이 0을 익스포트하게 한다.
  } (module, module.exports);
  return module;
}

exportsmodule.exports의 관계가 어렵게 느껴진다면 그냥 exports는 무시하고 module.exports만 사용해라.

module.require(id)#

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

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

이 메소드를 호출하려면 일단 module 객체의 참조를 얻어야 한다. module 객체의 참조는 해당 모듈에서만 접근할 수 있고 require()module이 아니라 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는 실행 가능하도록 모든 의존성을 정적으로 컴파일한다. 모듈을 컴파일할 때 이러한 라이브러리의 연결에 대해서 걱정할 필요가 없다.

아래의 예제는 모두 download에서 다운받을 수 있고 자신만의 애드온을 작성할 때 시작지점으로 사용할 수 있다.

Hello world#

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

module.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> exports) {
  exports->Set(String::NewSymbol("hello"),
      FunctionTemplate::New(Method)->GetFunction());
}
NODE_MODULE(hello, init)

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

void Initialize (Handle<Object> exports);
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> exports) {
  exports->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> exports, Handle<Object> module) {
  module->Set(String::NewSymbol("exports"),
      FunctionTemplate::New(RunCallback)->GetFunction());
}

NODE_MODULE(addon, Init)

이 예제는 전체 module 객체를 두 번째 인자로 받는 두 개의 인자를 갖는 형식의 Init()를 사용한다. 이는 애드온이 exports의 프로퍼티로 함수를 추가하는 대신 하나의 함수로 exports를 완전히 덮어쓸 수 있게 한다.

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

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

addon(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> exports, Handle<Object> module) {
  module->Set(String::NewSymbol("exports"),
      FunctionTemplate::New(CreateObject)->GetFunction());
}

NODE_MODULE(addon, Init)

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

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

var obj1 = addon('hello');
var obj2 = addon('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> exports, Handle<Object> module) {
  module->Set(String::NewSymbol("exports"),
      FunctionTemplate::New(CreateFunction)->GetFunction());
}

NODE_MODULE(addon, Init)

다음과 같이 테스트한다.

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

var fn = addon();
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> exports) {
  MyObject::Init(exports);
}

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> exports);

 private:
  explicit MyObject(double value = 0);
  ~MyObject();

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

#endif

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

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

using namespace v8;

Persistent<Function> MyObject::constructor;

MyObject::MyObject(double value) : value_(value) {
}

MyObject::~MyObject() {
}

void MyObject::Init(Handle<Object> exports) {
  // 생성자 템플릿을 준비한다
  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());
  exports->Set(String::NewSymbol("MyObject"), constructor);
}

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

  if (args.IsConstructCall()) {
    // 생성자로 `new MyObject(...)`가 호출된다
    double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
    MyObject* obj = new MyObject(value);
    obj->Wrap(args.This());
    return args.This();
  } else {
    // 평범한 함수 `MyObject(...)`로 호출되고 생성자 호출로 변환된다.
    const int argc = 1;
    Local<Value> argv[argc] = { args[0] };
    return scope.Close(constructor->NewInstance(argc, argv));
  }
}

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

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

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

다음 코드로 테스트한다.

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> exports, Handle<Object> module) {
  MyObject::Init();

  module->Set(String::NewSymbol("exports"),
      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:
  explicit MyObject(double value = 0);
  ~MyObject();

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

#endif

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

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

using namespace v8;

Persistent<Function> MyObject::constructor;

MyObject::MyObject(double value) : value_(value) {
}

MyObject::~MyObject() {
}

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;

  if (args.IsConstructCall()) {
    // 생성자로 `new MyObject(...)`가 호출된다
    double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
    MyObject* obj = new MyObject(value);
    obj->Wrap(args.This());
    return args.This();
  } else {
    // 평범한 함수 `MyObject(...)`로 호출되고 생성자 호출로 변환된다.
    const int argc = 1;
    Local<Value> argv[argc] = { args[0] };
    return scope.Close(constructor->NewInstance(argc, argv));
  }
}

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->value_ += 1;

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

다음으로 테스트한다.

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

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

var obj2 = 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->Value() + obj2->Value();
  return scope.Close(Number::New(sum));
}

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

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

  exports->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 Value() const { return value_; }

 private:
  explicit MyObject(double value = 0);
  ~MyObject();

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

#endif

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

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

using namespace v8;

Persistent<Function> MyObject::constructor;

MyObject::MyObject(double value) : value_(value) {
}

MyObject::~MyObject() {
}

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;

  if (args.IsConstructCall()) {
    // 생성자로 `new MyObject(...)`가 호출된다
    double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
    MyObject* obj = new MyObject(value);
    obj->Wrap(args.This());
    return args.This();
  } else {
    // 평범한 함수 `MyObject(...)`로 호출되고 생성자 호출로 변환된다.
    const int argc = 1;
    Local<Value> argv[argc] = { args[0] };
    return scope.Close(constructor->NewInstance(argc, argv));
  }
}

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'#

프로세스가 종료될 때 발생한다. 이 때 이벤트 루프의 종료를 막을 수 있는 방법은 없고 일단 exit 리스너들이 모두 종료되면 해당 프로세스는 종료될 것이다. 그러므로 이 핸들러에서 동기적인 작업만을 반드시 수행해야 한다. 이 이벤트는 모듈의 상태를 상수시간으로 확인하기에 좋은 지점이다.(유닛테스트에서처럼) 콜백은 프로세스가 종료될 때의 코드를 인자로 가진다.

exit이벤트 예제:

process.on('exit', function(code) {
  // 이렇게 하지 *말아라*
  setTimeout(function() {
    console.log('This will not run');
  }, 0);
  console.log('About to exit with code:', code);
});

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, SIGHUP와 같은 표준 POSIX 신호이름의 목록은 sigaction(2)를 봐라.

SIGINT 예제:

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

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

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

Note:

  • SIGUSR1는 디버거를 시작하기 위해 node.js에 예약되어 있는 신호다. 리스터를 등록할 수는 있지만 시작할 때 디버거를 멈추지는 않을 것이다.
  • SIGTERMSIGINT128 + signal number 코드로 종료하기 전에 터미널 모드를 리셋하는 비윈도우 계열의 플랫폼에서 기본 핸들러들을 가진다. 이러한 신호 중 하나에 등록된 리스너가 있다면 해당 기본 동작은 제거될 것이다.(node가 종료하지 않을 것이다.)
  • SIGPIPE는 기본적으로 무시하지만 리스너를 등록할 수 있다.
  • SIGHUP는 윈도우즈에서는 콘솔창을 닫을 때 생성되는 신호고 다른 플랫폼에서는 다양한 유사상황(signal(7) 참고)에서 생성된다. 리스너를 등록할 수 있지만 윈도우즈가 약 10초후에 무조건 node를 종료할 것이고 비위도우즈 계열에서는 SIGHUP의 기본 동작이 node를 종료하는 것이지만 리스너를 등록했다면 기본 동작은 제거될 것이다.
  • SIGTERM는 윈도우즈는 지원하지 않고 리스너를 등록할 수는 있다.
  • 터미널에서 SIGINT 신호는 모든 플랫폼에서 지원하고 보통 CTRL+C로(설정할 수는 있지만) 생성할 수 있는 신호다. 터미널의 raw 모드가 활성화 되어 있을 때는 이 신호가 만들어지지 않는다.
  • SIGBREAK는 윈도우즈에서 CTRL+BREAK를 눌렀을 때 전달되는 신호다. 비윈도우즈 계열의 플렛폼에서는 리스너를 등록할 수 있지만 이 코드를 보내거나 생성할 수 있는 방법이 없다.
  • SIGWINCH는 콘솔의 크기를 변경했을 때 전달된다. 윈도우즈에서는 콘설에 작성을 해서 커서를 움직이거나 raw 모드에서 읽기가 가능한 tty를 사용했을 때만 발생할 것이다.
  • SIGKILL에는 리스너를 등록할 수 없고 모든 플랫폼이 무조건 node를 종료할 것이다.
  • SIGSTOP에는 리스너를 등록할 수 없다.

윈도우즈는 신호 전송을 지원하지 않지만 node가 process.kill()child_process.kill()를 에뮤레이팅해서 제공한다. - 프로세스의 존재를 검색하기 위해 신호 0을 보낼 수 있다. - SIGINT, SIGTERM, SIGKILL신호를 보내면 해당 프로세스가 무조건 종료된다.

process.stdout#

stdout에 대한 Writable Stream이다.

예제: console.log의 정의

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

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

Node가 TTY 컨텐스트로 실행되는지 확인하려면 process.stderr, process.stdout, process.stdinisTTY 프로퍼티를 읽어라.

$ node -p "Boolean(process.stdin.isTTY)"
true
$ echo "foo" | node -p "Boolean(process.stdin.isTTY)"
false

$ node -p "Boolean(process.stdout.isTTY)"
true
$ node -p "Boolean(process.stdout.isTTY)" | cat
false

자세한 내용은 the tty docs를 참고해라.

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.execArgv#

이 값은 프로세스를 실행할 때 사용한 노드의 커맨드라인 옵션의 세트다. 이 옵션은 process.argv에서는 보이지 않고 node 실행명령어, 스크립트 명, 스크립트 명 뒤에 오는 옵션들은 포함하지 않는다. 이 옵션은 부모와 같은 실행환경으로 자식 프로세스를 생성할때 유용하다.

예제:

$ node --harmony script.js --version

process.execArgv는 다음 값이 된다.

['--harmony']

process.argv는 다음과 같다.

['/usr/local/bin/node', 'script.js', '--version']

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.getgroups()#

Note: 이 함수는 POSIX 플랫폼에서만 사용할 수 있다.(윈도우즈에선 사용할 수 없다.)

추가적인 그룹 ID를 가진 배열을 반환한다. POSIX는 유효한 그룹 ID를 포함하고 있는지 지정하지 않지만 node.js를 이를 항상 보장한다.

process.setgroups(groups)#

Note: 이 함수는 POSIX 플랫폼에서만 사용할 수 있다.(윈도우즈에선 사용할 수 없다.)

추가적인 그룹 ID를 설정한다. 이는 권한힌 필요한 작업이다. 즉, root이거나 CAP_SETGID 권한을 가져야 한다.

목록은 그룹 ID, 그룹명 또는 둘 다를 포함할 수 있다.

process.initgroups(user, extra_group)#

Note: 이 함수는 POSIX 플랫폼에서만 사용할 수 있다.(윈도우즈에선 사용할 수 없다.)

/etc/group를 읽고 사용자가 멤버로 있는 모든 그룹을 사용해서 그룹 접근 목록을 초기화한다. 이는 권한이 필요한 작업이므로 root이거나 CAP_SETGID 권한을 가져야 한다.

user는 사용자명이거나 사용자 ID이다. extra_group는 그룹명이나 그룹ID이다.

권한을 버릴때는 조심할 필요가 있다. 예제:

console.log(process.getgroups());         // [ 0 ]
process.initgroups('bnoordhuis', 1000);   // switch user
console.log(process.getgroups());         // [ 27, 30, 46, 1000, 0 ]
process.setgid(1000);                     // drop root gid
console.log(process.getgroups());         // [ 27, 30, 46, 1000 ]

process.version#

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

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

process.versions#

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

console.log(process.versions);

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

{ http_parser: '1.0',
  node: '0.10.4',
  v8: '3.14.5.8',
  ares: '1.9.0-DEV',
  uv: '0.10.3',
  zlib: '1.2.3',
  modules: '11',
  openssl: '1.0.1e' }

process.config#

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

출력은 다음 예제와 같다.

{ target_defaults:
   { cflags: [],
     default_configuration: 'Release',
     defines: [],
     include_dirs: [],
     libraries: [] },
  variables:
   { host_arch: 'x64',
     node_install_npm: 'true',
     node_prefix: '',
     node_shared_cares: 'false',
     node_shared_http_parser: 'false',
     node_shared_libuv: 'false',
     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'나 'SIGHUP'같은 문자열이다. signal을 생략하면 'SIGTERM'가 될 것이다. 더 자세한 내용은 Signal Events와 kill(2)를 봐라.

대상이 존재하지 않으면 오류를 던질 것이다. 예외적으로 신호 0 을 해당 프로세스의 존재를 검사하는데 사용할 수 있다.

이 함수의 이름이 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');

Note: Node.js가 SIGUSR1를 받으면 디버거를 시작한다. Signal Events를 참고해라.

process.pid#

프로세스의 PID.

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

process.title#

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

setter로 사용하는 경우 최대 길이는 플랫폼에 의존적이고 아마도 길지 않을 것이다.

리눅스나 OS X에서는 argv 메모리를 덮어쓰기 때문에 바이너리 명의 크기에 명령행 인자의 길이를 더한 크기로 제한될 것이다.

v0.8은 environ 메모리를 덮어써서 프로세스 타이틀 문자열을 더 길게 처리할 수 있지만 몇몇 (명확하지 않은)경우에는 잠재적으로 보안에 취약하거나 혼란스러울 수 있다.

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)에 대한 별칭이 아니라 훨씬 더 효율적이다. 이는 보통 어떤 I/O 이벤트도 발생하기 전에 실행되지만 몇 가지 예외가 존재한다. 아래의 process.maxTickDepth를 참고해라.

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

이는 객체가 만들어진 후이지만 어떤 I/O도 발생하기 전에 사용자가 이벤트 핸들러를 할당할 기회를 주고자 하는 API를 개발할 때 중요하다.

function MyThing(options) {
  this.setupOptions(options);

  process.nextTick(function() {
    this.startDoingStuff();
  }.bind(this));
}

var thing = new MyThing();
thing.getReadyForStuff();

// thing.startDoingStuff()는 이제 호출된다.(그 이전이 아니라)

이는 100% 동기이거나 100% 비동기여야 하는 API에 아주 중요하다. 다음 예제를 봐라.

// WARNING!  DO NOT USE!  BAD UNSAFE HAZARD!
function maybeSync(arg, cb) {
  if (arg) {
    cb();
    return;
  }

  fs.stat('file', cb);
}

이 API는 위험하다. 다음과 같이 사용한다면

maybeSync(true, function() {
  foo();
});
bar();

foo()bar() 중 어느 쪽이 먼저 호출될 것인지 명확하지 않다.

다음 접근방법이 훨씬 좋다.

function definitelyAsync(arg, cb) {
  if (arg) {
    process.nextTick(cb);
    return;
  }

  fs.stat('file', cb);
}

process.maxTickDepth#

  • Number Default = 1000

process.nextTick에 전달되는 콜백은 보통 현재 실행 흐름의 끝에 호출될 것이므로 대략 함수를 동기적으로 호출한 것만큼 빠르게 호출된다. 확인하지 않은 채로 놔두면 이는 어떤 I/O의 발생도 발생하지 못하게 막으면서 이벤트 루프가 굶주리게(starve) 할 것이다.

다음 코드를 보자.

process.nextTick(function foo() {
  process.nextTick(foo);
});

nextTick의 재귀 호출로 인한 무한루프가 Node를 블록 하는 상황을 피하려면 매번 일부 I/O가 수행되도록 지연시켜야 한다.

process.maxTickDepth 값은 다른 I/O가 일어나도록 하기 전에 계산할 nextTick이 호출하는 nextTick 콜백의 최대 깊이이다.

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, 25 ]

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

  console.log('benchmark took %d nanoseconds', diff[0] * 1e9 + diff[1]);
  // benchmark took 1000000527 nanoseconds
}, 1000);

util#

Stability: 4 - API Frozen

이 함수들은 '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, [options])#

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

포매팅 된 문자열의 형식을 바꾸기 위해서 선택적인 options 객체를 전달한다.

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

  • depth - 객체를 포매팅할 때 inspect가 몇 번 재귀를 할 것인지를 지정한다. 이 값은 크고 복잡한 객체를 검사할 때 유용하다. 기본값은 2이다. 무한으로 재귀하려면 null를 전달해라.

  • colors - true이면 ANSI 색상코드로 스타일을 입혀서 출력한다. 기본값은 false이다. 색상은 커스터마이징 할 수 있는데 자세한 내용은 하단을 참고해라.

  • customInspect - false이면 검사하는 객체에 정의된 커스텀 inspect() 함수를 호출하지 않는다. 기본값은 true이다.

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

var util = require('util');

console.log(util.inspect(util, { showHidden: true, depth: null }));

Customizing util.inspect colors#

util.inspect의 칼라 출력은 util.inspect.stylesutil.inspect.colors 객체로 전역적으로 커스터마이징할 수 있다.

util.inspect.stylesutil.inspect.colors에서 각 색상 스타일을 할당하는 맵이다. 강조된 스타일과 그 기본값은 다음과 같다. number (yellow) boolean (yellow) string (green) date (magenta) regexp (red) null (bold) undefined (grey) special - 여기서 유일한 함수 (cyan) * name (의도적으로 스타일이 없다.)

미리 정의된 색상 코드는 white, grey, black, blue, cyan, green, magenta, red, yellow이다. bold, italic, underline, inverse도 있다.

객체들도 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!"); // 받은 데이터: "It works!"

Events#

Stability: 4 - API Frozen

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

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

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

Class: events.EventEmitter#

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

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

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

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.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] ]

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

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

이벤트가 리스너를 가지고 있으면 true을 반환하고 가지고 있지 않으면 false를 반환한다.

Class Method: EventEmitter.listenerCount(emitter, event)#

해당 이벤트의 리스너 개수를 반환한다.

Event: 'newListener'#

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

이 이벤트를 새로운 리스너가 어딘가에 추가될 때마다 발생한다. emitter.listeners(event)가 반환한 리스트에 listener있는지는 알려주지 않는다.

Event: 'removeListener'#

  • event String The event name
  • listener Function The event handler function

이 이벤트는 리스너를 제거할 때마다 발생한다. emitter.listeners(event)가 반환한 리스트에 listener있는지는 알려주지 않는다.

Domain#

Stability: 2 - Unstable

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

Warning: Don't Ignore Errors!#

도메인 오류 핸들러는 오류 발생 시 프로세스를 종료하는 것을 대체하지는 않는다.

JavaScript에서 throw 동작방식의 기본적인 특성으로 인해 참조를 누출하거나 undefined같은 류의 깨지기 쉬운 상태를 생성하지 않은 채로 안전하게 "벗어난 곳이 어디인지를 찾아내는" 방법이 거의 없다.

오류가 던져졌을 때 가장 안정한 반응은 프로세스를 종료하는 것이다. 물론 일반적인 웹서버에서 다수의 연결을 얼어놓고 있을 것인데 어디선가 오류가 발생해서 갑자기 종료시켜버리는 것은 합당하지는 않다.

더 좋은 접근방법은 오류를 발생 시킨 요청에 오류 응답을 보내면서 다른 요청들은 정상적으로 종료해도 해당 워커는 새로운 요청을 받아들이지 않도록 하는 것이다.

이 접근에서 워커가 오류를 만났을 때 마스터 프로세스가 새로운 워커를 만들 수 있으므로 domain은 클러스터 모듈과 친하다. 다중 머신으로 확장하는 node 프로그램에서 프록시나 서비스 등록을 종료하는 것은 실패를 관리하고 적절하게 대응할 수 있다.

예를 들어 다음은 좋은 생각이 아니다.

// XXX WARNING!  BAD IDEA!

var d = require('domain').create();
d.on('error', function(er) {
  // 오류는 프로세스를 깨뜨리지는 않지만, 오류가 하는하는 일은 더 좋지 않다!
  // 갑작스러운 프로세스의 재시작을 막더라도 이런 일이 발생하면 리소스를 노출하게 된다.
  // 이는 process.on('uncaughtException')보다 나을 게 없다!
  console.log('error, but oh well', er.message);
});
d.run(function() {
  require('http').createServer(function(req, res) {
    handleRequest(req, res);
  }).listen(PORT);
});

도메인의 컨텍스트와 프로그램을 다중 워커 프로세스로 분리하는 강력함을 사용해서 적절하게 대응하고 훨씬 더 안전하게 오류를 다룰 수 있다.

// 훨씬 좋다!

var cluster = require('cluster');
var PORT = +process.env.PORT || 1337;

if (cluster.isMaster) {
  // 실환경에서는 2개 이상의 워커를 사용할 것이고 마스터와 워커를 같은 파일에
  // 두지 않을 것이다.
  //
  // 물론 DoS 공격이나 다른 공격행위를 차단하는 데 필요한 커스텀 로직을 구현하고
  // 로깅을 더 깔끔하게 할 수도 있다.
  //
  // 클러스터 문서의 옵션을 참고해라.
  //
  // 중요한 점은 의도치 않은 오류의 복구능력을 높이면서 마스터가 하는 일은
  // 아주 적다는 것이다.

  cluster.fork();
  cluster.fork();

  cluster.on('disconnect', function(worker) {
    console.error('disconnect!');
    cluster.fork();
  });

} else {
  // 워커
  //
  // 버그를 넣을 곳이다!

  var domain = require('domain');

  // 요청을 처리하는 워커 프로세스를 사용하는 자세한 방법은 클러스터 문서를 참고해라.
  // 동작하는 방법이나 경고 등등

  var server = require('http').createServer(function(req, res) {
    var d = domain.create();
    d.on('error', function(er) {
      console.error('error', er.stack);

      // Note: 위험지역이다.
      // 당연히 원치 않은 일이 일어난다.
      // 이제 어떤 일이든 발생할 수 있다! 조심해라!

      try {
        // 30초 이내에 종료되었는지 확인한다
        var killtimer = setTimeout(function() {
          process.exit(1);
        }, 30000);
        // 하지만 이를 위해 프로세스를 열어둔 채 유지하지 마라!
        killtimer.unref();

        // 새로운 요청을 받아들이는 것을 멈춘다.
        server.close();

        // 워커가 죽은 것을 마스터가 알도록 한다. 이는 클러스터 마스터에 'disconnect'를
        // 발생시키고 마스터가 새로운 워커를 포크 할 것이다.
        cluster.worker.disconnect();

        // 문제를 일으킨 요청에 오류전송을 시도한다.
        res.statusCode = 500;
        res.setHeader('content-type', 'text/plain');
        res.end('Oops, there was a problem!\n');
      } catch (er2) {
        // 여기서 할 수 있는 일은 많지 않다.
        console.error('Error sending 500!', er2.stack);
      }
    });

    // req와 res는 이 도메인이 존재하기 전에 생성되므로 명시적으로 req와 res를
    // 추가해야 한다. 아래의 암시적/명시적 바인딩의 설명을 참고해라.
    d.add(req);
    d.add(res);

    // 이제 도메인에서 핸들러 함수를 실행한다.
    d.run(function() {
      handleRequest(req, res);
    });
  });
  server.listen(PORT);
}

// 이 부분은 중요하지는 않다. 그냥 라우팅 예제일 뿐이다.
// 세련된 애플리케이션 로직을 여기에 둘 수 있다.
function handleRequest(req, res) {
  switch(req.url) {
    case '/error':
      // 비동기 작업을 수행하고...
      setTimeout(function() {
        // Whoops!
        flerb.bark();
      });
      break;
    default:
      res.end('ok');
  }
}

Additions to Error objects#

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

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

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.');
      } catch (er) {
        console.error('Error sending 500', er, req.url);
      }
    });
  }).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.enter()#

enter 메서드는 활성화된 도메인을 설정하기 위해서 run, bind, intercept 메서드가 사용하는 배관시설이라고 볼 수 있다. enterdomain.activeprocess.domain를 도메인에 설정하고 암묵적으로 도메인모듈이 관리하는 도메인 스택에 도메인을 추가한다.(도메인 스택의 자세한 내용은 domain.exit()을 봐라.) enter를 호출하면 도메인에 바인딩 된 비동기 호출과 I/O 작업 체인의 시작 부분으로 경계를 설정한다.

enter 호출은 활성화된 도메인만 변경하고 도메인 자체는 바꾸지 않는다. 하나의 도메인에서 enterexit를 여러 번 호출할 수 있다.

enter가 호출된 도메인이 이미 폐기되었으면 enter는 도메인을 설정하지 않고 끝날 것이다.

domain.exit()#

exit 메서드는 현재 도메인을 종료하고 도메인 스택에서 현재 도메인을 제거한다. 비동기 호출의 다른 체인의 컨텍스트로 바꿀 때마다 현재 도메인이 종료되었음을 보장하는 것은 중요하다. exit 호출은 도메인에 바인딩 된 비동기 호출과 I/O 작업의 체인의 끝이나 체인을 중단하는 것으로 경계를 설정한다.

현재 실행 컨텍스트에 여러 도메인이 중첩되어 있다면 exit는 해당 도메인 내에 중첩된 모든 도메인을 종료할 것이다.

exit를 호출하면 활성화된 도메인만 변경하고 도메인 자체는 변경하지 않는다. 하나의 도메인에서 enterexit를 여러 번 호출할 수 있다.

exit가 호출된 도메인이 이미 폐기되었으면 exit는 도메인을 종료하지 않고 끝날 것이다.

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)과도 사용할 수 있다. 이때 버퍼 객체는 타입 있는 배열을 지원하는 저장소로 사용하는 ArrayBuffer로 복제(clone)된다. 버퍼의 메모리와 ArrayBuffer는 공유되지 않는다.

NOTE: Node.js v0.8은 복제하는 대신 단순히 array.buffer안에 버퍼에 대한 참조를 유지한다.

더 효율적이지만 타입 있는 배열(typed array) 스펙과 미세하게 호환되지 않는다. ArrayBuffer#slice()가 slice의 복사본을 만드는 반면 Buffer#slice()는 뷰(view)를 생성한다.

Class: Buffer#

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

new Buffer(size)#

  • size 숫자

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

new Buffer(array)#

  • array 배열

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

new Buffer(str, [encoding])#

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

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

Class Method: Buffer.isEncoding(encoding)#

  • encoding 문자열 테스트할 인코딩 문자열

encoding이 유효한 인코딩 인자라면 true를 반환하고 유효한 인코딩 인자가 아니면 false를 반환한다.

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.toJSON()#

Buffer 인스턴스의 JSON 표현을 반환하고 이 JSON 표현은 JSON 배열을 출력한 것과 같다. Buffer 인스턴스를 문자열화했을 때 JSON.stringify가 암묵적으로 이 함수를 호출한다.

예제:

var buf = new Buffer('test');
var json = JSON.stringify(buf);

console.log(json);
// '[116,101,115,116]'

var copy = new Buffer(JSON.parse(json));

console.log(copy);
// <Buffer 74 65 73 74>

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이다.

undefined/NaN이나 범위를 벗어난(out of bound) 값을 전달하면 각각의 기본값을 설정한 것과 같다.

예제: 두 버퍼를 만들고 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 서버에 대한 요청stdout과 같은 스트림이다. 스트림은 읽을수 있거나 쓸 수 있고 때로는 둘 다 가능하다. 모든 스트림은 EventEmitter의 인스턴스다.

require('stream')을 사용해서 기반 Stream 클래스를 로드할 수 있다. Readable 스트림, Writable 스트림, Duplex 스트림, Transform 스트림을 위한 기반 클래스들이 존재한다.

이 문서는 세 부분으로 나뉜다. 첫번째 부분은 프로그램에서 스트림을 사용하려면 알아야 하는 API를 설명한다. 스트리밍 API를 직접 구현하지 않는다면 이 부분을 건너띌 수 있다.

두번째 부분은 커스텀 스트림을 구현한다면 사용해야 하는 API을 설명한다. API는 커스텀 스트림을 쉽게 만들 수 있도록 설계되었다.

세번째 부분은 스트림이 어떻게 동작하는지 자세히 살펴보고 여기서 내부 메카니즘과 수정하는 부분을 확실히 알지 못한다면 수정하지 않아야 하는 함수를 설명한다.

API for Stream Consumers#

스트림은 Readable, Writable이 될 수 있고 둘 다(Duplex) 가능할 수도 있다.

모든 스트림은 EventEmitter지만 추가적인 커스텀 메서드들과 프로퍼티들이 있고 이는 Readable, Writable, Duplex냐에 따라 약간씩 다르다.

스트림이 Readable이면서 Writable이면 아래 나와 있는 모든 메서드와 이벤트를 구현한다. 그러므로 DuplexTransform도 이 API에서 완전히 설명하고 있지만 이 둘의 구현은 약간 다르다.

프로그램에서 스트림을 사용하려고 Stream 인터페이스를 구현할 필요는 없다. 프로그램에서 스트리밍 인터페이스를 구현한다면 아래의 API for Stream Implementors도 참고해라.

대부분의 Node 프로그램(얼마나 간단하냐에 상관없이)은 어떤 방법으로든 Stream을 사용한다. 다음은 Node 프로그램에서 Stream을 사용하는 예제이다.

var http = require('http');

var server = http.createServer(function (req, res) {
  // req는 Readable 스트림인 http.IncomingMessage 이다.
  // res는 Writable 스트림인 http.ServerResponse 이다.

  var body = '';
  // utf8 문자열로 데이터를 받기 원한다.
  // 인코딩을 설정하지 않으면 Buffer 객체를 받을 것이다.
  req.setEncoding('utf8');

  // 리스너를 추가했으면 Readable 스트림은 'data' 이벤트를 발생시킨다.
  req.on('data', function (chunk) {
    body += chunk;
  })

  // end 이벤트는 바디 전체를 받았다고 알려준다.
  req.on('end', function () {
    try {
      var data = JSON.parse(body);
    } catch (er) {
      // json 형식이 잘못됐다.
      res.statusCode = 400;
      return res.end('error: ' + er.message);
    }

    // 사용자에게 데이터를 작성해서 돌려준다.:
    res.write(typeof data);
    res.end();
  })
})

server.listen(1337);

// $ curl localhost:1337 -d '{}'
// object
// $ curl localhost:1337 -d '"foo"'
// string
// $ curl localhost:1337 -d 'not json'
// error: Unexpected token o

Class: stream.Readable#

Readable 스트림 인터페이스는 데이터를 읽어 오는 소스에 대한 추상화이다. 다시 말하면 Readable 스트림에서 데이터가 온다.

Readable 스트림은 데이터를 받을 준비가 되었다고 알려줄 때까지 데이터를 보내지 않는다.

Readable 스트림에는 flowing 모드non-flowing 모드 두 가지 "모드"가 있다. flowing mode에서는 기반시스템에서 데이터를 읽어와서 가능한 한 빨리 프로그램에 제공한다. non-flowing 모드에서 데이터 청크를 받으려면 stream.read()를 명시적으로 호출해야 한다.

readable 스트림의 예제에는 다음이 포함되어 있다.

Event: 'readable'#

스트림에서 데이터의 청크를 읽을 수 있을 때 'readable' 이벤트를 발생시킬 것이다.

몇몇 경우에서는 'readable' 이벤트를 처리할 때 데이터가 기존에 없다면 데이터를 의존 시스템에서 내부 버퍼로 읽도록 할 것이다.

var readable = getReadableStreamSomehow();
readable.on('readable', function() {
  // 이제 읽을 데이터가 있다.
})

내부 버퍼가 비워지면 추가적인 데이터가 있을 때 readable 이벤트를 다시 발생시킨다.

Event: 'data'#

  • chunk Buffer | String 데이터의 청크.

data 이벤트 리스너를 추가하면 스트림이 flowing 모드로 바뀌고 데이터를 사용할 수 있게 되면 바로 핸들러로 전달할 것이다.

가능한 한 빨리 스트림에서 데이터를 모두 받으려면 다음과 같이 하는 것이 가장 좋은 방법이다.

var readable = getReadableStreamSomehow();
readable.on('data', function(chunk) {
  console.log('got %d bytes of data', chunk.length);
})

Event: 'end'#

더는 데이터가 없으면 이 이벤트가 발생한다.

데이터를 완전히 소비하지 않는 한 end 이벤트는 발생하지 않는다. 이는 flowing 모드로 바꾸거나 끝까지 반복해서 read()를 호출해서 데이터를 모두 소비할 수 있다.

var readable = getReadableStreamSomehow();
readable.on('data', function(chunk) {
  console.log('got %d bytes of data', chunk.length);
})
readable.on('end', function() {
  console.log('there will be no more data.');
});

Event: 'close'#

의존 리소스(예를 들어 기반(backing) 파일 디스크립터)가 닫혔을 때 발생한다. 모든 스트림은 이 이벤트를 발생할 것이다.

Event: 'error'#

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

readable.read([size])#

  • size Number 읽을 데이터의 양을 지정하기 위한 선택적인 인자.
  • Return String | Buffer | null

read() 메서드는 내부 버퍼에서 데이터를 가져와서 반환한다. 데이터가 없다면 null을 반환할 것이다.

size 인자를 전달하면 size 만큼의 바이트를 반환할 것이다. size 만큼의 데이터가 없다면 null을 반환할 것이다.

size 인자를 지정하지 않으면 내부 버퍼의 데이터 모두를 반환할 것이다.

이 메서드는 non-flowing 모드에서만 호출해야 한다. flowing 모드에서 이 메서드는 내부 버퍼가 비워질 때까지 자동으로 호출된다.

var readable = getReadableStreamSomehow();
readable.on('readable', function() {
  var chunk;
  while (null !== (chunk = readable.read())) {
    console.log('got %d bytes of data', chunk.length);
  }
});

readable.setEncoding(encoding)#

  • encoding String 사용할 인코딩.

이 함수를 호출하면 스트림이 Buffer 객체 대신 지정한 인코딩의 문자열을 반환하도록 한다. 예를 들어, readable.setEncoding('utf8')를 실행하면 출력데이터를 UTF-8로 인터프리팅해서 문자열을 반환한다. readable.setEncoding('hex')를 실행하면 데이터를 16진수 문자열 형식으로 인코딩할 것이다.

이 함수는 버퍼를 가져와서 그냥 buf.toString(encoding)를 실행하는 경우 엉망이 될 가능성이 있는 멀티바이트 문자를 잘 다룬다. 문자열로 데이터를 읽으려면 이 메서드를 항상 사용해라.

var readable = getReadableStreamSomehow();
readable.setEncoding('utf8');
readable.on('data', function(chunk) {
  assert.equal(typeof chunk, 'string');
  console.log('got %d characters of string data', chunk.length);
})

readable.resume()#

이 메서드는 readable 스트림에서 data 이벤트가 다시 발생할 수 있게 한다.

이 메서드는 스트림을 flowing-mode로 바꿀 것이다. 스트림에서 데이터를 소비하기를 원치 않지만 end 이벤트는 받기를 원한다면 데이터의 흐름(flow)를 여는 readable.resume()를 호출할 수 있다.

var readable = getReadableStreamSomehow();
readable.resume();
readable.on('end', function(chunk) {
  console.log('got to the end, but did not read anything');
})

readable.pause()#

이 메서드는 flowing-mode의 스트림이 data 이벤트 발생을 멈추도록 할 것이다. 사용 가능한 모든 데이터는 내부 버퍼에 남아있을 것이다.

이 메서드는 flowing mode에서만 유효한 메서드이다. non-flowing 스트림에서 호출한 경우 flowing mode로 바꾸고 멈춘 채로 유지할 것이다.

var readable = getReadableStreamSomehow();
readable.on('data', function(chunk) {
  console.log('got %d bytes of data', chunk.length);
  readable.pause();
  console.log('there will be no more data for 1 second');
  setTimeout(function() {
    console.log('now data will start flowing again');
    readable.resume();
  }, 1000);
})

readable.pipe(destination, [options])#

  • destination Writable Stream 데이터를 쓰는 목적지
  • options Object Pipe 옵션
    • end Boolean reader가 종료되면 writer도 종료한다. 기본값은 true이다.

이 메서드는 readable 스트림에서 오는 데이터를 모두 가져오고 제공된 목적지에 데이터를 작성하고 빠른 readable 스트림이 목적지를 장악하지 않도록 흐름을 자동으로 관리한다.

안전을 위해서 여러 목적지를 파이프로 연결할 수 있다.

var readable = getReadableStreamSomehow();
var writable = fs.createWriteStream('file.txt');
// readable에서 오는 모든 데이터는 'file.txt'로 간다
readable.pipe(writable);

이 함수는 목적지 스트림을 반환하므로 다음과 같이 파이프 체인을 설정할 수 있다.

var r = fs.createReadStream('file.txt');
var z = zlib.createGzip();
var w = fs.createWriteStream('file.txt.gz');
r.pipe(z).pipe(w);

예를 들면 다음과 같이 Unix cat 명령어를 에뮬레이팅할 수 있다.

process.stdin.pipe(process.stdout);

기본적으로 소스 스트림이 end를 발생시켰을 때 목적지에서 end()가 호출되므로 destination는 더는 writable이 아니다. 목적지 스트림을 열어둔 채로 두려면 options으로 { end: false }를 전달해라.

이 코드는 마지막에 "Goodbye"를 쓸 수 있게 writer를 열어둔 채로 둔다.

reader.pipe(writer, { end: false });
reader.on('end', function() {
  writer.end('Goodbye\n');
});

process.stderrprocess.stdout는 옵션에 상관없이 프로세스가 종료되기 전에는 절대 닫히지 않는다.

readable.unpipe([destination])#

  • destination Writable Stream 파이프를 해제할 스트림(선택적인 값)

이 메서드는 이전에 호출한 pipe()를 설정하는 훅을 제거할 것이다.

목적지를 지정하지 않으면 모든 파이프를 제거한다.

목적지를 지정했지만, 목적지에 파이프가 설정되어 있지 않으면 아무런 동작도 일어나지 않는다.

var readable = getReadableStreamSomehow();
var writable = fs.createWriteStream('file.txt');
// readable에서 오는 모든 데이터를 딱 1초 동안만
// 'file.txt'로 보낸다.
readable.pipe(writable);
setTimeout(function() {
  console.log('stop writing to file.txt');
  readable.unpipe(writable);
  console.log('manually close the file stream');
  writable.end();
}, 1000);

readable.unshift(chunk)#

  • chunk Buffer | String 읽기 큐에서 언쉬프트할 데이터의 청크

이 함수는 소스에서 데이터를 가져와서 일부 데이터는 "소비하지 않아야"하고 스트림을 다른 어딘가로 전달할 수 있는 파서가 스트림을 사용하는 등의 특수한 경우에 유용하다.

프로그램에서 stream.unshift(chunk)를 호출해야만 한다면 Transform 스트림을 구현하는 걸 고려해 봐라. (아래 API for Stream Implementors 참고.)

// \n\n를 경계로 헤더를 분리한다.
// 너무 많이 받았다면 unshift()를 사용해라.
// (error, header, stream)로 callback을 호출한다.
var StringDecoder = require('string_decoder').StringDecoder;
function parseHeader(stream, callback) {
  stream.on('error', callback);
  stream.on('readable', onReadable);
  var decoder = new StringDecoder('utf8');
  var header = '';
  function onReadable() {
    var chunk;
    while (null !== (chunk = stream.read())) {
      var str = decoder.write(chunk);
      if (str.match(/\n\n/)) {
        // 헤더 경계를 찾았다
        var split = str.split(/\n\n/);
        header += split.shift();
        var remaining = split.join('\n\n');
        var buf = new Buffer(remaining, 'utf8');
        if (buf.length)
          stream.unshift(buf);
        stream.removeListener('error', callback);
        stream.removeListener('readable', onReadable);
        // 이제 메시지의 바디를 스트림에서 읽을 수 있다.
        callback(null, header, stream);
      } else {
        // 아직 헤더를 읽는 중이다
        header += str;
      }
    }
  }
}

readable.wrap(stream)#

  • stream Stream "예전 방식의" readable 스트림

Node v0.10 이전 버전의 스트림은 지금의 스트림 API 전체를 구현하지 않았다. 자세한 내용은 아래 "Compatibility"를 참고해라.)

'data' 이벤트를 발생시키고 경고(advisory) 전용인 pause() 메서드를 가지는 과거 버전의 Node 라이브러리를 사용하고 있다면 데이터 소스로 과거의 스트림을 사용하는 Readable 스트림을 생성하기 위해 wrap() 메서드를 사용할 수 있다.

이 함수를 호출해야 하는 경우는 극히 드물 것이지만 오래된 Node 프로그램과 라이브러리와 상호작용이

예를 들면 다음과 같다.

var OldReader = require('./old-api-module.js').OldReader;
var oreader = new OldReader;
var Readable = require('stream').Readable;
var myReader = new Readable().wrap(oreader);

myReader.on('readable', function() {
  myReader.read(); // 기타 등등
});

Class: stream.Writable#

Writable 스트림 인터페이스는 데이터를 작성할 목적지에 대한 추상화다.

writable 스트림 예제는 다음을 포함하고 있다.

writable.write(chunk, [encoding], [callback])#

  • chunk String | Buffer 작성할 데이터
  • encoding String chunk가 문자열인 경우의 인코딩
  • callback Function 데이터의 청크를 내보냈을 때(flushed)의 콜백함수
  • Returns: Boolean 데이터를 모두 처리했으면 true이다.

이 메서드는 기반 시스템에 데이터를 작성하고 데이터를 완전히 처리하고 나면 전달받은 콜백함수를 호출한다.

반환 값은 바로 이어서 작성해야 하는지를 나타낸다. 데이터가 내부적으로 버퍼링 되었다면 false를 반환할 것이고 그렇지 않으면 true를 반환할 것이다.

이 반환 값이 나타내는 경고는 엄격하다. 이 함수가 false를 반환했더라도 계속해서 작성이 가능할 수도 있다. 하지만 작성은 메모리에 버퍼링 될 것이므로 지나치게 이렇게 하지 않는 것이 좋다. 대신 추가적인 데이터를 작성하기 전에 drain 이벤트를 기다려라.

Event: 'drain'#

writable.write(chunk) 호출이 false를 반환한 뒤 스트림에 추가적인 데이터를 시작해도 될 때를 나타내기 위해서 drain 이벤트가 발생한다.

// 제공된 writable 스트림에 백만 번 데이터를 작성한다.
// 역압력(back-pressure)부분을 주의 깊게 봐라.
function writeOneMillionTimes(writer, data, encoding, callback) {
  var i = 1000000;
  write();
  function write() {
    var ok = true;
    do {
      i -= 1;
      if (i === 0) {
        // 마지막!
        writer.write(data, encoding, callback);
      } else {
        // 계속할지 기다릴지 확인
        // 아직 완료되지 않았으므로 callback을 전달하지 않는다.
        ok = writer.write(data, encoding);
      }
    } while (i > 0 && ok);
    if (i > 0) {
      // 일찍 마쳐야 한다!
      // drain되면 추가적인 데이터를 작성한다.
      writer.once('drain', write);
    }
  }
}

writable.end([chunk], [encoding], [callback])#

  • chunk String | Buffer 선택적인 값으로 작성할 데이터
  • encoding String chunk가 문자열인 경우 인코딩
  • callback Function 스트림이 완료되었을 때 호출한 콜백함수로 선택적인 값이다

스트림에 작성할 데이터가 더는 없을 때 이 함수를 호출해라. 콜백함수를 전달하면 finish 이벤트의 리스너로 등록된다.

end()를 호출한 뒤에 write()를 호출하면 오류가 발생할 것이다.

// 'hello, '를 작성한 뒤 'world!'로 종료한다.
http.createServer(function (req, res) {
  res.write('hello, ');
  res.end('world!');
  // 더는 데이터를 작성할 수 없다!
});

Event: 'finish'#

end() 메서드가 호출되었을 때 모든 데이터를 의존 시스템으로 내보내고 이 이벤트가 발생한다.

var writer = getWritableStreamSomehow();
for (var i = 0; i < 100; i ++) {
  writer.write('hello, #' + i + '!\n');
}
writer.end('this is the end\n');
writer.on('finish', function() {
  console.error('all writes are now complete.');
});

Event: 'pipe'#

  • src Readable Stream 해당 writable 스트림에 파이프로 연결할 소스 스트림

이 이벤트는 readable 스트림에서 pipe() 메서드가 호출될 때마다 해당 writable 스트림을 목적지로 설정하면서 발생한다.

var writer = getWritableStreamSomehow();
var reader = getReadableStreamSomehow();
writer.on('pipe', function(src) {
  console.error('something is piping into the writer');
  assert.equal(src, reader);
});
reader.pipe(writer);

Event: 'unpipe'#

  • src Readable Stream 해당 writable 스트림을 unpiped하는 소스 스트림

이 이벤트는 readable 스트림에서 unpipe() 메서드를 호출할 때마다 해당 writable 스트림을 목적지에서 제거하면서 발생한다.

var writer = getWritableStreamSomehow();
var reader = getReadableStreamSomehow();
writer.on('unpipe', function(src) {
  console.error('something has stopped piping into the writer');
  assert.equal(src, reader);
});
reader.pipe(writer);
reader.unpipe(writer);

Event: 'error'#

데이터를 쓰거나 파이프로 연결할 때 오류가 발생하면 이 이벤트가 발생한다.

Class: stream.Duplex#

Duplex 스트림은 Readable 인터페이스와 Writable 인터페이스를 모두 구현한 스트림이다. 사용방법은 윗부분을 봐라.

Duplex 스트림의 예제는 다음을 포함하고 있다.

Class: stream.Transform#

Transform 스트림은 입력을 어떤 방법으로 계산해서 출력하는 Duplex 스트림이다. Transform은 Readable 인터페이스와 Writable 인터페이스를 모두 구현한다. 사용방법은 윗부분을 봐라.

Transform 스트림 예제는 다음을 포함하고 있다.

API for Stream Implementors#

어떤 종류의 스트림이든지 스트림을 구현하기 위한 패턴은 같다.

  1. 자신만의 하위클래스에 적절한 부모클래스를 상속받는다. (util.inherits 메서드는 이 부분에 유용하다.)
  2. 생성자에서 적절한 부모 클래스의 생성자를 호출해서 내부적인 메커니즘이 적절하게 설정되도록 해라.
  3. 하나 이상의 특정 메서드를 구현해라.(자세한 내용은 아래에 나온다.)

작성하는 스트림 클래스의 종류에 따라 상속받을 클래스와 구현할 메서드가 다르다.

사용처

클래스

구현할 메서드

읽기 전용

Readable

_read

쓰기 전용

Writable

_write

읽기/쓰기

Duplex

_read, _write

작성한 데이터를 처리 후 결과를 읽어드린다

Transform

_transform, _flush

구현 코드에서는 위의 API for Stream Consumers에서 설명한 메서드를 호출하지 않는 것이 아주 중요하다. 호출한다면 스트리밍 인터페이스를 사용하는 프로그램에서 부작용이 발생할 잠재성을 갖게 된다.

Class: stream.Readable#

stream.Readable_read(size) 메서드의 의존 구현체를 확장토록 설계된 추상 클래스다.

프로그램에서 스트림을 사용하는 방법은 위의 API for Stream Consumers를 참고해라. 이어서 프로그램에서 Readable 스트림을 어떻게 구현하는지 설명한다.

Example: A Counting Stream#

다음은 Readable 스트림의 기본적인 예제다. 이 예제는 차례대로 1부터 1,000,000까지 숫자를 발생시키고 끝난다.

var Readable = require('stream').Readable;
var util = require('util');
util.inherits(Counter, Readable);

function Counter(opt) {
  Readable.call(this, opt);
  this._max = 1000000;
  this._index = 1;
}

Counter.prototype._read = function() {
  var i = this._index++;
  if (i > this._max)
    this.push(null);
  else {
    var str = '' + i;
    var buf = new Buffer(str, 'ascii');
    this.push(buf);
  }
};

Example: SimpleProtocol v1 (Sub-optimal)#

이는 위에서 설명한 parseHeader 함수와 비슷하지만 커스텀 스트림으로 구현되었다. 그리고 이 구현체는 들어오는 데이터를 문자열로 변환하지 않는다.

하지만 이는 Transform 스트림으로 구현하는 것이 더 좋다. 더 좋은 구현체는 아랫부분을 참고해라.

// 간단한 데이터 프로토콜에 대한 파서.
// "header"는 JSON 객체이고 이어서 두 개의 \n 문자가 오고 이어서 메시지 바디가 온다.
//
// Note: 이는 Transform 스트림으로 훨씬 간단히 할 수 있다!
// 이를 위해 Readable를 직접 사용하는 것이 차선책이다. 이에 대한 예제는 아래의
// Transform 부분을 참고해라.

var Readable = require('stream').Readable;
var util = require('util');

util.inherits(SimpleProtocol, Readable);

function SimpleProtocol(source, options) {
  if (!(this instanceof SimpleProtocol))
    return new SimpleProtocol(source, options);

  Readable.call(this, options);
  this._inBody = false;
  this._sawFirstCr = false;

  // 소스는 소켓이나 파일 같은 readable 스트림이다
  this._source = source;

  var self = this;
  source.on('end', function() {
    self.push(null);
  });

  // 소스를 읽을 수 있을 때마다 무시한다.
  // read(0)은 바이트를 전혀 소비하지 않을 것이다
  source.on('readable', function() {
    self.read(0);
  });

  this._rawHeader = [];
  this.header = null;
}

SimpleProtocol.prototype._read = function(n) {
  if (!this._inBody) {
    var chunk = this._source.read();

    // 소스에 데이터가 없으면 아직 데이터를 갖지 않는다.
    if (chunk === null)
      return this.push('');

    // 청크가 \n\n를 가졌는지 검사한다
    var split = -1;
    for (var i = 0; i < chunk.length; i++) {
      if (chunk[i] === 10) { // '\n'
        if (this._sawFirstCr) {
          split = i;
          break;
        } else {
          this._sawFirstCr = true;
        }
      } else {
        this._sawFirstCr = false;
      }
    }

    if (split === -1) {
      // 아직 \n\n를 기다리고 있다
      // 청크를 치워두고 다시 시도한다.
      this._rawHeader.push(chunk);
      this.push('');
    } else {
      this._inBody = true;
      var h = chunk.slice(0, split);
      this._rawHeader.push(h);
      var header = Buffer.concat(this._rawHeader).toString();
      try {
        this.header = JSON.parse(header);
      } catch (er) {
        this.emit('error', new Error('invalid simple protocol data'));
        return;
      }
      // 이제 어떤 추가적인 데이터를 가졌으므로 컨슈머가 볼 수 있도록
      // 읽기 큐에 남은 부분을 다시 unshift한다.
      var b = chunk.slice(split);
      this.unshift(b);

      // 그리고 헤더 파싱을 완료했다는 것을 알려준다.
      this.emit('header', this.header);
    }
  } else {
    // 여기서 단순히 컨슈머에게 데이터를 제공한다.
    // push(null)이 EOF를 의미하므로 push(null)을 사용하지 않도록 조심해라.
    var chunk = this._source.read();
    if (chunk) this.push(chunk);
  }
};

// 사용방법:
// var parser = new SimpleProtocol(source);
// 이제 파서는 파싱된 헤더 데이터를 가진 'header'를 발생시킬
// readable 스트림이다.

new stream.Readable([options])#

  • options Object
    • highWaterMark Number 사용하는 리소스에서 읽기를 중단하기 전에 내부 버퍼에 저장할 최대 바이트 수. 기본값=16kb
    • encoding String 이 값을 지정하면 지정한 인코딩으로 버퍼를 문자열로 디코드한다. 기본값=null
    • objectMode Boolean 이 스트림이 객체의 스트림처럼 동작해야 하는지 여부. 즉, stream.read(n)이 n 크기의 Buffer 대신 문자열 값을 반환한다는 것을 의미한다. 기본값=false

Readable 클래스를 확장하는 클래스에서 버퍼링 설정을 적절하게 초기화할 수 있도록 Readable 생성자를 꼭 호출해라.

readable._read(size)#

  • size Number 비동기적으로 읽을 바이트 수

Note: 이 함수를 구현하되 직접 호출하지는 마라.

이 함수는 직접 호출하지 말아야 한다. 이 함수는 자식 클래스에서 구현해야 하고 Readable 클래스 내부 함수에서만 호출해야 한다.

모든 Readable 스트림 구현체는 사용하는 리소스에서 데이터를 가져오는 _read 메서드를 반드시 제공해야 한다.

이 함수는 클래스는 내부에서만 사용할 목적이므로 언더스코어 접두사가 붙어있고 사용자 프로그램에서 직접 호출하지 않아야 한다. 하지만 자신만의 확장 클래스에서 이 메서드를 오버라이드하기 원할 수도 있다.

데이터를 사용할 수 있을 때 readable.push(chunk)를 호출해서 읽기 큐에 데이터를 넣는다. push가 false를 반환하면 읽기를 멈춰야 한다. _read가 다시 호출됐을 때 추가적인 데이터를 큐에 넣기 시작해야 한다.

size 아규먼트는 권고사항이다. "read"는 데이터를 반환하는 하나의 호출인 구현체에서 얼마나 많은 데이터를 가여와야 하는지 지정하는데 이 값을 사용한다. TCP나 TLS처럼 관련이 없는 구현체는 이 인자를 무시하고 데이터를 이용할 수 있을때마다 제공할 것이다. 예를 들면 stream.push(chunk)를 호출하기 전에 size 바이트가 사용 가능할 때까지 "기다릴" 필요가 없다.

readable.push(chunk, [encoding])#

  • chunk Buffer | null | String 읽기 큐에 데이터의 청크를 넣는다
  • encoding String 문자열 청크의 인코딩. 'utf8''ascii'같은 유효한 Buffer 인코딩이어야 한다.
  • return Boolean push를 계속 해야 하는지 여부

Note: 이 함수는 Readable 구현체에서 호출해야 지 Readable 스트림를 소비하는 쪽에서 호출하면 안 된다.

push(chunk)가 최소 한번 실행될 때까지는 _read() 함수는 다시 호출되지 않을 것이다.

나중에 'readable' 이벤트가 발생했을 때 read() 메서드를 호출해서 데이터를 꺼내도록 Readable 클래스는 읽기 큐에 데이터를 넣어서 동작한다.

push() 메서드는 데이터를 읽기 큐에 명시적으로 넣을 것이다. 이 함수를 null로 호출하면 데이터가 끝났다는(EOF) 신호가 올 것이다.

이 API는 최대한 유연하게 설계되었다. 예를 들어 어떤 종류의 pause/resume 메커니즘과 데이터 콜백을 가진 저수준 소스를 감싸고 있다고 해보자. 이럴 때 다음과 같이 해서 저수준 소스를 감쌀 수 있다.

// source는 readStop(), readStart() 메서드와
// source가 데이터를 가졌을 때 호출되는 `ondata` 멤버,
// 데이터가 끝났을 때 호출되는 `onend` 멤버를 가진 객체이다.

util.inherits(SourceWrapper, Readable);

function SourceWrapper(options) {
  Readable.call(this, options);

  this._source = getLowlevelSourceObject();
  var self = this;

  // 데이터가 있을 때마다 내부 버퍼에 데이터를 넣는다.
  this._source.ondata = function(chunk) {
    // push()가 false를 반환하면 source에서 읽기를 멈춰야 한다
    if (!self.push(chunk))
      self._source.readStop();
  };

  // source가 끝났을 때 EOF를 알리는 `null` 청크를 넣는다.
  this._source.onend = function() {
    self.push(null);
  };
}

// 이 경우에 권고사항인 size 인자를 무시하고 스트림이 추가적인 데이터를
// 가져오도록 하고 싶을 때 _read를 호출할 것이다.
SourceWrapper.prototype._read = function(size) {
  this._source.readStart();
};

Class: stream.Writable#

stream.Writable_write(chunk, encoding, callback) 메서드의 의존 구현체로 확장하도록 설계된 추상 클래스다.

프로그램에서 writable 스트림을 사용하는 방법은 위의 API for Stream Consumers 부분을 참고해라. 여기서는 프로그램에서 Writable 스트림을 구현하는 방법을 설명한다.

new stream.Writable([options])#

  • options Object
    • highWaterMark Number write()가 false를 반환하기 시작했을 때 Buffer 레벨. 기본값=16kb
    • decodeStrings Boolean 문자열을 _write()에 전달하기 전에 Buffer로 디코딩할 것인지 여부. 기본값=true

Writable 클래스를 확장한 클래스에서는 버퍼링 설정이 적절하게 초기화될 수 있도록 꼭 생성자를 호출해라.

writable._write(chunk, encoding, callback)#

  • chunk Buffer | String 작성할 청크. decodeStrings 옵션이 false로 설정하지 않았다면 항상 버퍼가 될 것이다.
  • encoding String 청크가 문자열인 경우 인코딩 종류. 청크가 버퍼이면 무시한다. decodeStrings 옵션을 명시적으로 false로 설정하지 않았다면 청크는 항상 버퍼가 될 것이다.
  • callback Function 전달된 청크를 모두 처리했을 때 이 함수(선택적으로 오류 인자와 함께)를 호출한다.

모든 Writable 스트림 구현체는 기반 리소스에 데이터를 전송하기 위해서 _write() 메서드를 제공해야 한다.

Note: 이 함수는 직접 호출하지 말아야 한다. 이는 자식 클래스에서 구현되어야 하고 Writable 클래스 내부 메서드만 호출해야 한다.

작성이 성공적으로 완료되었거나 오류가 발생했다는 신호로 표준 callback(error) 패턴을 사용해서 콜백을 호출한다.

생성자 옵션에서 decodeStrings 플래그를 설정했다면 chunk는 버퍼가 아니라 문자열이 되고 encoding은 문자열의 종류를 나타내게 된다. 이는 특정 문자열 데이터 인코딩에 대한 최적화 처리를 하는 구현체를 지원하기 위함이다. 명시적으로 decodeStrings 옵션을 false로 설정하지 않았다면 encoding 인자를 무시하고 chunk가 항상 Buffer라고 생각할 수 있다.

이 메서드는 정의된 클래스 내부용이므로 언더스코어 접두사가 붙어있다. 그래서 사용자 프로그램이 직접 호출하지 말아야 한다. 하지만 자신의 확장 클래스에서 이 함수를 오버라이드하기 원할 수도 있다.

Class: stream.Duplex#

"duplex" 스트림은 TCP 소켓 연결처럼 Readable이면서 Writable인 스트림이다.

stream.Duplex는 Readable 스트림 클래스나 Writable 스트림 클래스에서 했듯이 _read(size)_write(chunk, encoding, callback) 메서드의 의존 구현체로 확장하도록 설계된 추상 클래스다.

자바스크립트가 프로토타입 다중 상속을 지원하지 않으므로 이 클래스는 Readable을 프로토타입으로 상속받은 뒤 Writable이 기생하는 식으로 동작한다. 그러므로 확장 duplex 클래스에서 저수준 _write(chunk, encoding, callback) 메서드와 저수준 _read(n)를 사용자가 모두 구현했는가에 달려있다.

new stream.Duplex(options)#

  • options Object Writable 생성자와 Readable 생성자에 전달되는 옵션. 다음의 값도 가진다.
    • allowHalfOpen Boolean 기본값=true. false로 설정했다면 스트림은 writable 쪽이 종료되었을 때 readable쪽도 자동으로 종료하고 그 반대의 경우도 마찬가지다.

Duplex 클래스를 확장한 클래스에서는 버퍼링 설정을 적절히 초기화할 수 있도록 생성자를 꼭 호출해야 한다.

Class: stream.Transform#

"transform" 스트림은 zlib 스트림이나 crypto 스트림처럼 출력이 어떤 식으로든 입력에 연결되는 duplex 스트림이다.

출력이 입력과 같은 크기이거나 청크의 수가 같거나 동시에 도착해야 한다는 등의 요구사항은 없다. 예를 들면 Hash 스트림은 입력이 종료되었을 때 제공된 출력의 단일 청크만을 가질 것이다. zlib 스트림은 입력보다 아주 작거나 아주 큰 출력을 보낼 것이다.

Transform 클래스는 _read()_write() 메서드를 구현하기보다는 _transform() 메서드를 구현해야 하고 선택적으로 _flush() 메서드를 구현할 수도 있다.(하단 참조)

new stream.Transform([options])#

  • options Object Writable과 Readable 생성자에 모두 전달된다.

Transform 클래스를 확장한 클래스에서는 버퍼링 설정을 적절하게 초기화할 수 있도록 생성자를 호출해야 한다.

transform._transform(chunk, encoding, callback)#

  • chunk Buffer | String 변환할 청크. decodeStrings 옵션을 false로 설정하지 않는 한 항상 버퍼가 될 것이다.
  • encoding String 청크가 문자열인 경우 이 인코딩 타입이다. (decodeStrings 청크가 버퍼이면 무시한다.)
  • callback Function 제공된 청크의 처리가 끝났을 때 이 함수를 호출한다. (선택적으로 오류 인자와 함께)

Note: 이 함수는 절대로 직접 호출하지 말아야 한다. 자식 클래스가 구현하거나 내부 Transform 클래스 메서드만 호출해야 한다.

모든 Transform 스트림 구현체는 입력을 받고 출력을 생산하도록 _transform를 제공해야 한다.

해당 Transform 클래스가 무엇을 하든지 간에 쓰인 바이트를 다루고 인터페이스의 읽기 쪽에 이를 전달하도록 _transform을 수행해야 한다. 비동기 I/O를 수행하거나 어떤 것을 처리하는 등이다.

해당 입력 청크에서 출력을 생성하도록 해당 청크의 결과로 얼마나 많은 데이터를 출력하기 원하는지에 따라 transform.push(outputChunk)를 0번이나 여러 번 호출해라.

현재 청크가 완전히 소비되었을 때만 callback 함수를 호출한다. 특정 입력 청크의 결과와 출력이 같을 수도 있고 같지 않을 수도 있다.

이 메서드는 메서드를 정의한 클래스 내부에서 사용하고 사용자 프로그램이 직접 호출하면 안되기 때문에 언더스코어로 시작한다. 하지만 자신의 확장 클래스에서 이 메서드를 오버라이드할 수 있다.

transform._flush(callback)#

  • callback Function 남아있는 데이터를 모두 내보냈을 때 이 함수를 호출한다. (선택적으로 오류 인자와 함께)

Note: 이 함수는 절대로 직접 호출하지 말아야 한다. 자식 클래스가 구현할 수도 있는데 자식 클래스가 구현했다면 내부 Transform 클래스 메서드만 호출할 것이다.

일부의 경우 변환 작업이 스트림의 끝에서 약간 더 많은 데이터를 발생시키도록 해야 할 수도 있다. 예를 들어 Zlib 압축 스트림은 출력을 최적화해서 압축할 수 있도록 어떤 내부 상태를 저장할 것이다. 하지만 결국에는 데이터가 완료될 수 있도록 남아있는 것으로 최선의 작업을 해야 할 필요가 있다.

이럴 때 쓰진 데이터를 모두 소비했지만 읽기 쪽에 끝났음을 알리는 end를 발생하기 전인 아주 마지막에 호출될 _flush 메서드를 구현할 수 있다. _transform와 마찬가지로 적절하게 0번이나 여러 번 transform.push(chunk)를 호출하고 flush 작업이 완료되었을 때 callback을 호출해라.

이 메서드는 메서드를 정의한 클래스 내부에서 사용하고 사용자 프로그램이 직접 호출하면 안되기 때문에 언더스코어로 시작한다. 하지만 자신의 확장 클래스에서 이 메서드를 오버라이드할 수 있다.

Example: SimpleProtocol parser v2#

위의 간단한 프로토콜 파서 예제를 고수준의 Transform 스트림 클래스를 사용해서 간단하게 구현할 수 있고 이는 위의 parseHeaderSimpleProtocol v1 예제와 비슷하다.

이 예제에서는 인자로 입력을 제공하기보다는 더 이상적인 Node 스트림 접근으로 파서에 파이프를 연결한다.

var util = require('util');
var Transform = require('stream').Transform;
util.inherits(SimpleProtocol, Transform);

function SimpleProtocol(options) {
  if (!(this instanceof SimpleProtocol))
    return new SimpleProtocol(options);

  Transform.call(this, options);
  this._inBody = false;
  this._sawFirstCr = false;
  this._rawHeader = [];
  this.header = null;
}

SimpleProtocol.prototype._transform = function(chunk, encoding, done) {
  if (!this._inBody) {
    // 청크가 \n\n를 가졌는지 검사한다.
    var split = -1;
    for (var i = 0; i < chunk.length; i++) {
      if (chunk[i] === 10) { // '\n'
        if (this._sawFirstCr) {
          split = i;
          break;
        } else {
          this._sawFirstCr = true;
        }
      } else {
        this._sawFirstCr = false;
      }
    }

    if (split === -1) {
      // 여전히 \n\n를 기다린다
      // 청크를 치워두고 다시 시도한다.
      this._rawHeader.push(chunk);
    } else {
      this._inBody = true;
      var h = chunk.slice(0, split);
      this._rawHeader.push(h);
      var header = Buffer.concat(this._rawHeader).toString();
      try {
        this.header = JSON.parse(header);
      } catch (er) {
        this.emit('error', new Error('invalid simple protocol data'));
        return;
      }
      // 헤더 파싱이 끝났음을 알려준다.
      this.emit('header', this.header);

      // 일부 추가적인 데이터를 얻었으므로 이를 먼저 발생시킨다.
      this.push(chunk.slice(split));
    }
  } else {
    // 여기서 기존처럼 컨슈머에 데이터를 제공한다.
    this.push(chunk);
  }
  done();
};

// 사용방법:
// var parser = new SimpleProtocol();
// source.pipe(parser)
// 이제 파서는 파싱된 헤더 데이터를 가진 'header'를 발생시킬
// readable 스트림이다.

Class: stream.PassThrough#

이는 입력 바이트를 출력으로 단순히 전달하는 Transform 스트림의 별로 중요치 않은 구현체이다. 이 클래스의 목적은 주로 예제나 테스트이지만 새로운 스트림에 대한 블록을 구성하는 경우처럼 가끔은 유용한 경우가 있다.

Streams: Under the Hood#

Buffering#

Writable 스트림과 Readable 스트림은 _writableState.buffer_readableState.buffer를 각각 호출해서 내부 객체에 데이터를 버퍼링할 것이다.

버퍼 될 데이터의 양은 생성자에 전달한 highWaterMark 옵션에 따라 다르다.

Readable 스트림에서는 구현체가 stream.push(chunk)를 호출했을 때 버퍼링이 일어난다. 스트림의 컨슈머가 stream.read()를 호출하지 않은 경우 데이터가 소비될 때까지 데이터는 내부 큐에 있을 것이다.

Writable 스트림에서는 사용자가 stream.write(chunk)를 반복적으로 호출했을 때(write()false를 반환하더라도) 버퍼링이 일어난다.

스트림의 목적은(특히 pipe() 메서드) 데이터 버퍼링을 수긍할만한 수준으로 제한해서 속도가 제각각인 소스와 목적지가 사용 가능한 메모리를 넘어서지 않게 하기 위함이다.

stream.read(0)#

데이터는 실제로 소비하지 않으면서 사용하는 readable 스트림의 메커니즘을 갱신하고자 하는 경우가 있다. 이러면 항상 null을 반환하는 stream.read(0)를 호출할 수 있다.

내부 읽기 버퍼가 highWaterMark보다 작고 스트림이 읽는 중이 아닌 경우 read(0)를 호출하면 저수준의 _read 호출이 발생할 것이다.

이렇게 해야 하는 경우는 거의 없지만 Node 내부에서 이러한 작업을 하는 경우를 볼 수 있을 것이다.(특히 Readable 스트림 클래스의 내부에서)

stream.push('')#

0 바이트 문자열이나 버퍼를 밀어 넣을 때(Object mode가 아닌 경우) 흥미로운 부가작용이 일어난다. 이는 stream.push()를 호출한 것이므로 reading 과정을 종료할 것이다. 하지만 읽기 가능한 버퍼에는 어떤 데이터도 추가하지 않으므로 사용자가 사용할 수 있는 데이터는 전혀 없다.

아주 드물게 지금은 제공할 데이터가 없지만 스트림의 컨슈머(또는 자신의 코드의 다른 곳에서)는 stream.read(0)를 호출해서 언제 다시 확인해 봐야 하는지 알아야 하는 경우가 있다. 이러한 경우 stream.push('')를 호출할 것이다.

지금까지는 이 기능을 유일하게 사용한 곳이 Node v0.12에서는 폐기된 tls.CryptoStream 클래스다. stream.push('')를 사용해야 한다면 무언가 크게 잘못될 가능성이 아주 크므로 다른 접근이 없는지 생각해 보기를 바란다.

Compatibility with Older Node Versions#

Node v0.10 이전 버전에서는 Readable 스트림 인터페이스가 더 간단하지만 동시에 덜 강력하고 덜 유용하다.

  • read() 메서드를 호출하기를 기다리기보다는 'data' 이벤트가 바로 발생하기 시작할 것이다. 데이터를 다루는 방법을 결정하기 위해서 I/O를 사용해야 한다면 데이터를 잃어버리지 않기 위해 어떤 종류든 버퍼에 청크를 저장해야 한다.
  • pause() 메서드는 보장된다기보다는 권고사항이다. 즉, 스트림이 멈춰진 상태에 있더라도 'data' 이벤트를 받을 준비를 하고 있어야 한다.

Node v0.10에는 아래에서 설명한 Readable 클래스가 추가되었다. 오래된 Node 프로그램과의 하위 호환성 때문에 'data' 이벤트 핸들러가 추가되거나 pause() 혹은 resume() 메서드가 호출되었을 때 Readable 스트림은 "flowing mode"로 바뀐다. 그래서 새로운 read() 메서드와 'readable' 이벤트를 사용하지 않더라도 'data' 청크를 잃어버릴 걱정을 하지 않아도 된다.

프로그램 대부분은 정상적으로 잘 동작할 것이지만 이 기능은 다음과 같은 상황에 대처한다.

  • 'data' 이벤트 핸들러가 추가되지 않았을 때.
  • pause()resume() 메서드가 호출되지 않았을 때.

예를 들면 다음 코드를 생각해 보자.

// 주의! 동작하지 않음!
net.createServer(function(socket) {

  // 'end' 메서드를 추가했지만, 데이터를 소비하지 않는다.
  socket.on('end', function() {
    // 이 코드는 실행되지 않는다.
    socket.end('I got your message (but didnt read it)\n');
  });

}).listen(1337);

Node v0.10 이전 버전에서 들어오는 메시지 데이터는 그냥 버려진다. 하지만 Node v0.10 이상에서는 소켓이 영원히 멈춰진 상태로 남게 된다.

이 상황을 해결하려면 "old mode"로 동작하도록 resume()을 호출해야 한다.

// 해결 방법
net.createServer(function(socket) {

  socket.on('end', function() {
    socket.end('I got your message (but didnt read it)\n');
  });

  // 데이터의 흐름이 시작되고 데이터를 버린다.
  socket.resume();

}).listen(1337);

flowing-mode로 변경되는 새로운 Readable 스트림에 추가로 v0.10 이전 방식의 스트림은 wrap() 메서드를 사용해서 Readable 클래스로 감쌀 수 있다.

Object Mode#

일반적으로 스트림은 문자열이나 버퍼에서만 동작한다.

object 모드의 스트림은 버퍼나 문자열이 아닌 일반적인 자바스크립트 값을 발생시킬 수 있다.

object 모드의 Readable 스트림은 size 인자의 크기에 상관없이 stream.read(size) 호출에서 항상 하나의 아이템을 반환할 것이다.

object 모드의 Writable 스트림은 stream.write(data, encoding)encoding 인자를 항상 무시할 것이다.

특별한 값인 null은 object 모드 스트림에서도 여전히 특별한 값이다. 즉, object 모드의 readable 스트림에서 stream.read()이 반환한 null은 더는 데이터가 없음을 의미하고 stream.push(null)은 스트림 데이터가 끝났다(EOF)는 신호로 사용한다.

Node 코어에서 object 모드인 스트림은 없다. 이 방식은 사용자의 스트리밍 라이브러리에서만 사용한다.

스트 자식 클래스 생성자에서 옵션 객체에 objectMode를 설정해야 했다. 중간 스트림에 objectMode를 설정하는 것은 안전하지 않다.

State Objects#

Readable 스트림은 _readableState라는 멤버 객체를 가진다. Writable 스트림은 _writableState라는 멤버 객체를 가진다. Duplex 스트림은 위의 두 멤버 객체를 모두 가진다.

이러한 객체는 일반적으로 자식 클래스에서 수정하면 안된다. 하지만 읽는 쪽이 objectMode이고 쓰는 쪽은 objectMode가 아닌 Duplex나 Transform 스트림이라면 적절한 상태 객체에 명시적으로 플래그를 설정해서 생성자에서 수정해야 한다.

var util = require('util');
var StringDecoder = require('string_decoder').StringDecoder;
var Transform = require('stream').Transform;
util.inherits(JSONParseStream, Transform);

// \n으로 구분된 JSON 문자열 데이터를 가져오고 파싱된 객체를 보낸다.
function JSONParseStream(options) {
  if (!(this instanceof JSONParseStream))
    return new JSONParseStream(options);

  Transform.call(this, options);
  this._writableState.objectMode = false;
  this._readableState.objectMode = true;
  this._buffer = '';
  this._decoder = new StringDecoder('utf8');
}

JSONParseStream.prototype._transform = function(chunk, encoding, cb) {
  this._buffer += this._decoder.write(chunk);
  // newline으로 분리한다
  var lines = this._buffer.split(/\r?\n/);
  // 버퍼링 된 마지막 부분의 라인을 유지한다
  this._buffer = lines.pop();
  for (var l = 0; l < lines.length; l++) {
    var line = lines[l];
    try {
      var obj = JSON.parse(line);
    } catch (er) {
      this.emit('error', er);
      return;
    }
    // 읽을 수 있는 컨슈머로 파싱된 객체를 밀어 넣는다
    this.push(obj);
  }
  cb();
};

JSONParseStream.prototype._flush = function(cb) {
  // 남은 부분을 처리한다
  var rem = this._buffer.trim();
  if (rem) {
    try {
      var obj = JSON.parse(rem);
    } catch (er) {
      this.emit('error', er);
      return;
    }
    // 읽을 수 있는 컨슈머로 파싱된 객체를 밀어 넣는다
    this.push(obj);
  }
  cb();
};

상태 객체에는 프로그램에서 스트림의 상태를 디버깅할 수 있는 유용한 정보가 담겨있다. 이 객체를 살펴보는 건 안전하지만, 생성자에서 옵션 플래그를 설정하는 것을 넘어서서 객체를 수정하는 것은 안전하지 않다.

Crypto#

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

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

crypto 모듈은 안정한 HTTPS net이나 http 연결에서 사용되는 안정한 인증서를 캡슐화하는 방법을 제공한다.

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

메서드에 대한 래퍼(wrapper) 세트를 제공한다.

crypto.getCiphers()#

지원하는 암호화 이름의 배열을 반환한다.

예제:

var ciphers = crypto.getCiphers();
console.log(ciphers); // ['AES-128-CBC', 'AES-128-CBC-HMAC-SHA1', ...]

crypto.getHashes()#

지원하는 해시 알고리즘 이름의 배열을 반환한다.

예제:

var hashes = crypto.getHashes();
console.log(hashes); // ['sha', 'sha1', 'sha1WithRSAEncryption', ...]

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#

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

읽고 쓸 수 있는 stream이다. 해시를 계산하는데 작성된 데이터를 사용한다. 스트림의 작성 가능한 쪽이 종료되고 나면 계산된 해시 다이제스트를 얻는데 read() 메서드를 사용해라. 레거시 updatedigest 메서드도 지원한다.

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

hash.update(data, [input_encoding])#

전달한 input_encodingdata로 해시 내용을 갱신한다. 전달한 input_encoding 인코딩은 'utf8', 'ascii', 'binary'가 될 수 있다. 인코딩을 지정하지 않고 입력이 문자열이면 'binary' 인코딩을 사용한다. dataBuffer이면 input_encoding는 무시한다.

이 함수는 스트림처럼 새로운 데이터가 올 때마다 여러 번 호출될 수 있다.

hash.digest([encoding])#

해시 되어야 하는 전달한 데이터의 모든 다이제스트를 계산한다. encoding'hex', 'binary', 'base64'가 될 수 있다. 인코딩을 지정하지 않으면 버퍼를 반환한다.

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

crypto.createHmac(algorithm, key)#

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

이는 읽고 쓸 수 있는 stream이다. hmac을 계산하는데 쓰인 데이터를 사용한다. 스트림의 쓰기 가능한 쪽이 종료되고 나면 계산한 다이제스트를 얻는데 read() 메서드를 사용해라. 레거시 updatedigest 메서드도 지원한다.

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

Class: Hmac#

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

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

hmac.update(data)#

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

hmac.digest([encoding])#

hmac이 되어야 하는 전달한 데이터의 모든 다이제스트를 계산한다. encoding'hex', 'binary', 'base64'가 될 수 있다. 인코딩을 지정하지 않으면 버퍼를 반환한다.

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

crypto.createCipher(algorithm, password)#

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

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

이는 읽고 쓸 수 있는 stream이다. 해시를 계산하는데 작성된 데이터를 사용한다. 스트림의 쓰기 가능한 쪽이 종료되면 계산된 해시 다이제스트를 얻을 때 read() 메서드를 사용해라. 레거시 updatedigest 메서드도 지원한다.

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 객체는 읽고 쓸 수 있는 streams이다. 읽기 가능한 쪽에서 암호화된 데이터를 생성할 때 작성한 평범한 텍스트 데이터를 사용한다. 레거시 updatefinal메서드도 지원한다.

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

전달한 input_encoding의 인코딩의 data로 cipher를 갱신한다. input_encoding'utf8', 'ascii', 'binary'가 될 수 있다. 인코딩을 지정하지 않으면 버퍼라고 가정한다. dataBufferinput_encoding은 무시한다.

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

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

cipher.final([output_encoding])#

'binary', 'base64', 'hex'중의 하나인 output_encoding로 남아있는 모든 암호화된 내용을 반환한다. 인코딩을 지정하지 않으면 버퍼를 반환한다.

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 객체는 읽고 쓸 수 있는 streams이다. 읽기 가능한 쪽에서 평범한 텍스트 데이터를 생성할 때 작성한 암호화된 데이터를 사용한다. 레거시 updatefinal메서드도 지원한다.

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

'binary', 'base64', 'hex'로 인코딩된 data로 decipher를 갱신한다. 인코딩을 지정하지 않으면 버퍼라고 가정한다. dataBufferinput_encoding은 무시한다.

output_decoding는 반환할 복호화된 평문의 형식을 지정한다. 'binary', 'ascii', 'utf8'가 될 수 있고 버퍼를 지정하지 않으면 버퍼를 반환한다.

decipher.final([output_encoding])#

'binary', 'ascii', 'utf8'중에 하나가 될 수 있는 output_encoding로 남아있는 모든 복호화된 평문을 반환한다. 인코딩을 지정하지 않으면 버퍼를 반환한다.

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: Sign#

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

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

Sign 객체는 쓸 수 있는 streams이다. 시그니처를 생성할 때 작성한 데이터를 사용한다. 모든 데이터를 쓰고 나면 sign 메서드가 시그니처를 반환할 것이다. 레거시 update 메서드도 지원한다.

sign.update(data)#

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

sign.sign(private_key, [output_format])#

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

'binary', 'hex', 'base64'가 될 수 있는 output_format의 서명을 반환한다. 인코딩을 지정하지 않으면 버퍼를 반환한다.

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

crypto.createVerify(algorithm)#

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

Class: Verify#

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

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

Verify 객체는 쓰기 가능한 streams이다. 제공된 시그니처로 유효성 검사를 할 때 작성한 데이터를 사용한다. 모든 데이터를 쓰고 나서 제공된 시그니처가 유효한 경우 verify 메서드가 true를 반환할 것이다. 레거시 update 메서드도 지원한다.

verifier.update(data)#

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

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

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

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

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

crypto.createDiffieHellman(prime_length)#

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

crypto.createDiffieHellman(prime, [encoding])#

제공된 소수를 사용해서 Diffie-Hellman 키 교환 객체를 생성한다. 사용된 제너레이터는 2이다. 인코딩은 'binary', 'hex', 'base64'가 될 수 있다. 인코딩을 지정하지 않으면 버퍼라고 가정한다.

Class: DiffieHellman#

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

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

diffieHellman.generateKeys([encoding])#

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

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

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

출력 인코딩을 지정하지 않으면 버퍼를 반환한다.

diffieHellman.getPrime([encoding])#

'binary', 'hex', 'base64'가 될 수 있는 지정한 인코딩의 Diffie-Hellman 소수를 반환한다. 인코딩을 지정하지 않으면 버퍼를 반환한다.

diffieHellman.getGenerator([encoding])#

'binary', 'hex', 'base64'가 될 수 있는 지정한 인코딩의 Diffie-Hellman 제너레이터를 반환한다. 인코딩을 지정하지 않으면 버퍼를 반환한다.

diffieHellman.getPublicKey([encoding])#

'binary', 'hex', 'base64'가 될 수 있는 지정한 인코딩의 Diffie-Hellman 공개키를 반환한다. 인코딩을 지정하지 않으면 버퍼를 반환한다.

diffieHellman.getPrivateKey([encoding])#

'binary', 'hex', 'base64'가 될 수 있는 지정한 인코딩의 Diffie-Hellman 개인 키를 반환한다. 인코딩을 지정하지 않으면 버퍼를 반환한다.

diffieHellman.setPublicKey(public_key, [encoding])#

Diffie-Hellman 공개키를 설정한다. 키 인코딩은 'binary', 'hex', 'base64'가 될 수 있다. 인코딩을 지정하지 않으면 버퍼로 간주한다.

diffieHellman.setPrivateKey(private_key, [encoding])#

Diffie-Hellman 개인 키를 설정한다. 키 인코딩은 'binary', 'hex', 'base64'가 될 수 있다. 인코딩을 지정하지 않으면 버퍼로 간주한다.

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(), null, 'hex');
var bob_secret = bob.computeSecret(alice.getPublicKey(), null, '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.pbkdf2Sync(password, salt, iterations, keylen)#

동기 PBKDF2 함수. 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) {
  // 처리 오류
  // 엔트로피 소스를 다 소비했을 가능성이 높다
}

NOTE: 강력한 암호의 데이터를 생성하기 위해 모인 엔트로피가 충분하지 않다면 오류를 던지거나 콜백함수를 호출하면서 오류를 전달한다. 즉, 모든 엔트로피 소스를 소비했더라도 콜백이 없는 crypto.randomBytes는 블락되지 않는다.

crypto.pseudoRandomBytes(size, [callback])#

암호가 아닌 강력한 임의의 의사 데이터(pseudo-random data)를 생성한다. 데이터가 아주 길다면 반환된 데이터는 유일한 값이 될 것이지만 반드시 예측할 수 없는 것은 아니다. 이 때문에 이 함수의 출력은 암호화된 키의 생성처럼 예측 불가능성이 중요한 곳에서는 사용하지 말아야 한다.

반면 사용방법은 crypto.randomBytes의 반대이다.

crypto.DEFAULT_ENCODING#

문자열이나 버퍼를 받을 수 있는 함수에 사용하는 기본 인코딩. 기본값은 Buffer 객체를 사용하는 'buffer'이다. crypto 모듈을 기본 인코딩으로 'binary'를 사용하는 레거시 프로그램과 쉽게 호환성을 맞출 수 있다.

새로운 프로그램은 버퍼를 기대할 것이므로 임시로만 이 값을 사용해라.

Recent API Changes#

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

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

이 변경사항은 전부는 아니지만, 일부에서는 호환성을 깨뜨릴 것이다.

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

하지만 Buffers에서 제대로 동작하지 않을 문자열 데이터로 어떤 작업을 한 거나(문자열을 이어 붙인다거나 데이터베이스에 저장하는 등) 인코딩 인자 없이 암호화 함수에 바이너리 문자열을 전달한다면 사용하고자 하는 인코딩을 지정하는 인코딩 인자를 제공해야 할 것이다. 기본적으로 바이너리 문자열을 사용하는 이전 방식으로 변경하려면 crypto.DEFAULT_ENCODING 필드를 'binary'로 설정해라. 새로운 프로그램은 버퍼를 기대할 것이므로 이는 임시로만 사용해라.

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.getCiphers()#

지원하는 SSL 암호 이름의 배열을 반환한다.

예제:

var ciphers = tls.getCiphers();
console.log(ciphers); // ['AES128-SHA', 'AES256-SHA', ...]

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 배열로 PEM 형식이다. 이 옵션을 생략하면 각각 VeriSign처럼 잘 알려진 "루트" CA를 사용할 것이다. 연결에 권한을 부여하는 데 이것들을 사용한다.

  • crl : PEM으로 인코딩된 CRL(Certificate Revocation List)의 문자열이나 문자열의 리스트

  • ciphers: 사용하거나 제외할 암호(cipher)를 설명하는 문자열이다.

    BEAST attacks을 완화하려면 CBC가 아닌 암호문을 우선시하도록 아래에서 설명할 honorCipherOrder을 이 옵션과 함께 사용하기를 권장한다.

    기본값은 AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH이다. 형식에 대해 자세히 알고 싶으면 OpenSSL cipher list format documentation를 참고해라. ECDH (Elliptic Curve Diffie-Hellman) 암호화는 아직 지원하지 않는다.

    node.js가 OpenSSL 1.0.1이나 그 상위 버전과 연결되었을 때 AES128-GCM-SHA256를 사용하고 클라이언트는 TLS 1.2, RC4을 안전한 폴백(fallback)으로 사용한다.

    NOTE: 이 섹션의 이전 버전에서는 AES256-SHA를 괜찮은 암호문으로 제안했었다. 하지만 AES256-SHA는 CBC 암호문이므로 BEAST attacks을 받기 쉽다. AES256-SHA를 사용하지 말아라.

  • handshakeTimeout: SSL/TLS 핸드쉐이크가 이 밀리 초 내에 끝나지 않으면 연결을 거부한다. 기본값은 120초이다.

    핸드쉐이크가 타임아웃될 때마다 tls.Server 객체에서 'clientError'가 발생한다.

  • 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가 아니면 기본값은 제공하지 않는다.

  • secureProtocol: 사용할 SSL 방식. 예를 들어 SSL 버전 3을 사용하려면 SSLv3_method이다. 사용 가능한 값은 설치한 OpenSSL에 따라 다르고 상수 SSL_METHODS에 정의되어 있다.

간단한 에코(echo) 서버의 예제다.

var tls = require('tls');
var fs = require('fs');

var options = {
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem'),

  // 클라이언트 인증서 인증(certificate authentication)을 사용할 때만 필요하다.
  requestCert: true,

  // 클라이언트가 직접 서명한 인증서를 사용할 때만 필요하다.
  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'),

  // 클라이언트 인증서 인증(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.SLAB_BUFFER_SIZE#

모든 tls 서버와 클라이언트가 사용하는 슬랩(slab) 버퍼의 크기 기본값: 10 * 1024 * 1024.

무슨 작업을 하는지 알지 못한다면 기본값을 바꾸지 마라.

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 배열로 PEM 형식이다. 이 옵션을 생락하면 각각 VeriSign처럼 잘 알려진 "루트" CA를 사용할 것이다. 연결에 권한을 부여하는 데 이것들을 사용한다.

  • rejectUnauthorized: 이 값이 true이면 서버 인증서를 제공한 CA 리스트로 검증한다. 검증에 실패하면 'error' 이벤트가 발생한다. 기본값: true.

  • NPNProtocols: 지원하는 NPN 프로토콜의 배열이나 Buffer다. Buffer는 다음의 형식이어야 한다. 0x05hello0x05world, 첫 바이트는 next protocol name의 길이이다.(배열을 전달하는 것이 보통 훨씬 간단할 것이다: ['hello', 'world'])

  • servername: SNI (Server Name Indication) TLS 확장에 대한 Servername이다.

  • secureProtocol: 사용할 SSL 방식. 예를 들어 SSL 버전 3을 사용하려면 SSLv3_method이다. 사용 가능한 값은 설치한 OpenSSL에 따라 다르고 상수 SSL_METHODS에 정의되어 있다.

callback 파라미터는 'secureConnect' 이벤트의 리스너로 추가할 것이다.

tls.connect()CleartextStream 객체를 반환한다.

앞에서 설명한 에코 서버의 클라이언트 예제다.

var tls = require('tls');
var fs = require('fs');

var options = {
  // 클라이언트 인증서 인증(certificate authentication)을 사용할 때만 필요하다.
  key: fs.readFileSync('client-key.pem'),
  cert: fs.readFileSync('client-cert.pem'),

  // 클라이언트가 직접 서명한 인증서를 사용할 때만 필요하다.
  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, securePair) { }

안전한 연결이 이뤄지기 전에 클라이언트 연결이 'error' 이벤트를 발생시켰을 때 발생하는 이벤트다.

securePair는 오류가 발생한 tls.SecurePair이다.

Event: 'newSession'#

function (sessionId, sessionData) { }

TLS 세션을 생성했을 때 발생한다. 외부 스토리지에 세션을 저장할 때 사용할 것이다.

Event: 'resumeSession'#

function (sessionId, callback) { }

클라이언트가 이전의 TLS 세션을 되찾고자 할 때 발생한다. 이벤트 리스너는 주어진 sessionId를 사용해서 외부 스토리지에서 검색하고 검색이 완료되면 callback(null, sessionData)를 호출할 것이다. 세션을 되찾을 수 없다면(예: 스토리지에 존재하지 않는 경우) callback(null, null)를 호출할 것이다. callback(err)를 호출하면 들어오는 요청을 종료시키고 소켓을 제거할 것이다.

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: CryptoStream#

이는 암호화된 스트림이다.

cryptoStream.bytesWritten#

의존하는 소켓의 bytesWritten 접근자로의 프록시로 이는 소켓에 쓰인 전체 바이트(TLS 오버헤드를 포함해서)를 반환할 것이다.

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 아규먼트를 받는다.

decoder.write(buffer)#

디코딩한 문자열을 반환한다.

decoder.end()#

버퍼에 남아있는 바이트를 반환한다.

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 함수는 callback 인자를 생략할 수 있게 한다. callback 인자를 생략하면 오류는 무시하지만 폐기(deprecation) 경고는 출력하는 기본 콜백을 사용한다.

IMPORTANT: 콜백 생략은 폐기되었다. v0.12는 기대하는 대로 오류를 던질 것이다.

fs.rename(oldPath, newPath, callback)#

비동기 rename(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.renameSync(oldPath, newPath)#

동기 rename(2).

fs.ftruncate(fd, len, callback)#

동기 ftruncate(2). 전달한 완료콜백에는 예외 아규먼트 외에 다른 아규먼트는 없다.

fs.ftruncateSync(fd, len)#

동기 ftruncate(2).

fs.truncate(path, len, callback)#

비동기 truncate(2). 발생할 수 있는 오류 인자 외에 완료 콜백에 전달되는 인자는 없다.

fs.truncateSync(path, len)#

동기 truncate(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'와 비슷하지만 path가 존재하면 실패한다.

  • 'w+' - 읽기와 쓰기모드로 파일을 연다. (파일이 존재하지 않으면) 파일을 생성하거나 (파일이 존재하면) 새로 쓴다.

  • 'wx+' - 'w+'와 비슷하지만 path가 존재하면 실패한다.

  • 'a' - 추가모드로 파일을 연다. 파일이 존재하지 않으면 예외가 발생한다.

  • 'ax' - 'a'와 비슷하지만 path가 존재하면 실패한다.

  • 'a+' - 읽기와 추가모드로 파일을 연다. 파일이 존재하지 않으면 예외가 발생한다.

  • 'ax+' - 'a+'와 비슷하지만 path가 존재하면 실패한다.

mode는 파일을 생성할 때만 파일모드(권한과 스티키비트(sticky bits))를 설정한다. 기본값은 0666이며 읽고 쓸 수 있다.

콜백은 두 아규먼트 (err, fd)를 받는다.

독점 모드 'x'(open(2)의 O_EXCL 플래그)는 새로 path를 생성한다는 것을 보장한다. POSIX 시스템에서 존재하지 않는 파일에 대한 심볼릭 링크도 path가 존재하는 것으로 간주한다. 네트워크 파일 시스템에서 독점 모드는 동작할 수도 있고 동작하지 않을 수도 있다.

리눅스에서는 추가(append) 모드로 파일을 열었을 경우 위치 쓰기(positional write)가 동작하지 않는다. 커널이 위치 인자를 무시하고 항상 파일 끝에 데이터를 추가한다.

fs.openSync(path, flags, [mode])#

fs.open()의 동기버전

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를 사용하기를 강력하게 추천한다.

리눅스에서는 추가(append) 모드로 파일을 열었을 경우 위치 쓰기(positional write)가 동작하지 않는다. 커널이 위치 인자를 무시하고 항상 파일 끝에 데이터를 추가한다.

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, [options], callback)#

  • filename String
  • options Object
    • encoding String | Null default = null
    • flag String default = 'r'
  • callback Function

파일의 전체 내용을 비동기로 읽는다. 예제:

fs.readFile('/etc/passwd', function (err, data) {
  if (err) throw err;
  console.log(data);
});

콜백에는 두 아규먼트 (err, data)를 전달하고 data는 파일의 내용이다.

인코딩을 지정하지 않으면 로우(raw) 버퍼를 반환한다.

fs.readFileSync(filename, [options])#

fs.readFile의 동기 버전이다. filename의 내용을 반환한다.

encoding옵션을 지정하면 이 함수는 문자열을 반환하고 encoding을 지정하지 않으면 버퍼를 반환한다.

fs.writeFile(filename, data, [options], callback)#

  • filename String
  • data String | Buffer
  • options Object
    • encoding String | Null default = 'utf8'
    • mode Number default = 438 (aka 0666 in Octal)
    • flag String default = 'w'
  • callback Function

비동기로 파일에 데이터를 작성하고 파일이 이미 존재하는 경우에는 파일을 대체한다. 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, [options])#

fs.writeFile의 동기 버전이다.

fs.appendFile(filename, data, [options], callback)#

  • filename String
  • data String | Buffer
  • options Object
    • encoding String | Null default = 'utf8'
    • mode Number default = 438 (aka 0666 in Octal)
    • flag String default = 'a'
  • callback Function

비동기로 파일에 데이터를 추가하고 파일이 존재하지 않는 경우 파일을 생성한다. data는 문자열이거나 버퍼다.

예제:

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, [options])#

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)#

파일시스템을 확인해서 전달한 경로가 존재하는지 검사한다. 존재여부를 true나 false로 callback을 호출한다. 예제:

fs.exists('/etc/passwd', function (exists) {
  util.debug(exists ? "it's there" : "no passwd!");
});

fs.exists()는 시대착오적인 API로 과거에 존재했기 때문에 그냥 존재할 뿐이다. 그래서 코드에서 fs.exists()를 사용할 이유는 거의 없을 것이다.

특히 파일을 열기 전에 파일이 존재하는지 검사하는 것은 권장하지 않는 방법인데 이렇게 하면 레이스 컨디션에 빠지기 쉬워진다.(fs.exists()fs.open()의 호출 사이에 다른 프로세스가 파일을 삭제할 수도 있다.) 파일을 그냥 열고 파일이 존재하지 않는 경우 오류를 처리해라.

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()1970년 1월 1일 00:00:00 UTC부터 지난 시간을 밀리 초로 반환할 것이고 이 정숫값은 비교하기에 충분하다. 하지만 명확하지 않은 정보를 보여주는 데 사용할 수 있는 추가적인 메서드들이 있다. 더 자세한 내용은 MDN JavaScript Reference 페이지에 있다.

fs.createReadStream(path, [options])#

새로운 ReadStream 객체를 반환한다. (Readable Stream를 봐라)

options는 다음의 기본값을 가진 객체다.

{ flags: 'r',
  encoding: null,
  fd: null,
  mode: 0666,
  autoClose: true
}

options는 전체 파일 대신 읽어드릴 파일의 범위인 startend를 포함할 수 있다. startend 둘 다 포함하고 0부터 시작한다. encoding'utf8', 'ascii', 'base64'가 될 수 있다.

autoClose가 false이면 오류가 발생하더라도 파일 디스크립터를 닫지 않는다. 파일 디스크립터를 닫고 파일 디스크립터가 새는 문제가 없도록 하는 것은 개발자의 책임이다. autoClose를 true로 설정하면(기본 동작) errorend에서 파일 디스크립터를 자동으로 닫을 것이다.

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+를 사용해야 한다.

Class: 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], [...])#

모든 아규먼트를 합쳐서 최종 경로로 정규화한다.

아규먼트는 문자열이어야 한다. v0.8에서 문자열이 아닌 아규먼트는 경고 없이 무시한다. v0.10 이상에서는 예외를 던진다.

예제:

path.join('/foo', 'bar', 'baz/asdf', 'quux', '..')
// returns
'/foo/bar/baz/asdf'

path.join('foo', {}, 'bar')
// throws exception
TypeError: Arguments to path.join must be strings

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#

플랫폼의 파일 구분자. '\\''/'이다.

*nix의 예제:

'foo/bar/baz'.split(path.sep)
// returns
['foo', 'bar', 'baz']

윈도우의 예제:

'foo\\bar\\baz'.split(path.sep)
// returns
['foo', 'bar', 'baz']

path.delimiter#

플랫폼에 특화된 경로 구분자인 ;':'이다.

*nix의 예제:

console.log(process.env.PATH)
// '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin'

process.env.PATH.split(path.delimiter)
// returns
['/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/bin']

윈도우의 예제:

console.log(process.env.PATH)
// 'C:\Windows\system32;C:\Windows;C:\Program Files\nodejs\'

process.env.PATH.split(path.delimiter)
// returns
['C:\Windows\system32', 'C:\Windows', 'C:\Program Files\nodejs\']

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: 네트워크 연결에 바인딩할 로컬 인터페이스.

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 서버를 생성하는 데 사용한다.

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.unref()#

서버에 unref를 호출하는 것은 해당 서버가 이벤트 시스템에서 유일하게 활성화된 서버인 경우 프로그램이 종료되도록 할 것이다. 서버에 이미 unref를 호출했다면 다시 unref를 호출해도 아무런 영향이 없다.

server.ref()#

unref와는 반대로 이미 unref된 서버에 ref를 호출하는 것은 해당 서버가 유일하게 남은 서버인 경우 프로그램이 종료되지 않도록 할 것이다.(기본 동작이다) 서버에 이미 ref를 호출했다면 다시 ref를 호출해도 아무런 영향이 없을 것이다.

server.maxConnections#

서버의 연결 수가 많아졌을 때 연결을 거절하려면 이 프로퍼티를 설정해라.

child_process.fork()로 자식에 소켓을 보냈을 때 이 옵션을 사용하는 것은 권하지 않는다.

server.connections#

이 함수는 폐기되었다. 대신 [server.getConnections()][]를 사용해라. 서버의 동시접속 수.

child_process.fork()로 자식 프로세스에 소켓을 보냈을 때 이 값은 null이 된다. 현재 활성화된 연결의 수를 얻거나 포크 하려면 대신 비동기 server.getConnections를 사용해라.

net.Server는 다음 이벤트를 가진 EventEmitter이다.

server.getConnections(callback)#

비동기적으로 서버의 현재 연결 수를 가져온다. 포크(fork)하려보 소켓을 보냈을 때 동작한다.

콜백은 두 개의 인자 errcount를 받아야 한다.

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
  allowHalfOpen: false,
  readable: false,
  writable: false
}

fd로 이미 존재하는 소켓의 파일 디스크립터를 지정할 수 있다. 해당 소켓을 읽고 쓰기 가능하게 하려면 readable이나 writable를 각각 true로 설정해라.(주의: fd를 전달했을 때만 동작한다.) allowHalfOpen에 대해서는 createServer()'end' 이벤트를 참고해라.

socket.connect(port, [host], [connectListener])#

socket.connect(path, [connectListener])#

주어진 소켓에 대한 연결을 오픈한다. porthost를 전달하면 소켓은 TCP 소켓으로 열릴 것이고 host를 생략하면 localhost를 사용할 것이다. path를 전달하면 소켓은 해당 경로로의 유닉스 소켓으로 열릴 것이다.

net.createConnection이 소켓을 여는 한 이 메서드는 보통 필요하지 않다. 커스텀 Sokcet을 구현했을 때만 이 메서드를 사용해라.

이 함수는 비동기 함수이다. '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.unref()#

소켓에 unref를 호출하는 것은 해당 소켓이 이벤트 시스템에서 유일하게 활성화된 소켓일 때 프로그램이 종료되도록 할 것이다. 소켓에 이미 unref를 호출했다면 다시 unref를 호출해도 아무런 영향이 없을 것이다.

socket.ref()#

unref와는 반대로 이전에 unref된 소켓에 ref를 호출하는 것은 해당 소켓이 유일하게 남은 소켓일 때 프로그램이 종료되지 않도록 할 것이다.(기본 동작이다) 소켓에 이미 ref를 호출했다면 다시 ref를 호출해도 아무런 영향이 없을 것이다.

socket.remoteAddress#

원격 IP 주소의 문자열 표현이다. 예를 들면 '74.125.127.100''2001:4860:a005::68'와 같은 식이다.

socket.remotePort#

원격 포트의 숫자 표현이다. 예를 들면 80이나 21이다.

socket.localAddress#

원격 클라이언트가 접속한 로컬 IP 주소의 문자열 표현이다. 예를 들어 '0.0.0.0'를 리스닝하고 있고 클라이언트가 '192.168.1.1'에 접속했다면 이 값은 '192.168.1.1'가 될 것이다.

socket.localPort#

로컬 포트의 숫자 표현이다. 예를 들면 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.Socket#bind()의 동작은 v0.10에서 변경되어서 이제는 항상 비동기로 동작한다. 다음과 같이 작성했다면:

var s = dgram.createSocket('udp4');
s.bind(1234);
s.addMembership('224.0.0.114');

다음과 같이 변경해야 한다.

var s = dgram.createSocket('udp4');
s.bind(1234, function() {
  s.addMembership('224.0.0.114');
});

dgram.createSocket(type, [callback])#

  • type 문자열. 'udp4'나 'udp6'
  • callback 함수. message 이벤트에 리스너로 추가된다. 선택사항
  • 반환 값: Socket 객체

지정한 타입의 데이터그램 소켓을 생성한다. 유효한 타입은 udp4udp6이다.

message 이벤트의 리스너로 추가되는 선택사항인 콜백을 받는다.

데이터그램을 받으려면 socket.bind를 호출해라. socket.bind()는 임의의 포트에서 "모든 인터페이스" 주소에 바인딩한다. (udp4udp6 소켓 둘 다에 대해 맞는 것이다.) 그다음 socket.address().addresssocket.address().port로 주소와 포트를 획득할 수 있다.

Class: dgram.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 객체

오류가 생겼을 때 발생한다.

socket.send(buf, offset, length, port, address, [callback])#

  • buf Buffer 객체. 보내는 메시지다
  • offset 정수. 버퍼에서 메시지가 시작되는 오프셋.
  • length 정수. 메시지의 바이트 수
  • port 정수. 목적지 포트
  • address 문자열. 목적지 호스트 명이나 IP 주소
  • callback 함수. 메시지를 보냈을 때 호출될 콜백. 선택사항.

UDP 소켓에서 목적지 포트와 IP 주소는 반드시 지정해야 한다. address 파라미터에 문자열을 전달하고 이는 DNS로 처리될 것이다.

address를 지정하지 않거나 빈 문자열로 지정하면 '0.0.0.0''::0'를 사용한다. 이러한 기본값은 네트워크 설정에 따라 동작할 수도 있고 동작하지 않을 수도 있다. 목적지 주소를 명시적으로 지정하는 것이 가장 좋다.

소켓이 bind 호출로 이전에 바인딩 되지 않았다면 임의의 포트 번호를 할당받고 "모든 인터페이스" 주소에 바인딩 될 것이다. (udp4에는 0.0.0.0, udp6 소켓에는 ::0)

DNS 오류를 탐지하거나 buf 객체를 다시 사용할 수 있는 상태인지를 판단하기 위해서 선택사항인 콜백을 지정할 것이다. DNS 검색은 최소한 다음 tick까지 통신이 이뤄지는 시간 동안 연기될 것이다. 데이터그램이 전송되었는지 확실하게 아는 유일한 방법이 콜백을 사용하는 것이다.

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보다 큰 데이터그램을 전송하는 것은 동작하지 않는다. (데이터가 의도된 수신자에게 도달하지 않았다는 것을 소스에 알리지 않고 패킷을 경고 없이 버린다.)

socket.bind(port, [address], [callback])#

  • port 정수
  • address 문자열, 선택사항
  • callback 함수이고 파라미터는 없다. 선택사항. 바인딩이 완료되었을 때 실행된다.

UDP 소켓에서 port와 선택사항인 address에서 데이터그램을 받는다. address를 지정하지 않으면 운영체제는 모든 주소에서 받을 것이다. 바인딩이 완료되면 "listening" 이벤트가 발생하고 callback(지정한 경우)을 실행한다. "listening" 이벤트 리스너와 callback을 모두 지정해도 문제는 없지만 별로 유용하지도 않다.

바인딩 된 데이터그램 소켓은 데이터그램을 받기 위해서 노드 프로세스가 계속 동작하도록 유지한다.

바인딩이 실패하면 "error" 이벤트가 발생한다. 드문 경우이지만(예를 들면 닫힌 소켓을 바인딩하는 경우) 이 메서드가 Error를 던질 수도 있다.

UDP 서버가 41234 포트에서 받는 예제:

var dgram = require("dgram");

var server = dgram.createSocket("udp4");

server.on("error", function (err) {
  console.log("server error:\n" + err.stack);
  server.close();
});

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

socket.close()#

의존하는 소켓을 닫고 소켓에서 데이터를 받는 것을 멈춘다.

socket.address()#

소켓에 대한 주소 정보를 담고 있는 객체를 반환한다. UDP 소켓에서 이 객체는 addressfamilyport를 담고 있을 것이다.

socket.setBroadcast(flag)#

  • flag 불리언

SO_BROADCAST 소켓 옵션을 설정하거나 없앤다. 이 옵션을 설정하면 로컬 인터페이스의 브로드캐스트 주소로 UDP 패킷을 보낼 것이다.

socket.setTTL(ttl)#

  • ttl 정수

IP_TTL 소켓 옵션을 설정한다. TTL은 "Time to Live"를 의미하지만, 이 상황에서 TTL은 패킷이 거쳐 가도 되는 IP 홉(hop)의 수를 지정한다. 패킷이 지나쳐가는 각 라우터나 게이트웨이는 TTL을 감소시킨다. TTL이 라우터에 의해서 0까지 줄어들면 더는 전송되지 않을 것이다. TTL 값을 변경하는 것은 보통 네트워크 검사나 멀티캐스팅 될 때 이뤄진다.

setTTL()의 아규먼트는 1부터 255 사이의 홉 수이다. 대부분 시스템에서 기본값은 64이다.

socket.setMulticastTTL(ttl)#

  • ttl 정수

IP_MULTICAST_TTL 소켓 옵션을 설정한다. TTL은 "Time to Live"를 의미하지만, 이 상황에서 TTL은 특히 멀티캐스트 트래픽에 대해서 패킷이 거쳐 가도 되는 IP 홉(hop)의 수를 지정한다. 패킷이 지나쳐가는 각 라우터나 게이트웨이는 TTL을 감소시킨다. TTL이 라우터에 의해서 0까지 줄어들면 더는 전송되지 않을 것이다.

setMulticastTTL()의 아규먼트는 0부터 255 사이의 홉(hop) 수이다. 대부분 시스템에서 기본값은 1이다.

socket.setMulticastLoopback(flag)#

  • flag 불리언

IP_MULTICAST_LOOP 소켓 옵션을 설정하거나 제거한다. 이 옵션을 설정하면 멀티캐스트 패킷도 로컬 인터페이스에서 받을 것이다.

socket.addMembership(multicastAddress, [multicastInterface])#

  • multicastAddress 문자열
  • multicastInterface 문자열, 선택사항

IP_ADD_MEMBERSHIP 소켓 옵션과 함께 멀티캐스트 그룹에 합류하도록 커널에 알려준다.

multicastInterface를 지정하지 않으면 운영체제는 회원을 유효한 모든 인터페이스에 추가하려고 할 것이다.

socket.dropMembership(multicastAddress, [multicastInterface])#

  • multicastAddress 문자열
  • multicastInterface 문자열, 선택사항

addMembership와는 반대로 IP_DROP_MEMBERSHIP 소켓 옵션과 함께 멀티캐스트 그룹에서 나오도록 커널에 명령한다. 이는 소켓이 닫히거나 진행이 종료되었을 때 커널이 자동으로 호출할 것이므로 대부분의 애플리케이션은 이 함수를 호출할 필요가 없을 것이다.

multicastInterface를 지정하지 않으면 운영체제는 회원을 유효한 모든 인터페이스에서 버리려고 할 것이다.

socket.unref()#

소켓에서 unref를 호출하는 것은 해당 소켓이 이벤트 시스템에서 유일하게 활성화된 소켓인 경우 프로그램이 종료될 수 있게 할 것이다. 소켓에 이미 unref가 호출됐다면 다시 unref를 호출해도 아무런 영향이 없을 것이다.

socket.ref()#

unref과는 반대로 이전에 unref된 소켓에 ref를 호출하면 해당 소켓이 유일하게 남아있는 소켓인 경우 프로그램이 종료되지 않도록 할 것이다.(기본 동작) 소켓에 이미 ref를 호출했다면 ref를 다시 호출하는 것은 아무런 영향이 없을 것이다.

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.IncomingMessage의 인스턴스이고 responsehttp.ServerResponse의 인스턴스다.

Event: 'connection'#

function (socket) { }

새로운 TCP 스트림이 생성되었을 때 발생한다. socketnet.Socket 타입의 객체다. 보통 사용자들은 이 이벤트에 접근하지 않을 것이다. 특히 프로토콜 파서가 소켓에 연결되는 방법 때문에 소켓이 readable 이벤트를 발생시키지 않을 것이다. 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, socket) { }

클라이언트 연결에서 'error' 이벤트가 발생하면 이 이벤트가 진행된다.

socket은 오류가 발생한 net.Socket 객체이다.

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으로 설정하면 제한을 두지 않는다.

server.setTimeout(msecs, callback)#

  • msecs Number
  • callback Function

소켓의 타임아웃 값을 설정하고 타임아웃이 발생하면 인자로 소켓을 넘기면서 Server 객체에 'timeout' 이벤트를 발생시킨다.

Server 객체에 'timeout' 이벤트 리스너가 있으면 타임아웃이 발생한 소켓을 인자로 전달하면서 이벤트 리스너를 호출할 것이다.

기본적으로 Server의 타임아웃 값은 2분이고 타임아웃이 발생했을 때 소켓을 자동으로 없앤다. 하지만 Server의 'timeout' 이벤트에 콜백을 할당하면 소켓 타임아웃을 처리하는 책임은 개발자에게 있다.

server.timeout#

  • Number Default = 120000 (2 minutes)

소켓이 타임아웃 되었다고 판단하기 전에 활동하지 않는 밀리 초 시간.

소켓 타임아웃 로직이 연결에 설정되므로 이 값을 변경해도 서버에 이미 존재하는 연결이 아닌 새로운 연결에만 영향을 끼칠 것이다.

들어오는 요청에 자동 타임아웃 동작을 모두 비활성화하려면 0으로 설정해라.

Class: http.ServerResponse#

사용자가 아니라 HTTP 서버가 내부적으로 생성하는 객체다. 'request' 이벤트의 두 번째 파라미터로 전달된다.

응답은 Writable Stream 인터페이스를 구현했다. 이 클래스는 다음의 이벤트를 가지고 있는 EventEmitter이다.

Event: 'close'#

function () { }

response.end()가 호출되기 전이나 플러시할 수 있을 때 의존하는 연결을 종료했다는 것을 나타낸다.

Event: 'finish'#

function () { }

응답을 보냈을 때 발생한다. 조금 더 자세히 얘기하면 응답 헤더와 바디의 마지막 부분을 네트워크를 통해서 전송하기 위해서 운영체제에 전달했을 때 이 이벤트가 발생한다. 이는 클라이언트는 아직 아무것도 받지 않았다는 것을 의미하지 않는다.

이 이벤트 뒤에는 응답 객체에서 어떤 이벤트도 더는 발생하지 않을 것이다.

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.setTimeout(msecs, callback)#

  • msecs Number
  • callback Function

Socket의 타임아웃값을 msecs로 설정한다. 콜백을 지정하면 응답 객체의 'timeout' 이벤트 리스너로 추가한다.

요청, 응답, 서버에 'timeout' 리스너를 추가하지 않으면 타임아웃 되었을 때 소켓을 제거한다. 요청, 응답, 서버의 'timeout' 이벤트에 핸들러를 할당하면 타임아웃된 소켓을 처리하는 것은 개발자의 몫이다.

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.headersSent#

불리언 값(읽기 전용). 헤더를 보냈으면 true이고 보내지 않았으면 false이다.

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: 기본 인증이다. 예를 들면 Authorization 헤더를 처리하는 'user:password'이다.
  • agent: Agent 동작을 제어한다. 에이전트를 사용했을 때 요청은 기본적으로 Connection: keep-alive가 될 것이다. 가능한 값은 다음과 같다.
    • undefined (default): 이 호스트와 포트에 대한 global Agent를 사용한다.
    • Agent object: 명시적으로 Agent에 전달된 객체를 사용한다.
    • false: Agent와 함께 연결 풀을 사용하지 않는다. 기본값은 Connection: close에 요청한다.

선택사항인 callback 파라미터는 'response' 이벤트의 일회성 리스터로 추가될 것이다.

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);
});

// 요청 바디에 데이터를 쓴다
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.IncomingMessage 인스턴스를 아규먼트로 받아서 실행된다.

'response' 이벤트 가운데 응답 객체에 리스너들을 추가할 수 있다. 특히 'data' 이벤트를 받기 위해 추가할 수 있다.

'response' 핸들러를 추가하지 않았다면 응답을 완전히 버릴 것이다. 하지만 'response' 이벤트 핸들러를 추가했다면 'readable' 이벤트가 발생할 때마다 response.read()를 호출하거나 'data' 핸들러를 추가하거나 .resume() 메서드를 호출해서 응답객체의 데이터를 반드시 소비해야 한다. 데이터가 소비될 때까지 'end' 이벤트는 발생하지 않을 것이다. 또한, 데이터를 읽는 동안 메모리를 소비하므로 'process out of memory' 오류가 발생할 수도 있다.

Note: Node는 Content-Length와 전송된 바디의 길이가 같은지 같지 않은지 확인하지 않는다.

요청은 Writable Stream 인터페이스를 구현했다. 이는 다음 이벤트를 가진 EventEmitter이다.

Event 'response'#

function (response) { }

해당 요청에 대한 응답을 받았을 때 발생한다. 이 이벤트는 딱 한 번만 발생한다. response 아규먼트는 http.IncomingMessage의 인스턴스가 될 것이다.

옵션:

  • 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.IncomingMessage#

IncomingMessage 객체는 http.Serverhttp.ClientRequest가 생성하고 'request''response' 이벤트에 각각 첫 번째 인자로 전달된다. 응답 상태, 헤더, 데이터에 접근할 때 사용한다.

이는 Readable Stream 인터페이스를 구현했고 다음의 추가적인 이벤트, 메서드, 프로퍼티를 구현했다.

Event: 'close'#

function () { }

의존하는 연결이 닫혔는지를 나타낸다. 'end'처럼 이 이벤트는 응답마다 딱 한 번만 발생한다.

message.httpVersion#

서버 요청의 경우 HTTP 버전을 클라이언트가 보낸다. 클라이언트 응답의 경우 서버에 연결된 HTTP 버전이다. 아마 '1.1''1.0' 둘 중 하나일 것이다.

response.httpVersionMajor는 첫 번째 정수이고 response.httpVersionMinor는 두 번째 정수이다.

message.headers#

요청/응답 헤더 객체.

읽기 전용인 헤더 이름과 값의 맵이다. 헤더 이름은 소문자이다. 예제:

// Prints something like:
//
// { 'user-agent': 'curl/7.22.0',
//   host: '127.0.0.1:8000',
//   accept: '*/*' }
console.log(request.headers);

message.trailers#

요청/응답의 trailers 객체다. 'end' 이벤트 후에만 존재한다.

message.setTimeout(msecs, callback)#

  • msecs Number
  • callback Function

message.connection.setTimeout(msecs, callback)를 호출한다.

message.method#

http.Server에서 얻은 요청에만 유효하다.

요청 메서드의 문자열 표현. 읽기 전용이다. 예제: 'GET', 'DELETE'.

message.url#

http.Server에서 얻은 요청에만 유효하다..

요청 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' }

message.statusCode#

http.ClientRequest에서 얻은 응답에만 유효하다.

3자리 숫자의 HTTP 응답 상태코드. 예를 들면 404등이다.

message.socket#

연결과 관련된 net.Socket 객체이다.

HTTPS를 지원한다면 클라이언트의 자세한 인증을 얻을 때 request.connection.verifyPeer()와 request.connection.getPeerCertificate()를 사용해라.

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 요청을 보내기 연결단계에서 이뤄진다. 기본값은 true이다.
  • secureProtocol: 사용할 SSL 방식. 예를 들어 SSL 버전 3을 사용하려면 SSLv3_method이다. 사용 가능한 값은 설치한 OpenSSL에 따라 다르고 상수 SSL_METHODS에 정의되어 있다.

이러한 옵션들을 지정하려면 커스텀 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을 받아서 앵커태그를 위해서 브라우저처럼 처리한다. 예제:

url.resolve('/one/two/three', 'four')         // '/one/two/four'
url.resolve('http://example.com/', '/one')    // 'http://example.com/one'
url.resolve('http://example.com/one', '/two') // 'http://example.com/two'

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에 데이터를 스트리밍 하는 것이 가능하다. (일부 프로그램은 내부적으로 라인 버퍼링 I/O (line-buffered I/O)를 사용한다. 이는 node.js에는 영향을 주지 않지만 자식 프로세스에 보낸 데이터가 즉시 소비되지 않는다는 것을 의미한다.)

자식 프로세스를 생성하려면 require('child_process').spawn()require('child_process').fork()를 사용해라. 각각의 의미는 약간 다른데 아래에서 설명한다.

Class: ChildProcess#

ChildProcessEventEmitter이다.

자식 프로세스들은 자신들과 연관된 세 가지 스트림 child.stdin, child.stdout, child.stderr를 항상 가진다. 이 세 스트림은 부모 프로세스의 stdio 스트림을 공유하거나 파이프로 연결될 수 있는 스트림을 구분할 것이다.

ChildProcess 클래스는 직접 사용하도록 만들어진 것이 아니다. 자식 프로세스의 인스턴스를 생성하려면 spawn()fork()를 사용해라.

Event: 'error'#

  • err Error Object 오류.

다음과 같은 상황에 발생한다.

  1. 프로세스를 생성(spawn)할 수 없거나
  2. 프로세스를 죽일 수 없거나
  3. 어떤 이유로든지 자식 프로세스에 메시지 전송이 실패한 경우

오류가 발생한 후에 exit-이벤트가 발생할 수도 있고 발생하지 않을 수도 있다는 점을 주의해라. 함수를 실행하려고 두 이벤트를 리스닝하고 있다면 함수가 두 번 호출되지 않도록 해야 한다.

ChildProcess#kill()ChildProcess#send()도 참고해라.

Event: 'exit'#

  • code 숫자 정상적으로 종료되는 경우의 종료코드.
  • signal String 부모가 자식프로세스를 죽일 때 자식 프로세스에 전달하는 신호.

이 이벤트는 자식프로세스가 종료된 후에 발생한다. 프로세스가 정상적으로 종료된다면 code는 프로세스의 최종 종료코드이고 정상적으로 종료되지 않았다면 null이다. 프로세스가 신호를 받아서 종료되었다면 signal는 문자열로 된 신호의 이름이고 신호를 받아서 종료되지 않았다면 null이다.

자식 프로세스의 stdio 스트림은 여전히 열려있을 것이다.

노드가 'SIGINT''SIGTERM'에 대한 신호 핸들러를 만들었으므로 이러한 신호를 받아서는 종료되지(terminate) 않고 빠져나갈(exit) 것이다.

waitpid(2)를 봐라.

Event: 'close'#

  • code Number 정상적으로 종료되었다면 종료 코드다.
  • signal String 부모가 자식 프로세스를 종료했다면 자시 프로세스에 전달된 신호이다.

이 이벤트는 자식 프로세스의 stdio 스트림이 모두 종료되었을 때 발생한다. 이 이벤트는 다중 프로세스가 같은 stdio 스트림을 공유할 수도 있으므로 'exit'와는 다르다.

Event: 'disconnect'#

이 이벤트는 부모나 자식의 .disconnect() 메서드를 사용한 수에 발생한다. 연결이 끊긴 후에는 더는 메시지를 보낼 수 없고 .connected가 false가 된다.

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.connected#

  • Boolean `.disconnect'가 호출되면 false로 설정한다

.connected가 false이면 더는 메시지를 보낼 수 없다.

child.kill([signal])#

  • signal 문자열

자식 프로세스에 신호를 보낸다. 아규먼트를 전달하지 않으면 프로세스는 'SIGTERM'를 보낼 것이다. 사용할 수 있는 신호 목록은 signal(7)를 참고해라.

var spawn = require('child_process').spawn,
    grep  = spawn('grep', ['ssh']);

grep.on('close', function (code, signal) {
  console.log('child process terminated due to receipt of signal '+signal);
});

// 프로세스에 SIGHUP를 보낸다
grep.kill('SIGHUP');

신호를 전송할 수 없을 때는 'error'를 발생시킬 것이다. 종료된 자식 프로세스에 신호를 보내는 것은 오류가 아니지만 의도치 않은 결과가 될 수 있다. PID(프로세스 ID)가 다른 프로세스에 재할당되었다면 신호는 해당 프로세스로 전달될 것이고 무슨 일이 일어날지 아무도 짐작할 수 없다.

함수의 이름이 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는 채널에서 메시지를 받을 때마다 객체를 발생시킬 것이다.

부모와 자식 모두에서 send() 메서드는 동기라는 점을 유념해라. 청크가 큰 데이터를 보내는 것은 권장하지 않는다.(대신 파이프를 사용할 수 있다. child_process.spawn 참고.)

{cmd: 'NODE_foo'} 메시지를 보냈을 때 특별한 경우가 있다. cmd 프로퍼티에 NODE_ 접두사가 있는 모든 메시지는 node 코어에서 사용되는 내부 메시지이므로 message 이벤트에서 발생하지 않을 것이다. 접두사가 있는 메시지들은 internalMessage 이벤트를 발생시킨다. 이는 별도의 공지 없이 변경되므로 이 기능을 사용하지 말아야 한다는 것을 의미한다.

child.send()sendHandle 옵션은 TCP 서버나 소켓 객체를 다른 프로세스에 보내는 용도이다. 자식 프로세스는 message 이벤트의 두 번째 아규먼트로 이 객체를 받을 것이다.

메시지를 보낼 수 없다면 'error' 이벤트를 발생시킨다. 예를 들어 자식프로세스가 이미 종료되었다면 보낼 수 없다.

Example: sending server object#

다음은 서버를 전송하는 예제다.

var child = require('child_process').fork('child.js');

// 서버객체를 열고 handle을 전송한다.
var server = require('net').createServer();
server.on('connection', function (socket) {
  socket.end('부모가 처리한다');
});
server.listen(1337, function() {
  child.send('server', server);
});

자식프로세스는 다음과 같이 서버 객체를 받는다.

process.on('message', function(m, server) {
  if (m === 'server') {
    server.on('connection', function (socket) {
      socket.end('자식이 처리한다');
    });
  }
});

서버는 이제 부모와 자식 사이에서 공유된다. 이는 연결들이 부모와 자식에서 모두 다룰 수 있다는 의미이다.

dgram 서버에서 워크플로우는 완전히 같다. 여기서 connection 대신 message 이벤트를 리스닝하고 server.listen 대신 server.bind를 사용한다. (현재는 UNIX 플랫폼에서만 지원한다.)

Example: sending socket object#

다음은 소켓을 전송하는 예제다. 이 예제는 두 자식 프로세스를 생성하고 "특별한" 자식 프로세스에 소켓을 전송해서 VIP인 원격주소 74.125.127.100의 연결을 다룬다. 다른 소켓들은 "보통의" 프로세스로 갈 것이다.

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 연결을 닫으면서 자식 프로세스에 더는 유지되는 연결이 없도록 안전하게 자식 프로세스를 종료한다. 이 메서드를 호출하면 부모와 자식 모두에서 .connected 플래그가 false로 설정될 것이고 더는 메시지를 보낼 수 없다.

메시지를 받는 프로세스에 더는 메시지가 없을 때 'disconnect' 이벤트가 발생할 것이고 대부분을 즉각적으로 발생한다.

자식 프로세스에서 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('close', 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('close', 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('close', 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로 복제된다. 스트림은 반드시 의존하는 디스크립터를 가져야 한다.('open' 이벤트가 발생할 때까지 파일 스트림을 하지 않는다.)
  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은 새로운 스트림이 생성되어야 한다는 것을 의미한다. 사용할 때는 위험을 감수해라.

child_process.exec()child_process.fork()도 참고해라.

child_process.exec(command, [options], callback)#

  • command 문자열 실행할 명령어로 전달할 아규먼트는 공백으로 구분한다
  • options 객체
    • cwd 문자열 자식 프로세스의 현재 워킹 디렉터리
    • 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 문자열 자식 프로세스의 현재 워킹 디렉터리
    • env 객체 환경변수의 키-밸류 쌍
    • encoding 문자열 (기본값: 'utf8')
    • timeout 숫자 (기본값: 0)
    • maxBuffer 숫자 (기본값: 200*1024)
    • killSignal 문자열 (기본값: 'SIGTERM')
  • callback 함수 프로세스가 종료되었을 때 출력과 함께 호출된다.
    • error Error
    • stdout Buffer
    • stderr Buffer
  • 반환값: ChildProcess 객체

하위 쉘을 실행하는 대신에 지정한 파일을 직접 실행한다는 점을 제외하면 child_process.exec()와 유사하다. child_process.exec보다 약간 의존적이게 만든다. 이는 같은 옵션을 가진다.

child_process.fork(modulePath, [args], [options])#

  • modulePath 문자열 자식프로세스에서 실행될 모듈
  • args 배열 문자열 아규먼트의 목록
  • options 객체
    • cwd 문자열 자식프로세스의 현재 워킹 디렉토리
    • env 객체 환경변수의 키-밸류 쌍
    • encoding 문자열 (기본값: 'utf8')
    • execPath String 자식 프로세스를 생성하는 데 사용하는 실행 가능한 경로
    • execArgv Array 실행파일에 전달하는 문자열 인자 목록 (기본값: process.execArgv)
    • silent Boolean true이면 자식의 stdin, stdout, stderr가 부모로 파이프로 연결될 것이다. false이면 stdin, stdout, stderr를 부모에서 상속받는다. 자세한 내용은 spawn()stdio에서 "pipe"와 "inherit" 옵션을 봐라. (기본값은 false)
  • 반환 값: ChildProcess 객체

이는 Node 프로세스를 생성하기(spawn) 위해 spawn() 기능의 특별한 경우이다. 게다가 보통의 ChildProcess 인스턴스에서 모든 메서드를 가지려고 반환된 객체는 내장된 통신 채널을 가진다. 자세한 내용은 child.send(message, [sendHandle])를 참고해라.

이러한 자식 노드들도 V8의 완전한 새 인스턴스이다. 새로운 노드마다 최소한 30ms의 구동시간과 10mb의 메모리를 가정해보자. 즉, 수천 개의 노드를 생성할 수 없다.

options객체의 execPath 프로퍼티는 현재 실행된 node 대신 자식을 위한 프로세스를 생성할 수 있게 한다. 이는 조심히 수행되어야 하고 기본적으로 자식프로세스의 NODE_CHANNEL_FD 환경변수를 나타내는 fd에 얘기할 것이다. 이 fd의 입력과 출력은 라인으로 구분된 JSON 객체일 것이다.

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, [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)#

폐기됐다. tty.ReadStream#setRawMode()를 사용하라(예시: 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.Zlib#

zlib 모듈이 익스포트하지 않는다. 여기서 문서로 만든 이유는 compressor/decompressor 클래스의 기반 클래스이기 때문이다.

zlib.flush(callback)#

지연되고 있는 데이터를 내보낸다. 경박하게 호출하지 마라. 너무 이른 플러시는 압축 알고리즘의 효율성에 부정적인 영향을 준다.

zlib.reset()#

compressor/decompressor를 원래의 기본값으로 리셋한다. inflate와 deflate 알고리즘에만 적용할 수 있다.

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#

모든 클래스는 옵션 객체를 아규먼트로 받고 생략 가능하다(단축 메소드는 기본값을 사용한다.).

어떤 옵션은 압축 클래스에만 사용하고 압축을 푸는 클래스에서는 무시한다.

  • flush (기본값: zlib.Z_NO_FLUSH)
  • chunkSize (기본값: 16*1024)
  • windowBits
  • level (압축 전용)
  • memLevel (압축 전용)
  • strategy (압축 전용)
  • dictionary (deflate/inflate 전용, 기본값은 빈 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.endianness()#

CPU의 엔디언(endianness)을 반환한다. 가능한 값은 "BE""LE"이다.

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분 로드 평균값을 배열에 담아 반환한다.

로드 평균값은 시스템 활동을 측정한 것으로 운영체제가 계산해서 작은 수로 나타낸 것이다. 경험적으로 로드 평균값은 시스템의 논리 CPU 수보다 적은 것이 이상적이다.

로드 평균값은 완전히 UNIX적인 개념이다. 윈도우 플랫폼에는 로드 평균값 같은 개념이 없어서 윈도우 플랫폼에서는 이 함수가 항상 [0, 0, 0]를 반환한다.

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를 제거

아직 로딩되지 않은 파일(모듈)에 breakpoint를 설정할 수도 있다.

% ./node debug test/fixtures/break-in-module/main.js
< debugger listening on port 5858
connecting to port 5858... ok
break in test/fixtures/break-in-module/main.js:1
  1 var mod = require('./mod.js');
  2 mod.hello();
  3 mod.hello();
debug> setBreakpoint('mod.js', 23)
Warning: script 'mod.js' was not loaded yet.
  1 var mod = require('./mod.js');
  2 mod.hello();
  3 mod.hello();
debug> c
break in test/fixtures/break-in-module/mod.js:23
 21
 22 exports.hello = function() {
 23   return 'hello from module';
 24 };
 25
debug>

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 디버거가 켜지고 접근해서 사용할 수 있다.

프로세스가 디버그 모드로 설정되면 노드 디버거로 접속할 수 있다. 디버거에 pid나 URI로 접속할 수 있다. 문법은 다음과 같다.

  • node debug -p <pid> - pid로 프로세스에 연결한다
  • node debug <URI> - localhost:5858 같은 URI로 프로세스에 연결한다.

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_DEBUG=cluster node server.js
23521,Master Worker 23524 online
23521,Master Worker 23526 online
23521,Master Worker 23523 online
23521,Master Worker 23528 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
  • execArgv Array 노드 실행파일에 전달할 문자열 인자 목록 (기본값=process.execArgv)
  • exec String 워커 파일의 경로. (기본값=process.argv[1])
    • args Array 워커에 넘겨지는 스트링 아규먼트. (기본값=process.argv.slice(2))
    • silent Boolean 워커의 output을 부모의 stdio로 보낼지 말지 여부 (기본값=false)

.setupMaster()를 한번만 호출할 수 있으므로 설정된 뒤에는 효과적으로 값이 고정된다.

이 객체를 직접 수정하지 말아야 한다.

cluster.isMaster#

  • Boolean

프로세스가 마스터이면 true를 리턴한다. 이 프로퍼티는 process.env.NODE_UNIQUE_ID 값을 이용하는데 process.env.NODE_UNIQUE_ID이 undefined이면 isMaster는 true이다.

cluster.isWorker#

  • Boolean

프로세스가 마스터가 아니면 true이다.(cluster.isMaster의 반대다.)

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' 이벤트가 발생한 경우 마스터의 cluster에서 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);
});

addressType는 다음 값 중 하나이다.

  • 4 (TCPv4)
  • 6 (TCPv6)
  • -1 (유닉스 도메인 소켓)
  • "udp4", "udp6" (UDP v4, v6)

Event: 'disconnect'#

  • worker Worker 객체

워커의 IPC 채널의 연결이 끊긴 뒤에 발생한다. 이 이벤트는 워커가 안전하게 종료되거나 죽거나 수동으로 연결이 끊긴(worker.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) {
  console.log('worker %d died (%s). restarting...',
    worker.process.pid, signal || code);
  cluster.fork();
});

child_process event: 'exit'를 봐라.

Event: 'setup'#

.setupMaster()가 처음 호출될 때 발생한다.

cluster.setupMaster([settings])#

  • settings Object
    • exec String 워커 파일의 경로. (기본값=process.argv[1])
    • args Array 워커에 넘겨지는 스트링 아규먼트. (기본값=process.argv.slice(2))
    • silent Boolean 워커의 output을 부모의 stdio로 보낼지 말지 여부. (기본값=false)

setupMaster는 'fork'의 기본 행동을 수정하는 데 사용한다. 일당 호출되면 cluster.settings에서 설정정보가 유지될 것이다.

Note that:

  • .setupMaster()를 처음 호출할 때만 효과가 있고 이어진 .setupMaster() 호출은 무시된다.
  • 위의 이유로 워커마다 커스터마이징하는 유일한 속성은 .fork()함수에서 env로 전달해야 한다.
  • 기본값을 구성하기 위해서 .fork()는 내부적으로 .setupMaster()를 호출하므로 반드시 .fork()를 호출하기 전에 .setupMaster()를 호출해야 적용된다.

예제:

var cluster = require("cluster");
cluster.setupMaster({
  exec : "worker.js",
  args : ["--use", "https"],
  silent : true
});
cluster.fork();

이 함수는 마스터 프로세스에서만 호출할 수 있다.

cluster.fork([env])#

  • env Object 워커 프로세스의 환경변수에 추가할 키/값 쌍.
  • return Worker 객체

워커 프로세스를 하나 만든다(spawn).

이 함수는 마스터 프로세스에서만 호출할 수 있다.

cluster.disconnect([callback])#

  • callback Function 모든 워커가 Disconnect되고 핸들러가 닫히면 호출되는 함수

cluster.workers의 워커마다 .disconnect()를 호출한다.

워커의 연결이 끊길 때 내부의 모든 핸들이 닫으면서 대기 중인 다른 이벤트가 없다면 마스터 프로세스를 안전하게 종료할 수 있게 한다.

콜백을 인자로 넘기면 끝날 때 호출된다.

이 함수는 마스터 프로세스에서만 호출할 수 있다.

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필드가 키다. 모든 워커를 쉽게 순회할 수 있다. 이는 마스터 프로세스에서만 사용할 수 있다.

'disconnect''exit' 이벤트가 발생하기 바로 직전에 cluster.workers에서 워커를 제거한다.

// 모든 워커에 적용한다.
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에 저장된다. 워커에서 전역 process를 저장한다.

참고: Child Process module

process에서 'disconnect'이벤트가 발생하거나 .suicidetrue가 아니면 해당 워커는 process.exit(0)를 호출할 것이다. 이는 의도치 않은 연결종료를 막아준다.

worker.suicide#

  • Boolean

.kill().disconnect()를 호출해서 설정한다. 설정되기 전에는 undefined다.

불리언 값인 worker.suicide는 의도적인 종료와 실수로 종료하는 걸 구분해 준다. 마스터는 이 값에 기반을 둬서 워커를 다시 생성하지 않을지를 선택할 것이다.

cluster.on('exit', function(worker, code, signal) {
  if (worker.suicide === true) {
    console.log('Oh, it was just suicide\' – no need to worry').
  }
});

// 워커를 죽인다
worker.kill();

.kill()를 호출하고 나서 해당 워커가 죽으면 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.kill([signal='SIGTERM'])#

  • signal String 워커 프로세스에 보내는 kill 신호의 이름.

이 함수로 워커를 죽인다. 마스터에서는 worker.process의 연결을 종료해서 이를 수행하고 일단 연결이 종료되면 signal로 죽인다. 워커에서는 채널의 연결이 종료되고 0 코드와 함께 죽는다.

이 함수는 .suicide가 설정되도록 한다.

이 메서드는 하위 호환성을 위한 worker.destroy()라는 별칭이 존재한다.

워커에 process.kill()가 존재하지만 process.kill()는 이 함수가 아니고 kill이다.

worker.disconnect()#

워커에서 이 함수는 모든 서버를 닫고 각 서버의 'close' 이벤트를 기다린 후 IPC 채널의 연결을 종료한다.

마스터에서 내부적인 메시지를 워커에 보내서 워커가 .disconnect()를 호출하도록 한다.

이 함수는 .suicide가 설정되도록 한다.

서버가 닫힌 뒤에는 더는 새로운 연결을 받지 않지만 동작 중인 다른 워커가 연결을 받을 수도 있다. 기존에 존재하는 연결은 평소처럼 닫힐 수 있는 상태가 된다. 더는 연결이 존재하지 않을 때 (server.close() 참고) 워커에 대한 IPC 채널은 워커가 안전하게 죽을 수 있도록 닫힐 것이다.

위의 내용은 서버 연결에만 적용되고 클라이언트 연결은 워커가 자동으로 닫지 않고 disconnect는 종료하기 전에 닫기 위해서 기다리지 않는다.

워커에 process.disconnect가 존재하지만 process.disconnect는 이 함수가 아니고 disconnect이다.

오래 살아있는 서버 연결이 워커의 연결 종료를 막을 수 있으므로(메시지를 보내는 데 유용하다) 연결을 닫기 위해 애플리케이션의 특정 동작을 추가할 수 있다. 일정 시간 후에 disconnect 이벤트가 발생하지 않는다면 워커를 죽이도록 타임아웃을 구현하는데도 유용하다.

if (cluster.isMaster) {
  var worker = cluster.fork();
  var timeout;

  worker.on('listening', function(address) {
    worker.send('shutdown');
    worker.disconnect();
    timeout = setTimeout(function() {
      worker.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);

  process.on('message', function(msg) {
    if(msg === 'shutdown') {
      // initiate graceful close of any connections to server
    }
  });
}

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')

cluster.on('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!");
  }
});

Event: 'error'#

이 이벤트는 child_process.fork()의 것과 같다.

워커에서는 process.on('error')를 사용할 수도 있다.