1. 스트림의 기본 개념
스트림은 데이터를 작은 청크(chunk)로 나누어 순차적으로 처리하는 방식입니다. 이를 통해 전체 데이터를 메모리에 한 번에 로드하지 않고도 효율적으로 처리할 수 있습니다.
const fs = require('fs');
const readStream = fs.createReadStream('largefile.txt');
const writeStream = fs.createWriteStream('output.txt');
readStream.pipe(writeStream);
설명: 이 예제는 대용량 파일을 읽어 다른 파일로 복사하는 기본적인 스트림 사용법을 보여줍니다. pipe() 메서드를 사용하여 읽기 스트림과 쓰기 스트림을 연결합니다.
2. 스트림 최적화 기법
2.1 버퍼 크기 조정
const readStream = fs.createReadStream('largefile.txt', { highWaterMark: 64 * 1024 }); // 64KB 버퍼
설명: highWaterMark 옵션을 사용하여 버퍼 크기를 조정할 수 있습니다. 이를 통해 메모리 사용과 처리 속도 사이의 균형을 맞출 수 있습니다.
2.2 Transform 스트림 활용
const { Transform } = require('stream');
const upperCaseTransform = new Transform({
transform(chunk, encoding, callback) {
this.push(chunk.toString().toUpperCase());
callback();
}
});
readStream.pipe(upperCaseTransform).pipe(writeStream);
설명: Transform 스트림을 사용하여 데이터를 처리하면서 동시에 전달할 수 있습니다. 이 예제는 텍스트를 대문자로 변환하는 과정을 보여줍니다.
3. 병렬 처리
const cluster = require('cluster');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`마스터 프로세스 ${process.pid} 실행`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
console.log(`워커 프로세스 ${process.pid} 실행`);
// 각 워커에서 스트림 처리 로직 구현
}
설명: 클러스터 모듈을 사용하여 여러 CPU 코어에서 병렬로 스트림 처리를 수행할 수 있습니다. 이는 대용량 데이터 처리 시 성능을 크게 향상시킬 수 있습니다.
4. 백프레셔(Backpressure) 처리
const writableStream = fs.createWriteStream('output.txt');
readStream.on('data', (chunk) => {
if (!writableStream.write(chunk)) {
readStream.pause();
}
});
writableStream.on('drain', () => {
readStream.resume();
});
설명: 백프레셔는 데이터 생성 속도가 소비 속도보다 빠를 때 발생합니다. 위 코드는 쓰기 스트림이 처리할 수 있는 속도에 맞춰 읽기 스트림을 조절하는 방법을 보여줍니다.
5. 메모리 사용 최적화
const { pipeline } = require('stream');
const zlib = require('zlib');
pipeline(
fs.createReadStream('largefile.txt'),
zlib.createGzip(),
fs.createWriteStream('largefile.txt.gz'),
(err) => {
if (err) {
console.error('파이프라인 실패', err);
} else {
console.log('파이프라인 성공');
}
}
);
설명: pipeline 함수를 사용하면 여러 스트림을 안전하게 연결할 수 있으며, 메모리 누수를 방지할 수 있습니다. 이 예제는 파일을 읽어 압축하는 과정을 보여줍니다.
결론
Node.js 스트림을 효과적으로 활용하면 대용량 데이터 처리 속도를 크게 향상시킬 수 있는데. 특히 버퍼 크기 조정, Transform 스트림 활용, 병렬 처리, 백프레셔 처리, 그리고 메모리 사용 최적화 등의 기법을 적절히 조합하여 사용하면 더욱 효율적인 데이터 처리가 가능합니다. 스트림의 특성을 이해하고 적절한 최적화 전략을 적용하는 것이 중요합니다. 또한, 실제 애플리케이션에서는 데이터의 특성과 처리 요구사항에 따라 이러한 기법들을 적절히 조정하고 조합하여 사용해야 합니다.