JSON Toolkit

에디터, 명령행, 자동화 도구 체인

핵심 요약: 에디터는 "선명하게 보고 올바르게 편집"을, 명령행은 "배치와 반복"을 해결합니다. 양쪽 모두 십 년간 유용하게 남는 도구가 몇 있습니다.

초급약 10분 소요2026-10-30 발행2026-10-30 업데이트
에디터, 명령행, 자동화 도구 체인 표지

핵심부터 말하면, 에디터는 시나리오로 고르세요. 유틸리티 사이트와 모바일에는 CodeMirror 6, 완전한 IDE 경험만 필요하면 Monaco. 배치 작업은 jq와 문법, 구조, 스타일 세 가지 CI 검사에 맡기고, 인자는 항상 jq --arg로 넘기세요.

JSON을 눈으로 검사하는 시대는 끝나야 합니다. 기계가 반복할 수 있는 일은 인간의 눈에 맡기지 마세요.

1. 브라우저의 에디터

1.1 최소 CodeMirror 6 통합

import { EditorView, basicSetup } from 'codemirror';
import { json, jsonParseLinter } from '@codemirror/lang-json';
import { linter, lintGutter } from '@codemirror/lint';

const view = new EditorView({
  parent: document.getElementById('editor')!,
  doc: '{"hello": "json"}',
  extensions: [
    basicSetup,
    json(),
    linter(jsonParseLinter()),
    lintGutter(),
    EditorView.updateListener.of((update) => {
      if (update.docChanged) {
        // Debounce before doing heavy work; do not format on every keystroke
        scheduleParse(update.state.doc.toString());
      }
    }),
  ],
});

주의할 네 가지:

  1. json()이 구문 하이라이팅과 괄호 매칭을 제공합니다. 직접 쓰지 마세요.
  2. jsonParseLinter()가 행/열 수준 오류 마커를 만듭니다. lintGutter()와 짝지어 줄 번호 옆에 아이콘을 표시하세요.
  3. updateListener는 반드시 디바운스해야 합니다. 10만 줄에 키 입력마다 JSON.parse를 호출하면 눈에 띄게 늦습니다.
  4. 큰 문서는 한 번에 전부 렌더하지 않도록 EditorView.lineWrapping을 넘어서는 최적화가 필요합니다(CodeMirror 6는 기본으로 뷰포트 단위 렌더, 이전 버전보다 개선).

1.2 접기와 개요

CodeMirror 6에는 접기가 내장되어 있고, JSON 시나리오에서는 대개 "객체/배열" 단위로 접습니다:

import { foldGutter, foldKeymap } from '@codemirror/language';
import { keymap } from '@codemirror/view';

foldGutter()와 keymap.of(foldKeymap)으로 사용자가 키보드 단축키로 긴 배열을 접을 수 있습니다.

1.3 CodeMirror와 Monaco 중 선택

차원 CodeMirror 6 Monaco
번들 크기 작고 모듈형 큼(수 MB)
모바일 잘 지원 경험 보통
언어 서비스 직접 만들어야 함 풍부하고 내장
커스터마이즈 비용 낮음 중간

유틸리티 사이트와 모바일 우선 → CodeMirror, 완전한 IDE 경험(여러 파일, 정의로 이동, 완성) → Monaco.

2. 명령행: 빈도 높은 jq 조리법 여섯 가지

jq는 텍스트에 sed + awk가 하는 일을 JSON에 합니다. 문법의 작은 부분만 익혀도 일상 요구의 80퍼센트를 덮습니다.

# 1. Pick fields (the most common)
jq '.data.items[].name' response.json

# 2. Filter + reshape
jq '.data.items[] | select(.price < 10) | {id, price}' response.json

# 3. Aggregates (length, sum, max)
jq '[.items[] | .price] | {count: length, total: add, max: max}' response.json

# 4. Group and aggregate
jq '[.items[]] | group_by(.category) | map({category: .[0].category, count: length})' response.json
# 5. Merge multiple files into an array
jq -s '.' part-*.json > merged.json

# 6. Process JSON Lines line by line
jq -c 'select(.level == "error")' app.jsonl

# Common output switches
jq -r '.data.name'      # Strip the quotes so it pipes cleanly into the shell
jq -c '.'               # Compact output, one line
jq --arg id "$ID" '.id == $id'   # Pass arguments safely, avoiding injection through concatenation

--arg는 특별히 언급할 만합니다. 변수를 표현식에 직접 잇는 것은 값에 인용부호가 들어가는 순간 깨지는 반면, --arg는 jq가 이스케이프를 처리하게 합니다.

2.1 다른 도구 한눈에

