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

핵심부터 말하면, 에디터는 시나리오로 고르세요. 유틸리티 사이트와 모바일에는 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());
}
}),
],
});
주의할 네 가지:
json()이 구문 하이라이팅과 괄호 매칭을 제공합니다. 직접 쓰지 마세요.jsonParseLinter()가 행/열 수준 오류 마커를 만듭니다.lintGutter()와 짝지어 줄 번호 옆에 아이콘을 표시하세요.updateListener는 반드시 디바운스해야 합니다. 10만 줄에 키 입력마다JSON.parse를 호출하면 눈에 띄게 늦습니다.- 큰 문서는 한 번에 전부 렌더하지 않도록
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. 디버깅 요령
jq -e를 단언으로 쓰기: 표현식 결과가false나null이면 종료 코드가 1이라 스크립트 검사로 바로 쓸 수 있습니다.jq 'paths'로 모든 경로 나열, 낯선 문서 구조를 빠르게 훑을 수 있습니다.- **
jq '.. | objects | keys'**로 등장했던 모든 키 이름을 찾아 표기 불일치를 사냥할 수 있습니다. - 에디터에서 비교 전에 형식화: 형식화되지 않은 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에 맡기세요.