LHJ

I'm a FE developer.

Streams and Buffers in Node.js

09 Dec 2020 » node

Streams and Buffers in Node.js

비디오, 대용량 파일 등과 같은 스트리밍 데이터를 처리하고 조작하려면 Node.js에 스트림이 필요합니다.
Node.js의 streams 모듈은 모든 스트림을 관리합니다.

이 기사에서는 Node.js의 스트림과 관련된 다음 개념에 초점을 맞출 것입니다.

이 기사에서 다루는 주제

  1. Node.js의 스트림 유형.
  2. 스트림의 버퍼.
  3. 스트림 및 이벤트 이미터.
  4. 읽기 스트림.
  5. 흐름 및 비 흐름 읽기 스트림.
  6. 읽기 스트림에 의한 버퍼 관리.

Types of Streams

Node에는 네 가지 유형의 스트림이 있습니다.

  1. 읽기 가능한 스트림 → 읽을 데이터 스트림 생성 (예 : 큰 파일을 청크 단위로 읽기).
  2. 쓰기 가능한 스트림 → 쓰기를위한 데이터 스트림 생성 (예 : 파일에 많은 양의 데이터 쓰기).
  3. 이중 스트림 → 읽기와 쓰기가 동시에 가능한 스트림을 만듭니다. 듀플렉스 스트림 (예 : 클라이언트와 서버 간의 소켓 연결)을 읽고 쓸 수 있습니다.
  4. 스트림 변환 → 읽기 및 쓰기가 가능한 스트림을 생성하지만 스트림을 읽고 쓰는 동안 데이터를 수정할 수 있습니다 (예 : 요청하는 동안 클라이언트 및 서버에서 데이터 압축).

스트림의 버퍼

스트림은 버퍼라는 개념에서 작동합니다.
버퍼는 소비 될 때까지 스트림이 일부 데이터를 보유하는 데 사용하는 임시 메모리입니다.
스트림에서 버퍼 크기는 버퍼 크기를 바이트 단위로 나타내는 숫자 인 스트림 인스턴스의 highWatermark 속성에 의해 결정됩니다.
기본적으로 Node의 버퍼 메모리는 String 및 Buffer에서 작동합니다.
자바스크립트 객체에서 버퍼 메모리가 작동하도록 만들 수도 있습니다.
그렇게하려면 스트림 객체의 objectMode 속성을 true로 설정해야합니다.
일부 데이터를 스트림으로 푸시하려고하면 데이터가 스트림 버퍼로 푸시됩니다.
푸시 된 데이터는 데이터가 사용될 때까지 버퍼에 있습니다.
버퍼가 가득 차고 데이터를 스트림에 푸시하려고하면 스트림이 해당 데이터를 받아들이지 않고 푸시 작업에 대해 거짓 값을 반환합니다.

스트림 및 EventEmitters

스트림은 EventEmitters를 확장합니다.
Node.js 스트림은 EventEmitter 클래스를 확장합니다.
데이터와 같은 이벤트를 듣고 스트림으로 끝낼 수 있습니다.
단순히 이벤트를 수신하려면 스트림에서 사용할 수있는 stream.on() 함수를 사용해야합니다.
Node.js의 EventEmitter에 대해 자세히 알아 보려면 다음 문서를 읽어보세요.

Node.js에서 스트림 읽기

스트리밍 데이터를 읽는 데 사용되는 스트림을 읽기 스트림이라고합니다.
읽기 스트림은 서버에서 파일을 읽거나 온라인 비디오를 스트리밍 할 수 있습니다.
내가 여러 곳에서 찾은 비유는 읽기 쉬운 스트림을 수도꼭지처럼 생각하는 것이며,이 비유는이 스트림에 완벽하게 맞습니다.
수도꼭지에는 누군가가 소비하는 물 (또는 데이터)이 통과합니다.
이 섹션에서는 큰 텍스트 파일에서 읽기 스트림을 만들어 실제로 작동하는지 확인합니다.

import { createReadStream, ReadStream } from 'fs';

var readStream: ReadStream = createReadStream('./data.txt');

readStream.on('data', chunk => {
  console.log('---------------------------------');
  console.log(chunk);
  console.log('---------------------------------');
});

readStream.on('open', () => {
  console.log('Stream opened...');
});

readStream.on('end', () => {
  console.log('Stream Closed...');
});

실행되면 다음 코드는 출력을 다음과 같이 제공합니다.

