Node.js 스트림으로 대용량 CSV 파일 처리하기
문제 상황
레거시 시스템에서 신규 DB로 사용자 데이터를 이관하는 작업을 맡았다. 약 50GB 크기의 CSV 파일을 읽어서 변환 후 MongoDB에 삽입해야 했는데, 처음 fs.readFileSync로 시도했다가 힙 메모리 에러가 발생했다.
해결 방법
Node.js의 스트림을 활용해 청크 단위로 처리하는 방식으로 변경했다.
const fs = require('fs');
const readline = require('readline');
const { Transform } = require('stream');
const transformStream = new Transform({
objectMode: true,
transform(chunk, encoding, callback) {
// CSV 행 파싱 및 변환
const row = chunk.toString().split(',');
const user = {
email: row[0],
name: row[1],
createdAt: new Date(row[2])
};
this.push(user);
callback();
}
});
const rl = readline.createInterface({
input: fs.createReadStream('users.csv'),
crlfDelay: Infinity
});
let batch = [];
rl.on('line', async (line) => {
batch.push(line);
if (batch.length >= 1000) {
rl.pause();
await insertBatch(batch);
batch = [];
rl.resume();
}
});
배치 단위로 끊어서 DB에 삽입하는 방식으로 구현했다. 1000개씩 모아서 bulkWrite를 사용하니 성능도 개선됐다.
결과
- 메모리 사용량: 8GB → 300MB로 감소
- 처리 시간: 약 2시간 소요
- 안정적으로 완료
스트림은 대용량 데이터 처리에서 필수적이다. backpressure 처리만 잘 해주면 메모리 걱정 없이 안정적으로 동작한다.