Node.js 스트림으로 대용량 CSV 파일 처리 최적화
문제 상황
데이터 분석팀에서 매일 생성되는 10GB 이상의 CSV 로그 파일을 파싱하여 DB에 적재하는 배치 작업이 필요했다. 기존 코드는 fs.readFileSync로 전체 파일을 메모리에 올린 후 처리했는데, 힙 메모리 부족으로 프로세스가 죽는 문제가 발생했다.
기존 코드의 문제
const fs = require('fs');
const data = fs.readFileSync('huge-file.csv', 'utf-8');
const lines = data.split('\n');
// OOM 발생
전체 파일을 메모리에 로드하면서 Node.js의 기본 힙 제한(약 1.4GB)을 초과했다.
스트림 기반 해결
readline과 stream 모듈을 활용해 라인 단위로 처리하도록 변경했다.
const fs = require('fs');
const readline = require('readline');
async function processLargeCSV(filePath) {
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
let batch = [];
for await (const line of rl) {
const parsed = parseLine(line);
batch.push(parsed);
if (batch.length >= 1000) {
await insertBatch(batch);
batch = [];
}
}
if (batch.length > 0) {
await insertBatch(batch);
}
}
결과
- 메모리 사용량: 10GB → 200MB 이하로 감소
- 처리 시간: 메모리 부족으로 실패하던 작업이 약 15분 내 완료
- 배치 사이즈 조정으로 DB INSERT 성능도 개선
스트림은 Node.js의 핵심 강점이지만 실제로 활용하지 않는 경우가 많았다. 이번 계기로 대용량 파일 처리 시 스트림을 기본 선택지로 고려하게 되었다.