도구 목적 한 문장
yq YAML/JSON/XML 변환 jq와 비슷한 문법, 설정 파일의 주력
dasel 형식 간 통합 쿼리 JSON/YAML/TOML/XML/CSV 지원
fx 대화형 JSON 탐색 터미널에서 클릭하며 탐색
jless 터미널 페이징 탐색 less처럼, 접기와 검색 포함
gron JSON을 grep 가능한 줄로 평탄화 "이 필드가 어느 단계에 있나" 파악에 탁월
ajv-cli 스키마 검증 CI에서 JSON 파일 검증

gron의 발상은 특히 알아 둘 만합니다:

$ echo '{"a":{"b":[1,2]}}' | gron
json = {};
json.a = {};
json.a.b = [];
json.a.b[0] = 1;
json.a.b[1] = 2;

평탄화되면 grep으로 경로를 찾을 수 있어 에디터에서 단계별로 확장하는 것보다 훨씬 빠릅니다.

3. CI의 세 계층 검증

계층 도구 실패가 뜻하는 것
문법 JSON.parse / jq empty 파일 자체가 잘못됨
구조 ajv-cli + 스키마 필드 누락, 타입 오류
스타일 Prettier 형식 불일치, 시끄러운 diff
# Syntax: batch-validate every json file in the repository
find . -name '*.json' -not -path './node_modules/*' -print0 \
  | xargs -0 -n1 jq empty

# Structure: validate against a schema
npx ajv-cli validate -s schemas/order.json -d 'fixtures/orders/*.json' --strict=false

# Style: check only, do not modify
npx prettier --check "**/*.json"

세 계층이 모두 필요합니다. 오류 메시지가 완전히 다르기 때문입니다. jq empty는 "어느 줄이 깨졌는지", ajv는 "어느 필드가 어느 키워드를 위반했는지", Prettier는 "형식이 일치하지 않는다"만 말합니다.

4. pre-commit 훅

# .husky/pre-commit example (excerpt)
npx lint-staged
{
  "lint-staged": {
    "*.json": ["prettier --write", "jq empty"]
  }
}

package.json, tsconfig.json 같은 도구 설정 파일에 주의하세요. Prettier가 마음대로 들여쓰기를 바꾸게 하지 마세요. 일부 도구는 형식에 민감합니다(유효한 JSON은 아니어야 하지만 실제로 문제를 일으킨 적이 있습니다). 개별로 제외할 수 있습니다.

5. 디버깅 요령

  1. jq -e를 단언으로 쓰기: 표현식 결과가 false나 null이면 종료 코드가 1이라 스크립트 검사로 바로 쓸 수 있습니다.
  2. jq 'paths'로 모든 경로 나열, 낯선 문서 구조를 빠르게 훑을 수 있습니다.
  3. **jq '.. | objects | keys'**로 등장했던 모든 키 이름을 찾아 표기 불일치를 사냥할 수 있습니다.
  4. 에디터에서 비교 전에 형식화: 형식화되지 않은 diff는 순수한 소음이고, /diff 같은 구조 비교가 더 정확합니다.

6. 체크리스트

  • [ ] 에디터 오류 마커가 행과 열에 정확하고 파싱이 디바운스됩니다
  • [ ] 팀 스크립트가 문자열을 잇지 않고 jq --arg로 인자를 넘깁니다
  • [ ] CI에 세 검증 계층(문법/구조/스타일)이 모두 있습니다
  • [ ] pre-commit이 스테이징된 파일만 처리해 전체 스캔이 커밋을 늦추지 않습니다
  • [ ] 도구 버전이 package.json이나 .tool-versions에 고정됩니다

FAQ

CodeMirror 6와 Monaco 중 어떻게 고르나?

유틸리티 사이트와 모바일에는 CodeMirror 6를 선호하세요. 작고 모듈형이며 모바일 지원이 잘 되고, 구문 하이라이팅, 괄호 매칭, 접기를 필요에 따라 조합할 수 있습니다. 여러 파일, 정의로 이동, 완성 등 완전한 IDE 경험이 필요할 때만 Monaco를 꺼내세요. 비용은 수 MB 번들과 더 높은 커스터마이즈 노력입니다.

CI에 문법, 구조, 스타일 검사가 왜 함께 필요한가?

세 계층은 완전히 다른 오류를 보고합니다. jq empty는 어느 줄이 깨졌는지, ajv는 어느 필드가 어느 키워드를 위반했는지, Prettier는 형식이 일치하지 않는다고만 말합니다. 한 계층을 빼면 다른 계층의 오류를 오해하기 쉬워 세 계층을 모두 유지하고 한 계층이 실패하면 멈추세요.

CI에서 검증하는 대신 pre-commit에 의존해도 되나?

아니요. 훅은 스테이징된 파일만 처리해 1분 안에 피드백을 주지만 전체 저장소나 커밋을 우회한 변경을 덮지 못합니다. 완전성은 CI가 담당합니다. pre-commit은 스테이징된 파일에 두고 전체 스캔은 CI에 맡기세요.