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초 정도 소요됐다.

스트림을 사용하면 파일 크기와 관계없이 일정한 메모리만 사용한다는 점을 다시 한번 확인했다. 대용량 데이터 처리 시 기본적으로 고려해야 할 패턴이다.

Node.js 스트림으로 대용량 CSV 파싱 메모리 문제 해결