Node.js 스트림으로 대용량 CSV 파일 처리 개선하기

문제 상황

고객사에서 500MB 크기의 CSV 파일을 업로드하면 서버 메모리가 부족해지는 문제가 발생했다. 기존 코드는 fs.readFile로 전체 파일을 읽어 처리하고 있었다.

const data = await fs.readFile('large.csv', 'utf-8');
const lines = data.split('\n');
// 메모리에 전체 파일이 로드됨

스트림 기반 처리로 전환

Node.js의 createReadStreamreadline 모듈을 조합해 라인 단위로 처리하도록 변경했다.

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의 핵심 장점 중 하나다. 대용량 파일 처리가 필요한 경우 반드시 고려해야 한다.

Node.js 스트림으로 대용량 CSV 파일 처리 개선하기