Streams는 데이터를 청크 단위로 나누어 순차적으로 처리하는 방식으로, 대용량 파일과 네트워크 페이로드를 메모리 낭비 없이 효율적으로 처리합니다.
스트림 유형 개요
| 유형 | 방향 | 설명 |
|---|---|---|
Readable |
읽기 전용 | 데이터를 읽고 소비할 수 있는 원본 소스 |
Writable |
쓰기 전용 | 데이터를 기록하고 출력할 수 있는 대상 |
Transform |
읽기 및 쓰기 | 통과하는 데이터를 변환하거나 수정하는 스트림 |
Duplex |
읽기 및 쓰기 | 독립된 읽기/쓰기 채널을 가진 양방향 스트림 (예: TCP 소켓) |
파이프라인 및 최신 stream/promises
최신 Node.js는 자동 오류 처리 및 리소스 정리를 제공하는 pipeline 함수를 지원합니다:
const { pipeline } = require('stream/promises');
const fs = require('fs');
const zlib = require('zlib');
async function compressFile() {
await pipeline(
fs.createReadStream('source.tar'),
zlib.createGzip(),
fs.createWriteStream('source.tar.gz')
);
console.log('압축 완료');
}
사용자 정의 스트림 생성
1. 사용자 정의 Readable 스트림
const { Readable } = require('stream');
function createTimestampStream() {
let count = 0;
return new Readable({
objectMode: true,
read() {
if (count >= 5) {
this.push(null); // 스트림 종료
} else {
this.push({ id: ++count, time: new Date() });
}
}
});
}
2. 사용자 정의 Transform 스트림
const { Transform } = require('stream');
const uppercaseTransformer = new Transform({
transform(chunk, encoding, callback) {
const transformed = chunk.toString().toUpperCase();
callback(null, transformed);
}
});
3. 사용자 정의 Writable 스트림
const { Writable } = require('stream');
const consoleWriter = new Writable({
objectMode: true,
write(chunk, encoding, callback) {
console.log('청크 기록 중:', chunk);
callback();
}
});
Flowing 모드 vs Paused 모드
Readable 스트림은 일시 중지(Paused) 또는 자동 흐름(Flowing) 모드로 작동합니다:
const stream = fs.createReadStream('data.csv');
// 'data' 리스너를 연결하면 자동으로 Flowing 모드가 활성화됩니다
stream.on('data', (chunk) => {
console.log(`수신된 바이트: ${chunk.length} 바이트`);
});
// 명시적 흐름 제어
stream.pause(); // 이벤트 일시 중지
stream.resume(); // 스트리밍 이벤트 재개
스트림 핵심 이벤트
| 이벤트 | 적용 대상 | 발생 조건 |
|---|---|---|
data |
Readable |
소비할 데이터 청크가 준비되었을 때 발생 |
end |
Readable |
더 이상 읽을 데이터가 없을 때 발생 |
finish |
Writable |
모든 기록된 데이터가 성공적으로 플러시되었을 때 발생 |
error |
All | 스트림에서 처리되지 않은 오류가 발생했을 때 발생 |
close |
All | 기본 리소스나 디스크립터가 닫혔을 때 발생 |