Stream opened...
---------------------------------
<Buffer 4c 6f 72 65 6d 20 69 70 73 75 6d 20 64 6f 6c 6f 72 20 73 69 74 20 61 6d 65 74 2c 20 63 6f 6e 73 65 63 74 65 74 75 72 20 61 64 69 70 69 73 63 69 6e 67 ... >
---------------------------------
---------------------------------
<Buffer 74 20 6e 75 6e 63 20 76 69 74 61 65 20 66 65 72 6d 65 6e 74 75 6d 2e 20 49 6e 20 75 74 20 61 72 63 75 20 74 65 6d 70 6f 72 2c 20 66 61 75 63 69 62 75 ... >
---------------------------------
---------------------------------
<Buffer 20 76 69 74 61 65 2c 20 65 67 65 73 74 61 73 20 69 64 20 73 65 6d 2e 20 44 6f 6e 65 63 20 75 74 20 75 6c 74 72 69 63 69 65 73 20 6c 6f 72 65 6d 2c 20 ... >
---------------------------------
Stream Closed...

버퍼 데이터는 스트림의 버퍼 메모리에있는 내용에 대한 바이트 데이터 일뿐입니다.

읽을 수있는 스트림 일시 중지 및 다시 시작

또한 스트림에서 pause() 및 resume() 함수를 호출하여 Node.js에서 스트림을 일시 중지하고 다시 시작할 수도 있습니다.
pause() 함수를 호출 한 결과, 스트림의 resume() 함수를 호출 할 때까지 데이터 이벤트가 트리거되지 않습니다.

흐르는 및 비 흐름 스트림

읽을 수있는 스트림에는 두 가지 유형이 있습니다.

  1. 흐르는 스트림 — 스트림의 데이터 이벤트를 사용하여 직접들을 수있는 데이터를 계속 전달하는 스트림입니다.
  2. 비 흐름 스트림 — 데이터를 자동으로 푸시하지 않는 스트림입니다.
    대신 스트림은 버퍼에 데이터를 저장하고이를 읽으려면 스트림의 read () 메서드를 명시 적으로 호출해야합니다.

위의 코드는 스트림의 데이터 이벤트를 방금 듣고 있었기 때문에 스트림에서 새로운 데이터가 나올 때마다 리스너가 자동으로 트리거되는 흐름 스트림의 예입니다.
다음은 비 흐름 스트림의 간단한 예입니다.

import { createReadStream, ReadStream } from 'fs';

var readStream: ReadStream = createReadStream('./data.txt');

setTimeout(() => {
  const data = readStream.read(10);
  console.log(data);
}, 10);

코드를 실행하면 다음과 같은 출력이 표시됩니다.

<Buffer 4c 6f 72 65 6d 20 69 70 73 75>

하지만 어떻게 작동 했습니까?

FS 모듈에서 createReadStream 메서드를 사용하여 읽기 스트림을 생성했기 때문입니다.

스트림이 생성되자마자 파일 데이터가 스트림 변수로 스트리밍되기 시작합니다.
또한 setTimeout 메서드를 사용하여 스트림이 버퍼에 일부 데이터를 채울 수있는 시간을 허용했습니다.
10 밀리 초 후에 setTimeout 콜백이 실행되고 10 (바이트 단위 크기)을 인수로 사용하여 호출 된 read () 메서드를 사용하여 버퍼의 처음 10 바이트를 읽습니다.

읽기 스트림에 의한 버퍼 관리

위 코드에서 console.log (data) 다음에 read () 함수를 다시 호출하고 새 데이터를 인쇄하면 데이터가 이전 로그와 다른 것을 알 수 있습니다.

import { createReadStream, ReadStream } from 'fs';

var readStream: ReadStream = createReadStream('./data.txt');

setTimeout(() => {
  const data = readStream.read(10);
  console.log(data);

  const data2 = readStream.read(10);
  console.log(data2);
}, 10);

위 코드의 경우 다음과 같이 출력됩니다.

<Buffer 4c 6f 72 65 6d 20 69 70 73 75>
<Buffer 6d 20 64 6f 6c 6f 72 20 73 69>

버퍼가 소비자가 읽은 후 데이터를 제거하기 때문에 로깅 된 값이 다릅니다.

따라서 read () 메서드의 첫 번째 호출에 대해 버퍼 데이터의 처음 10 바이트를 가져오고 read () 메서드의 두 번째 호출에 대해 현재 데이터가있는 실제 데이터의 11 번째 바이트에서 20 번째 바이트를 얻습니다.
버퍼의 처음 10 바이트.

결론

이것이 Node.js의 읽기 스트림에 관한 것입니다. 이 부분이 너무 길고 지루해지지 않도록 쓰기 가능한 스트림, 스트림 파이핑, 파이프 스트림에 대한 오류 처리, 스트림 이벤트 및 함수에 대해 설명하는 또 다른 블로그를 작성했습니다.