JSON Toolkit

대용량 JSON 파일이 안 열릴 때: 크기별 도구 고르기

핵심 요약: 수십 MB짜리 JSON 내보내기는 텍스트 편집기와 온라인 도구에서 자주 멈추지만, 파싱 자체는 수백 밀리초면 끝납니다. 45MB·50만 레코드 샘플로 각 단계의 시간과 메모리를 실측해 진짜 비용이 줄 수와 노드 수임을 보이고, 파일 크기별 도구 선택표를 제시합니다. 온라인 보기, 명령줄 추출, 스트리밍 처리, 데스크톱 앱의 역할 구분까지 정리했습니다.

중급약 6분 소요2026-11-07 발행2026-11-07 업데이트
크기별로 대용량 JSON 도구를 고르는 사다리 그림: 온라인·CLI·스트리밍

45MB JSON 파일의 파싱은 249밀리초면 끝납니다. 30초를 기다리게 하는 범인은 500만 줄 텍스트와 350만 개 트리 노드의 렌더링입니다. 이 둘을 구분하지 못하면 늘 엉뚱한 대상을 탓하게 됩니다.

一、먼저 실측하기: 파싱은 병목이 아니다

수십 MB짜리 .json 내보내기 파일을 더블클릭하고 편집기가 수십 초간 빙빙 도는 것을 보면 "JSON 파싱이 느리다"고 생각하기 쉽습니다. 실측 결과는 정반대입니다. 로컬 머신(Node 22)에서 측정한 결과입니다. 샘플은 50만 레코드, 레코드당 5개 필드(id, email, active, score, tags)이며 압축 상태로 45MB입니다.

작업 실측 결과
JSON.parse 전체 파싱 249 ms
JSON.stringify(data, null, 2) 예쁘게 출력 215 ms
예쁘게 출력한 텍스트 71 MB, 5,000,001줄
트리 노드 총수(레코드 + 필드) 약 3,500,001개
파싱 후 Node 프로세스 RSS 313 MB(파일 크기의 약 7배)

파싱은 200여 밀리초 만에 끝났습니다. 즉, 마우스를 떼기 전에 파서의 일은 이미 끝난 것입니다. 그 후 멈추는 십여 초에서 수 분은 전부 "결과를 사람에게 보여주는" 계층에서 발생합니다. 텍스트 편집기는 500만 줄을 배치하고, 한 줄씩 구문 강조를 적용하고, 되돌리기 버퍼를 유지해야 합니다. 트리 뷰어는 350만 개 노드 각각에 DOM 요소를 만들어야 하고, 브라우저는 10만 노드 수준에서 버벅대다가 수십만에서 반응을 잃습니다.

이 결론이 가리키는 수정 방향은 완전히 다릅니다. 렌더링과 싸우지 말고, 전체를 렌더링하지 않는 도구로 갈아타는 것입니다.

二、왜 멈추는가: 줄 수와 노드 수가 진짜 비용

파일 크기는 겉모습일 뿐이고, 도구를 무너뜨리는 것은 두 개의 파생 숫자입니다.

  1. 줄 수. 45MB의 컴팩트한 JSON은 예쁘게 출력하면 500만 줄이 됩니다. 편집기는 파일을 여는 순간 어느 화면을 보고 있든 전체의 줄 구조를 만들고, 강조 상태를 정하고, 버퍼를 할당해야 합니다. 비용은 총 줄 수에 비례합니다.
  2. 노드 수. 50만 레코드 × (1개 객체 + 5개 필드)면 350만 노드입니다. "전체 트리를 만든 뒤 보여주는" 뷰어는 한 분기만 펼치든 전부 펼치든 똑같은 메모리와 시간을 지불합니다.

여기서 직관에 반하는 결론 두 개가 나옵니다. 먼저 예쁘게 하고 보는 일은 도움이 되지 않습니다 — 예쁘게 하면 45MB가 71MB로 늘고 줄 수는 40배가 되어 도구에 부담만 더합니다. 파일을 cat으로 터미널에 흘리는 것도 마찬가지입니다. 스크롤백 버퍼가 새로운 병목이 되므로, 앞의 수백 바이트만 보려면 head -c 2000이면 충분합니다.

三、크기별 도구 선택: 의사결정표

크기가 다르면 맞는 도구도 완전히 다릅니다. 위 실측과 여러 도구 사이트의 공개 가이드(OpenFormatter는 온라인 도구의 쾌적 구간을 2-5MB로, PureJSON은 500MB 문서 파싱 후 메모리 2-4GB로 추정)를 합치면 다음 표가 됩니다.

