stream은 데이터를 한 번에 메모리에 모두 적재하는 대신 점진적으로, 청크(chunk) 단위로 처리합니다. 이 때문에 모든 것을 버퍼링하면 메모리가 고갈되는 대용량 데이터(큰 파일, 네트워크 전송)에 필수적입니다.
stream이 해결하는 문제
js
data = fs..();
(data);
fs.()
.(transform)
.(fs.());
stream을 사용하면 파일 크기와 무관하게 메모리 사용량이 일정하게 유지됩니다. 한 번에 하나의 청크씩 처리하기 때문입니다.
Readable → source you read FROM (fs.createReadStream, http request)
Writable → destination you write TO (fs.createWriteStream, http response)
Duplex → both readable & writable (TCP socket)
Transform → modify data as it passes through (zlib gzip, encryption)
import { createReadStream, createWriteStream } from "fs";
import { createGzip } from "zlib";
// read → compress → write, all streaming, low memory
createReadStream("file.txt")
.pipe(createGzip()) // Transform: compress on the fly
.pipe(createWriteStream("file.txt.gz"));
pipe는 readable과 writable을(중간에 transform과 함께) 연결하고 흐름을 자동으로 관리합니다. stream을 조합하는 관용적인 방식입니다.
import { pipeline } from "stream/promises";
await pipeline(
createReadStream("in.txt"),
createGzip(),
createWriteStream("out.gz")
); // ✅ cleans up all streams on error/completion (pipe alone leaks on errors)
pipeline은 에러를 전파하고 모든 stream을 적절히 정리하므로 체이닝된 .pipe()보다 선호됩니다.
✓ HTTP request/response bodies (req and res ARE streams)
✓ File reading/writing, file uploads/downloads
✓ Compression (zlib), encryption (crypto)
✓ Database cursors, large query results
✓ Real-time data processing, video streaming
Small data that fits comfortably in memory → readFile/simple buffering is simpler.
Streams add complexity; use them for LARGE or continuous data.
stream은 대용량 또는 연속 데이터를 메모리 효율적으로 처리하는 Node의 대표적인 기능으로, 큰 업로드에서 충돌하는 앱과 어떤 크기든 일정한 메모리로 처리하는 앱의 차이를 만듭니다.
네 가지 유형, pipe/pipeline 조합(그리고 pipeline이 더 안전한 이유), HTTP request/response 자체가 stream이라는 점을 이해하는 것은 Node에서 확장 가능한 파일 처리, 데이터 처리, 프록시 서비스를 구축하는 데 필수적입니다.