Node.js 스트림으로 대용량 CSV 파일 처리 개선하기
문제 상황
고객사에서 500MB 크기의 CSV 파일을 업로드하면 서버 메모리가 부족해지는 문제가 발생했다. 기존 코드는 fs.readFile로 전체 파일을 읽어 처리하고 있었다.
const data = await fs.readFile('large.csv', 'utf-8');
const lines = data.split('\n');
// 메모리에 전체 파일이 로드됨
스트림 기반 처리로 전환
Node.js의 createReadStream과 readline 모듈을 조합해 라인 단위로 처리하도록 변경했다.
const fs = require('fs');
const readline = require('readline');
const processCSV = async (filePath) => {
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
let count = 0;
for await (const line of rl) {
const data = line.split(',');
await processRow(data); // DB 저장 등
count++;
if (count % 10000 === 0) {
console.log(`Processed ${count} rows`);
}
}
};
배치 처리로 성능 개선
한 줄씩 DB에 insert하면 너무 느려서, 배치 단위로 모았다가 bulk insert로 처리했다.
const BATCH_SIZE = 1000;
let batch = [];
for await (const line of rl) {
batch.push(parseLine(line));
if (batch.length >= BATCH_SIZE) {
await db.bulkInsert(batch);
batch = [];
}
}
if (batch.length > 0) {
await db.bulkInsert(batch);
}
결과
- 메모리 사용량: 1.2GB → 80MB로 감소
- 처리 시간: 45초 → 12초로 단축
- 파일 크기 제한 없이 안정적 처리 가능
스트림은 Node.js의 핵심 장점 중 하나다. 대용량 파일 처리가 필요한 경우 반드시 고려해야 한다.