파일 크기 첫 선택 이유
< 2 MB 어떤 온라인 도구든 바로 보기. /parse류의 트리 보기 + 검증이 한 번에
2 – 20 MB 로컬 편집기(VS Code 등) 또는 브라우저 로컬 도구 온라인 도구가 나빠지기 시작. 로컬 처리는 업로드 없음
20 – 100 MB CLI jq 탐색 + 가상화 트리 뷰어 편집기가 느려짐. jq는 수 초에 원하는 부분 추출
100 MB – 1 GB jq --stream / ijson 스트리밍 일반 파싱 메모리는 파일의 약 7배로 증가. 안 들어가면 스트리밍
> 1 GB 또는 계속 추가됨 형식 변경: NDJSON / CSV / Parquet 거대한 단일 배열은 전부 파싱해야 첫 레코드를 쓸 수 있음. 형식 자체가 부적합

이 사이트의 [/parse](/parse)는 첫 구간의 트리 보기와 검증을 담당합니다. 더 큰 파일은 데스크톱 앱으로 로컬에서 열고, 수십 MB 데이터를 웹 페이지에 붙여 넣지 마세요.

四、파일을 열기 전에 구조부터 묻기

낯선 큰 JSON을 받으면 먼저 할 일은 여는 것이 아니라 몇 초 안에 구조를 파악하는 것입니다. jq는 200ms 안에 세 가지 질문에 답합니다.

# 최상위는 배열인가 객체인가? 레코드는 몇 개인가?
jq 'if type == "array" then {type: "array", count: length} else {type: "object", keys: (keys|length)} end' huge.json

# 첫 레코드의 생김새(한 건만, 도배 없이)
jq '.[0]' huge.json | head -40

# 전체 필드 이름(첫 레코드에서 추출)
jq '.[0] | keys' huge.json

구조를 알면 대부분의 요구는 "어떤 필드 추출"로 바뀌고, 파일을 열 필요조차 없어집니다. 예를 들어 모든 사용자의 이메일만 필요하다면:

jq -r '.[].email' huge.json | less
# 필터를 겹쳐 적용
jq -r '.[] | select(.active == true) | .email' huge.json

규칙은 간단합니다. "보기"는 가상화 렌더링 뷰어에서, "찾기·가져오기"는 jq에서. 두 역할을 한 도구에 섞지 마세요.

五、메모리의 급경사: 일반 모드와 스트리밍의 150배 차이

같은 45MB 샘플에서 /usr/bin/time -v로 jq 두 모드의 메모리 피크를 실측했습니다.

모드 명령 메모리 피크(RSS) 시간
일반 모드(전체 로드) jq '.[0].email' sample.json 542 MB 0.7 s
스트리밍 jq --stream 'select(.[0][1]=="email") | .[1]' sample.json 3.6 MB 2.5 s

일반 모드는 파일 전체를 읽어 객체 그래프를 만들므로 542MB 피크는 "객체 그래프 ≈ 텍스트의 7-12배"라는 경험칙을 그대로 확인해 줍니다. --stream은 입력을 토큰 단위로 소비해 메모리를 한 자릿수 MB로 일정하게 유지합니다. 대가는 총 시간이 조금 더 걸리는 것입니다. GB급에 이르면 이 차이가 "돌아간다"와 "OOM 킬러에게 당한다"의 차이가 됩니다.

# 메모리를 넘는 파일: 전체를 로드하지 않는 유일한 방법이 --stream
jq --stream -n 'fromstream(1|truncate_stream(inputs))' huge.json

Python 쪽 등가물은 ijson입니다. ijson.items(f, 'item')가 배열 요소를 하나씩 내놓으며 같은 상수 메모리로 동작합니다. 이 도구들의 공통점은 임의 접근을 포기한다는 것입니다. 스트림은 앞으로 한 번만 흐르고, 두 번째는 파일을 다시 흘려야 합니다.

六、브라우저에서 어디까지 볼 수 있는가: 로컬 처리의 한계

온라인 도구를 못 쓰는 게 아니라 한계가 있는 것입니다. 현대 브라우저는 탭당 대략 1-4GB의 메모리를 쓸 수 있고, 45MB 파일의 파싱 + 트리 렌더링은 보통 버팁니다. 수백 MB가 되면 절벽 가장자리입니다. 온라인 도구를 고를 때 세 가지를 확인하세요.

  1. 처리가 로컬에서 완결된다(붙여 넣은 내용이 서버로 안 간다). 데이터도 지키고 업로드 시간도 아낍니다. 이 사이트의 모든 도구는 순수 프런트엔드 구현으로 오프라인에서도 동작합니다.
  2. 파싱이 Web Worker에서 실행된다. 메인 스레드가 얼지 않고, 멈춰도 탭만 닫으면 됩니다.
  3. 트리가 가상화 또는 지연 펼침이다. 접힌 가지의 비용은 거의 0이고, 펼친 만큼만 렌더링됩니다.

