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

문제 상황

데이터 마이그레이션 작업 중 10GB가 넘는 CSV 파일을 파싱해야 했다. 기존에는 fs.readFileSync로 전체 파일을 읽어 처리했는데, 이번엔 힙 메모리 부족으로 프로세스가 죽었다.

// 기존 방식 - 메모리 부족 발생
const data = fs.readFileSync('large.csv', 'utf-8');
const rows = data.split('\n');

해결 방법

Node.js의 스트림을 활용해 청크 단위로 읽고 처리하도록 변경했다. readline 모듈로 라인별 처리가 가능했다.

const fs = require('fs');
const readline = require('readline');

const processFile = 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 row = line.split(',');
    await processRow(row); // DB 저장 등
    count++;
    if (count % 10000 === 0) {
      console.log(`Processed ${count} rows`);
    }
  }
};

결과

메모리 사용량이 2GB에서 100MB 이하로 감소했다. 처리 속도도 배치 인서트를 추가하니 오히려 더 빨라졌다. 대용량 파일 처리 시 스트림은 선택이 아닌 필수였다.

추가로 --max-old-space-size 옵션으로 힙 사이즈를 늘리는 방법도 있지만, 근본적인 해결책은 아니었다. 스트림 기반 처리가 정답이다.