Node.js 스트림으로 대용량 CSV 파싱 메모리 문제 해결
문제 상황
데이터 마이그레이션 작업 중 150MB 크기의 CSV 파일을 파싱하는 스크립트를 작성했다. 로컬에서는 문제없이 동작했지만, 메모리가 512MB로 제한된 EC2 인스턴스에서 실행하니 heap out of memory 에러가 발생했다.
기존 코드는 전체 파일을 메모리에 올린 뒤 처리하는 방식이었다.
const fs = require('fs');
const csv = require('csv-parser');
const data = fs.readFileSync('large-data.csv', 'utf8');
// 메모리에 전체 파일이 로드됨
해결 방법
fs.createReadStream과 pipe를 사용해 스트림 방식으로 변경했다. 파일을 청크 단위로 읽으면서 즉시 처리하는 방식이다.
const fs = require('fs');
const csv = require('csv-parser');
const results = [];
let count = 0;
fs.createReadStream('large-data.csv')
.pipe(csv())
.on('data', async (row) => {
// 100개 단위로 배치 처리
results.push(row);
if (results.length >= 100) {
await batchInsert(results);
results.length = 0;
}
count++;
if (count % 10000 === 0) {
console.log(`Processed ${count} rows`);
}
})
.on('end', async () => {
if (results.length > 0) {
await batchInsert(results);
}
console.log(`Total: ${count} rows`);
});
결과
메모리 사용량이 500MB에서 80MB로 감소했고, 동일한 서버에서 안정적으로 동작했다. 150MB 파일 처리 시간은 약 45초 정도 소요됐다.
스트림을 사용하면 파일 크기와 관계없이 일정한 메모리만 사용한다는 점을 다시 한번 확인했다. 대용량 데이터 처리 시 기본적으로 고려해야 할 패턴이다.