2026년에 크래시 없이 대용량 JSON 파일 파싱하는 방법
JavaScript와 Python에서 대용량 JSON 파일(100MB 이상)을 처리하는 완전한 가이드. 메모리 오류를 피하기 위한 스트리밍, 점진적 파싱, 청킹 기술을 배워보세요.
Big JSON Team
• Technical WriterExpert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.
# 2026년에 크래시 없이 큰 JSON 파일 파싱하는 방법
소개
큰 JSON 파일을 파싱하려고 할 때 "FATAL ERROR: JavaScript heap out of memory" 오류를 만난 적이 있나요? 당신만 그런 것이 아닙니다. 대용량 JSON 파일(100MB+) 처리는 현대 웹 개발에서 가장 일반적인 과제 중 하나입니다.
이 종합 가이드에서는 메모리를 소진하거나 애플리케이션을 크래시시키지 않고 대용량 JSON 파일을 효율적으로 파싱하는 검증된 기술을 배웁니다.
JSON.parse()가 큰 파일에서 실패하는 이유
표준 JSON.parse() 메서드는 전체 파일을 한 번에 메모리에 로드합니다:
// ⚠️ 큰 파일(>100MB)에는 나쁨
const fs = require('fs');
const data = fs.readFileSync('huge-file.json', 'utf8');
const json = JSON.parse(data); // 엄청난 메모리 소비!
실패하는 이유:
- 전체 문자열을 메모리에 저장 (파일 크기의 2-3배)
- 완전한 JavaScript 객체 생성 (추가로 파일 크기의 2-5배)
- 완료될 때까지 메모리 해제 없음
- 총계: 500MB 파일이 2GB 이상의 메모리를 사용할 수 있습니다!
해결책 1: JSONStream을 사용한 스트리밍 (Node.js)
대용량 JSON 파일에 대한 최상의 접근 방식은 스트리밍 - 데이터를 조각조각 처리하는 것입니다.
실용적인 예제
const fs = require('fs');
const JSONStream = require('JSONStream');
// 객체 배열 파싱
const stream = fs.createReadStream('large-dataset.json', { encoding: 'utf8' });
const parser = JSONStream.parse(''); // 배열 요소에는 ''
let count = 0;
parser.on('data', (item) => {
// 한 번에 하나의 항목 처리
console.log(레코드 ${++count} 처리 중:, item.id);
// 여기에 로직 - DB에 삽입, 변환 등
if (item.price > 1000) {
saveToDatabase(item);
}
});
parser.on('end', () => {
console.log(${count}개 레코드 성공적으로 처리);
});
parser.on('error', (err) => {
console.error('파싱 오류:', err);
});
stream.pipe(parser);
중첩된 JSON 경로 파싱
// 용도: { "users": [ {...}, {...} ] }
const parser = JSONStream.parse('users.');
// 용도: { "data": { "items": [...] } }
const parser = JSONStream.parse('data.items.');
// 용도: 깊은 구조 또는 복잡한 조건
const parser = JSONStream.parse(['data', 'nested', { emitKey: true }]);
성능 비교
| 메서드 | 파일 크기 | 메모리 사용 | 시간 |
|-------|---------|----------|-----|
| JSON.parse() | 500MB | ~2.1GB 💥 | 8초 |
| JSONStream | 500MB | ~45MB ✅ | 12초 |
| stream-json | 500MB | ~38MB ✅ | 10초 |
벤치마크: Node.js 20, Mac M1, 16GB RAM해결책 2: stream-json (더 빠름)
stream-json은 JSONStream보다 빠르고 더 많은 제어를 제공합니다:
const { chain } = require('stream-chain');
const { parser } = require('stream-json');
const { streamArray } = require('stream-json/streamers/StreamArray');
const pipeline = chain([
fs.createReadStream('huge-array.json'),
parser(),
streamArray()
]);
pipeline.on('data', ({ key, value }) => {
console.log(인덱스 ${key}:, value.name);
// 여기서 처리
processRecord(value);
});
pipeline.on('end', () => console.log('완료!'));
JSON 객체용 (배열 아님)
const { streamObject } = require('stream-json/streamers/StreamObject');
const pipeline = chain([
fs.createReadStream('huge-object.json'),
parser(),
streamObject()
]);
pipeline.on('data', ({ key, value }) => {
console.log(키: ${key}, value);
});
해결책 3: 청크 읽기 (브라우저)
브라우저에서는 FileReader를 사용하여 파일을 점진적으로 처리합니다:
async function parseHugeJSON(file) {
const chunkSize = 1024 1024; // 1MB
let offset = 0;
let buffer = '';
let results = [];
while (offset < file.size) {
const chunk = file.slice(offset, offset + chunkSize);
const text = await chunk.text();
buffer += text;
// 완전한 객체 파싱 시도
const lines = buffer.split('\n');
buffer = lines.pop(); // 불완전한 줄 유지
for (const line of lines) {
if (line.trim()) {
try {
const obj = JSON.parse(line);
results.push(obj);
// 메모리 누적 방지를 위한 배치 처리
if (results.length >= 1000) {
await processBatch(results);
results = [];
}
} catch (e) {
console.warn('유효하지 않은 줄 건너뜀:', line.substring(0, 50));
}
}
}
offset += chunkSize;
// 진행 상황 보고
const progress = (offset / file.size 100).toFixed(1);
console.log(진행률: ${progress}%);
}
// 마지막 배치 처리
if (results.length > 0) {
await processBatch(results);
}
}
// 사용법
document.getElementById('fileInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
await parseHugeJSON(file);
});
해결책 4: Web Workers (논블로킹)
UI 동결을 방지하기 위해 파싱을 Web Worker로 이동:
// worker.js
self.onmessage = async function(e) {
const { file } = e.data;
const chunkSize = 1024 1024;
let processed = 0;
for (let offset = 0; offset < file.size; offset += chunkSize) {
const chunk = file.slice(offset, offset + chunkSize);
const text = await chunk.text();
// 청크 처리
const lines = text.split('\n').filter(l => l.trim());
const objects = lines.map(line => {
try {
return JSON.parse(line);
} catch {
return null;
}
}).filter(Boolean);
// 결과 반환
self.postMessage({
type: 'progress',
data: objects,
percent: (offset / file.size 100)
});
processed += objects.length;
}
self.postMessage({ type: 'complete', total: processed });
};
// main.js
const worker = new Worker('worker.js');
worker.onmessage = function(e) {
if (e.data.type === 'progress') {
console.log(처리됨: ${e.data.percent.toFixed(1)}%);
updateUI(e.data.data);
} else if (e.data.type === 'complete') {
console.log(완료! 총: ${e.data.total});
}
};
// 처리 시작
const fileInput = document.getElementById('upload');
fileInput.addEventListener('change', (e) => {
worker.postMessage({ file: e.target.files[0] });
});
해결책 5: API 페이지네이션
API에서 JSON을 가져오는 경우 페이지네이션을 사용:
async function fetchAllData(apiUrl) {
let allData = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const response = await fetch(${apiUrl}?page=${page}&limit=100);
const data = await response.json();
// 페이지 즉시 처리
await processPage(data.items);
allData.push(...data.items);
hasMore = data.hasNextPage;
page++;
// 속도 제한 방지를 위한 지연 추가
await new Promise(resolve => setTimeout(resolve, 100));
}
return allData;
}
// 최적화 버전: 모든 것을 저장하지 않음
async function processAllData(apiUrl) {
let page = 1;
let hasMore = true;
let totalProcessed = 0;
while (hasMore) {
const response = await fetch(${apiUrl}?page=${page}&limit=100);
const data = await response.json();
// 저장하지 않고 처리
for (const item of data.items) {
await processItem(item); // DB에 저장, 변환 등
totalProcessed++;
}
hasMore = data.hasNextPage;
page++;
console.log(현재까지 ${totalProcessed}개 항목 처리...);
}
return totalProcessed;
}
해결책 6: ijson을 사용한 Python
서버 측 처리의 경우 Python의 ijson이 훌륭합니다:
import ijson
def process_large_json(filename):
with open(filename, 'rb') as file:
# 객체 배열 파싱
objects = ijson.items(file, 'item')
count = 0
for obj in objects:
# 각 요소 처리
if obj.get('price', 0) > 1000:
save_to_database(obj)
count += 1
if count % 1000 == 0:
print(f"{count}개 레코드 처리...")
print(f"총: {count}개 레코드")
# 중첩 구조용
def process_nested(filename):
with open(filename, 'rb') as file:
# 특정 키만 추출
for user_id in ijson.items(file, 'users.item.id'):
print(f"사용자 ID: {user_id}")
최적화 전략
1. 개별 대신 배치 처리
let batch = [];
const BATCH_SIZE = 1000;
parser.on('data', async (item) => {
batch.push(item);
if (batch.length >= BATCH_SIZE) {
await insertBatch(batch); // 하나의 배치 삽입
batch = [];
}
});
parser.on('end', async () => {
if (batch.length > 0) {
await insertBatch(batch); // 마지막 배치 처리
}
});
2. 조기 데이터 필터링
const { pick } = require('stream-json/filters/Pick');
const { streamArray } = require('stream-json/streamers/StreamArray');
const pipeline = chain([
fs.createReadStream('data.json'),
parser(),
pick({ filter: 'users' }), // "users" 속성만
streamArray()
]);
3. 파일 압축
const zlib = require('zlib');
const pipeline = chain([
fs.createReadStream('data.json.gz'),
zlib.createGunzip(), // 즉석에서 압축 해제
parser(),
streamArray()
]);
일반적인 시나리오 및 솔루션
시나리오 1: JSON에서 데이터베이스 가져오기
const { chain } = require('stream-chain');
const { parser } = require('stream-json');
const { streamArray } = require('stream-json/streamers/StreamArray');
const { MongoClient } = require('mongodb');
async function importToMongo(filename, collectionName) {
const client = await MongoClient.connect('mongodb://localhost:27017');
const collection = client.db('mydb').collection(collectionName);
let batch = [];
const BATCH_SIZE = 1000;
const pipeline = chain([
fs.createReadStream(filename),
parser(),
streamArray()
]);
for await (const { value } of pipeline) {
batch.push(value);
if (batch.length >= BATCH_SIZE) {
await collection.insertMany(batch);
console.log(${batch.length}개 문서 삽입);
batch = [];
}
}
if (batch.length > 0) {
await collection.insertMany(batch);
}
await client.close();
}
시나리오 2: JSON을 CSV로 변환
const { Transform } = require('stream');
const csvWriter = require('csv-write-stream');
const jsonToCSV = new Transform({
objectMode: true,
transform(chunk, encoding, callback) {
// JSON 객체를 CSV 행으로 변환
callback(null, {
id: chunk.id,
name: chunk.name,
email: chunk.email
});
}
});
const writer = csvWriter();
writer.pipe(fs.createWriteStream('output.csv'));
pipeline
.pipe(jsonToCSV)
.pipe(writer);
시나리오 3: 줄바꿈 구분 JSON (NDJSON) 처리
const readline = require('readline');
async function processNDJSON(filename) {
const fileStream = fs.createReadStream(filename);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
let count = 0;
for await (const line of rl) {
if (line.trim()) {
const obj = JSON.parse(line);
await processObject(obj);
count++;
}
}
console.log(${count}개 줄 처리);
}
문제 해결
오류: JavaScript heap out of memory
해결책:# Node.js에서 메모리 제한 증가
node --max-old-space-size=4096 script.js
하지만 더 나은 방법: 대신 스트리밍을 사용하세요!
오류: 청크 읽기 시 유효하지 않은 JSON
문제: 청크가 JSON 객체를 분할할 수 있습니다. 해결책: 줄 구분 JSON (NDJSON) 사용:// 먼저 JSON을 NDJSON으로 변환
const items = require('./data.json'); // 한 번만 작게 로드
const stream = fs.createWriteStream('data.ndjson');
items.forEach(item => {
stream.write(JSON.stringify(item) + '\n');
});
stream.end();
오류: 프로세스가 너무 느림
최적화:JSONStream 대신 stream-json 사용 (2배 빠름)권장 도구
JSON 검증용
BigJSON.online을 사용하여 대용량 JSON 파일을 완전히 로드하지 않고 빠르게 검증하고 검사합니다. 스트리밍 및 실시간 검증을 지원합니다.라이브러리
- Node.js:
stream-json(가장 빠름),JSONStream(가장 간단) - Python:
ijson(최고) - 브라우저: FileReader API + Web Workers
결론
빠른 권장 사항:| 사용 사례 | 최고의 솔루션 | 메모리 사용 |
|---------|-----------|----------|
| Node.js (큰 배열) | stream-json | 매우 낮음 |
| Node.js (단순 구조) | JSONStream | 낮음 |
| 브라우저 (< 50MB) | 청크 읽기 | 보통 |
| 브라우저 (> 50MB) | Web Workers | 낮음 |
| API 다운로드 | 페이지네이션 + 스트림 처리 | 매우 낮음 |
| Python | ijson | 매우 낮음 |
핵심 사항:- ✅ 50MB 이상 파일에는 항상 스트리밍 사용
- ✅ DB 삽입에는 배치 처리
- ✅ 메모리 사용 줄이기 위해 조기 필터링
- ❌ 큰 파일에 JSON.parse() 사용하지 말 것
- ❌ 모든 것을 한 번에 메모리에 로드하지 말 것
최신 스트리밍 패키지를 사용하면 메모리를 소진하거나 크래시하지 않고 GB 크기의 JSON 파일을 쉽게 처리할 수 있습니다.
추가 리소스
이 가이드가 유용했나요? 큰 JSON 파일로 고생하는 개발자 동료들과 공유하세요!
관련 글
JavaScript와 JSON: 완벽한 가이드 2026
JavaScript에서 JSON을 마스터하세요. JSON.parse(), JSON.stringify(), 오류 처리, fetch API 및 모범 사례에 대한 완벽한 가이드입니다.
데이터 과학에서의 JSON: 실용 가이드와 분석 기법
데이터 과학과 머신러닝에서 JSON을 효과적으로 다루는 방법을 배워보세요. Pandas, NumPy, 데이터 변환까지 완벽 정리.
대용량 JSON 다루기: 성능 최적화와 실용 기법
기가바이트 크기의 JSON 파일을 효율적으로 처리하는 방법을 배워보세요. 스트리밍, 청크 처리, 메모리 최적화까지 완벽 정리.