이 사이트의 예: [/parse](/parse)의 트리 보기는 수십 MB 이하 파일의 구조 파악과 오류 행렬 위치 확인에 적합합니다. 그 이상은 jq나 데스크톱 앱에 맡기고, 브라우저는 작은 조각의 확인용으로만 씁니다.

七、형식만 잘 골라도 문제가 절반은 사라진다

파일이 만들어지는 쪽에 손을 쓸 수 있다면, 더 강한 뷰어로 바꾸는 대신 "하나의 거대한 배열"로 자라지 않게 하는 편이 정답입니다.

  • NDJSON/JSONL: 한 줄에 레코드 하나. 추가 기록이 전체를 다시 쓰지 않고, head·grep·tail·split 등 텍스트 도구 일습이 그대로 쓰이며, 파일이 끊겨도 마지막 줄만 잃습니다. 이 사이트의 JSONL 핸드북에 잘못된 줄 처리 절차가 있습니다.
  • 페이지 내보내기: 10만 건을 한 번에 주는 API는 페이지네이션 파라미터를 받아 한 번에 100건씩 줘야 양쪽에 다 좋습니다.
  • 표형 데이터에는 JSON을 쓰지 않기: 한 줄에 한 레코드, 필드 고정, 중첩 없는 데이터는 본질적으로 표입니다. CSV나 Parquet이 더 작고 생태계도 풍부합니다.

기억해 둘 만한 실측 수치가 있습니다. 50만 건짜리 배열을 중간에서 자르면 앞쪽 절반은 여전히 유효한 NDJSON 파일입니다(기록 절반이 살아남음). 같은 내용의 단일 거대 배열은 전멸합니다. 501건 중 500건 회수가 0건보다 훨씬 낫습니다.

八、체크리스트

  • 먼저 jq '.[0] | keys'나 500건 샘플로 구조를 파악한 뒤 처리 방법을 정한다;
  • 보려고만 하면서 먼저 예쁘게 하지 않는다 — 줄 수가 배로 늘어 더 느려질 뿐;
  • 2MB 미만은 온라인 트리 보기, 20MB 이상은 명령줄, 1GB급은 무조건 --stream 또는 ijson;
  • 온라인 도구가 로컬 처리인지(붙여 넣은 내용이 브라우저 밖으로 안 나가는지) 확인;
  • 생성 쪽을 바꿀 수 있으면 NDJSON이나 페이지로 전환해 거대 단일 배열을 원천 제거.

자주 묻는 질문

편집기로 열면 수십 초 걸리는데 프로그램 파싱은 200ms 남짓인 이유는?

파싱은 네이티브 코드가 텍스트를 자료구조로 바꾸는 작업으로, 45MB 샘플 실측 249ms였습니다. 편집기가 멈추는 곳은 표시 계층입니다. 500만 줄 배치·강조·되돌리기 버퍼, 그리고 350만 노드의 DOM 구축. 가상화 뷰어로 바꾸거나 jq로 바로 추출하면 피할 수 있습니다.

jq가 1GB 넘는 JSON에서 메모리가 넘치나요?

일반 모드에서는 넘칩니다. 문서 전체를 로드하고 객체 그래프 피크는 파일의 약 7-12배입니다. --stream을 붙이면 입력을 토큰 단위로 소비해 45MB 실측 피크 3.6MB, 파일이 아무리 커도 메모리가 늘지 않습니다. 대가는 순방향 한 번의 순회뿐이라는 점입니다.

온라인 JSON 도구로 볼 수 있는 파일의 한도는?

대부분 온라인 도구의 쾌적 구간은 2-5MB이고 10MB를 넘으면 뚜렷이 나빠집니다. 하드 한도는 탭 메모리(약 1-4GB)지만, 수십 MB 시점부터 이미 파싱 + 트리 구축 대기가 고통스럽습니다. 20MB를 넘으면 로컬 도구나 CLI를 권합니다.

큰 JSON에서 특정 필드 값만 보려면?

파일을 열지 말고 jq로 추출합니다. jq -r '.[].email' huge.json이 모든 email을 출력하고 jq -r '.[] | select(.active == true) | .email'로 걸러냅니다. 앞의 몇 건은 jq '.[0:5]' huge.json. 파일을 여는 것보다 자릿수가 다르게 빠릅니다.

먼저 예쁘게 한 뒤 여는 것이 도움이 되나요?

아니요. 컴팩트한 45MB가 71MB·500만 줄이 되어 편집기와 뷰어에 순수한 부담만 더합니다. 예쁘게 하기가 가치를 가지는 경우는 사람이 읽을 목적이고 파일이 충분히 작을 때뿐입니다.