SHIN
Node.js 실전 팁 20선
20편파일 전체를 메모리에 올리지 않고 청크(chunk) 단위로 처리하는 것이 Stream의 핵심입니다.
| 종류 | 설명 | 예시 |
|---|---|---|
| Readable | 읽기 스트림 | fs.createReadStream |
| Writable | 쓰기 스트림 | fs.createWriteStream |
| Duplex | 읽기+쓰기 | TCP 소켓 |
| Transform | 변환 | zlib.createGzip |
const fs = require('fs');
// ❌ 메모리 폭발 위험
const content = fs.readFileSync('large.csv'); // 5GB 파일이면 RAM 5GB 사용
fs.writeFileSync('copy.csv', content);
// ✅ Stream 파이프 - 메모리 일정 유지
fs.createReadStream('large.csv')
.pipe(fs.createWriteStream('copy.csv'))
.on('finish', () => console.log('복사 완료'));const fs = require('fs');
const zlib = require('zlib');
const { pipeline } = require('stream/promises');
async function compressFile(input, output) {
await pipeline(
fs.createReadStream(input),
zlib.createGzip(),
fs.createWriteStream(output)
);
console.log(`압축 완료: ${output}`);
}
compressFile('data.csv', 'data.csv.gz');const fs = require('fs');
const readline = require('readline');
async function processCSV(filePath) {
const rl = readline.createInterface({
input: fs.createReadStream(filePath),
crlfDelay: Infinity,
});
let lineCount = 0;
for await (const line of rl) {
const fields = line.split(',');
// 각 줄 처리 (메모리에는 한 줄만 유지)
lineCount++;
if (lineCount % 100000 === 0) {
console.log(`${lineCount}줄 처리 완료`);
}
}
console.log(`총 ${lineCount}줄 처리`);
}const { Transform } = require('stream');
class UpperCaseTransform extends Transform {
_transform(chunk, encoding, callback) {
this.push(chunk.toString().toUpperCase());
callback();
}
}
fs.createReadStream('input.txt')
.pipe(new UpperCaseTransform())
.pipe(fs.createWriteStream('output.txt'));const readable = fs.createReadStream('large.bin');
const writable = fs.createWriteStream('dest.bin');
readable.on('data', (chunk) => {
const ok = writable.write(chunk);
if (!ok) {
// 쓰기 버퍼가 가득 참 → 읽기 일시 중단
readable.pause();
writable.once('drain', () => readable.resume());
}
});
readable.on('end', () => writable.end());pipeline() 을 사용하면 백프레셔와 에러 처리가 자동으로 됩니다. 직접 pipe()보다 항상 pipeline()을 선호하세요.