[cookbook] Stream #3 이론편

Stream #3 이론편

nodejs 에서 가장 중요한 stream의 이론을 예제소스를 통해서 알아봅니다.

그림으로 Stream 이해

시냇물

stream

물의 흐름은 고여있지 않도록 흘러야 합니다.

데이타의 연속적인 흐름

pipe

크하하하하. 그림 정말 잘 구한거 같아욤. ㅎㅎ

파일 업로드 흐름을 브라우져에 표현

pipe2

여러개의 stream 객체들이 붙어서 웹으로 파일업로드 과정을 표현하는 아주 적절한 그림입니다.

우리가 인터넷에서 활동하는 모든것이 stream 이라는것을 잊지 마세요.

언제 사용되냐?

소스맛보기

소스를 맛봄으로써(?) 뭔가를 획득할 수도 있을것입니다. :)

nodejs에서의 stream 장단점

nodejs는 네트웍 프로그래밍을 쉽게 도와줄 수 있는 환경이라는것을 잊지 마세요. 이게 점점 이상하게 변질되고 있네요. ㅋㅋ (저조차도 ;;)

장점

단점

객체구조

Readable / Writeable Type

stream 에는 2가지 type이 존재합니다. 읽기(readable), 쓰기(writeable) 별거 없습니다.

readable

event

2가지 이벤트가 존재합니다.

method

data의 흐름제어할 수 있는 도구입니다.

writeable

event

method

이런 흐름은 stream을 상속하여 별도의 custom stream 객체를 만들 때 기본적으로 구현해야 하는 부분입니다. nodejs util api의 readStream 과 writeStream을 엮어주는 util.pump 작성코드를 보시면 이해가 되실 것입니다.

    exports.pump = function(readStream, writeStream, callback) {
      var callbackCalled = false;
      //
      function call(a, b, c) {
        if (callback && !callbackCalled) {
          callback(a, b, c);
          callbackCalled = true;
        }
      }
      //
      // data 이벤트
      // 이 부분은 가장 중요
      //
      readStream.addListener('data', function(chunk) {
        if (writeStream.write(chunk) === false) readStream.pause();
      });
      //
      // 이 부분도 중요
      // write의 drain과 read의 resume
      //
      writeStream.addListener('drain', function() {
        readStream.resume();
      });
      readStream.addListener('end', function() {
        writeStream.end();
      });
      readStream.addListener('close', function() {
        call();
      });
      readStream.addListener('error', function(err) {
        writeStream.end();
        call(err);
      });
      writeStream.addListener('error', function(err) {
        readStream.destroy();
        call(err);
      });
    };

소스코드를 통해서 이벤트의 발생순서를 알아보자.

input.txt 파일 만들기

$ cat > input.txt
abcdefghijklmnopqrstuvwxyz

$ cat input.txt 
abcdefghijklmnopqrstuvwxyz

stream.js

var fs = require('fs');
var colors = require('colors'); // 알아보기 쉽게

//
// bufferSize를 조절하는 이유는
// 크지 않은 데이타로 테스트하기 때문에
// 버퍼사이즈를 작게 하여
// 여러 이벤트를 확인하기 위함입니다.
//
var bufferSize = 8;

var read = fs.createReadStream('input.txt', {
  bufferSize: bufferSize
});
var write = fs.createWriteStream('output.txt');

read
  .on('data', function(data) {
    console.log('read: data'.red);
    console.log(data.toString()); // buffer type으로 받기 때문에 변경한다.
  })
  .on('end',   function()          { console.log('read: end');    })
  .on('error', function(e)         { console.log('read: error');  })
  .on('close', function()          { console.log('read: colse');  })
  .on('fd',    function(fd)        { console.log('read: fd');     })
  ;

write
  .on('drain', function()          { console.log('write: drain'.yellow); })
  .on('error', function(e)         { console.log('write: error' ); })
  .on('close', function()          { console.log('write: close' ); })
  .on('pipe',  function(src)       { console.log('write: pipe'  ); })
  ;

read.pipe(write);

결과

result

소스에서 확인할 내용

Stream을 my Style로 작성해보자.

pipe 인터페이스가 구현된 stream상속

file i/o 관련된 모든 nodejs lib은 모두 stream.js 를 사용합니다. 최고수준의 인터페이스라 생각하시면 됩니다. 제 생각에 사용하는 이유는 pipe 기능 때문이라 생각됩니다.

super class Stream을 상속받자. nodejs lib/stream.js

…. 이건 다음편에서 아 덥네요. ;;

결론

아 날씨 덥네요. ;; stream 은 nodejs에서 가장 중요한 부분이라 생각하시면 됩니다. networking programming 을 쉽게 해주는 환경인데, nodejs에 대해서 다른 부분부터 알고 싶어서 입문하시면, 나중에 불이익을 (메모리leak ..) 당할 수도 있습니다. ;; 그리고, stream은 굉장히 많은 아이디어를 내포하고 있습니다. 단일 pipe말고도, 3way pipe 같은 여러가지 stream을 엮으면 멋진 작품이 나올 수 있습니다. 다음편에서도 stream에 대한 예제를 통해서 공유하겠습니다.

감사합니다. KIN플 하세요. ~~~~

추천 관련글