Bash에서 JSON 포맷하는 방법: jq 완전 가이드
무료 JSON 포맷터을 브라우저에서 직접 사용하세요 — 설치 불필요.
JSON 포맷터 온라인으로 사용하기 →배포 스크립트가 API 응답을 처리하거나 CI에서 설정 파일을 검증하기 시작하면, bash에서 JSON을 포맷하는 방법을 아는 것이 곧 필수적인 기술이 됩니다. 실제 사용 사례의 99%를 커버하는 두 가지 도구는 jq와 python3 -m json.tool입니다 — 둘 다 bash JSON 포맷 파이프라인을 안정적으로 처리하고, 종료 코드로 검증하며, CI/CD 워크플로우에 깔끔하게 통합됩니다. 터미널 없이 빠르게 확인하고 싶을 때는 브라우저 기반 JSON Formatter가 즉시 처리해 줍니다. 이 가이드에서는 jq 설치, 파이프 및 파일 포맷팅, 검증 함수, GitHub Actions에서의 CI/CD 통합, pre-commit 훅, heredoc 패턴, 그리고 Python 표준 라이브러리 대안을 언제 사용할지에 대해 다룹니다.
- •
jq .은 포맷과 검증을 동시에 수행 — 잘못된 JSON이면 종료 코드 1로 종료 - • CI 파이프라인에서는
jq -e사용: 출력이 비어있거나 false/null이면 비정상 종료 - •
jq . file.json > /dev/null && echo "valid"— 출력 변경 없이 검증만 수행 - •
python3 -m json.tool은 추가 설치 없이 모든 시스템에서 사용 가능 - •
jq . f.json > f.json은 절대 하지 마세요 — 쉘이 jq가 읽기 전에 소스 파일을 잘라버립니다
Bash에서 JSON 포맷팅이란?
bash에서 JSON 포맷팅이란 압축된 JSON을 들여쓰기가 있는 사람이 읽기 쉬운 출력으로 변환하는 것입니다. 기본 데이터는 변경되지 않으며 — 공백과 줄바꿈만 달라집니다. 스크립트 컨텍스트에서 이것이 중요한 이유는 두 가지입니다: 디버깅 시의 가독성과, 포맷터가 부수 효과로 구문을 이중 검사할 때의 검증성입니다. jq 같은 도구는 재포맷 전에 JSON을 완전히 파싱하므로, 포맷이 성공하면 동시에 유효성 확인도 된 것입니다. 이 포맷과 검증을 한 단계에서 수행하는 이중 동작이 jq를 자동화 파이프라인에서 매우 유용하게 만드는 이유입니다.
{"service":"payments-api","version":"2.4.1","database":{"host":"db-prod-01.internal","port":5432,"pool_size":20},"cache":{"enabled":true,"ttl":300}}{
"service": "payments-api",
"version": "2.4.1",
"database": {
"host": "db-prod-01.internal",
"port": 5432,
"pool_size": 20
},
"cache": {
"enabled": true,
"ttl": 300
}
}jq — Bash에서 JSON 포맷하기
jq는 쉘 스크립트에서 JSON 처리의 사실상 표준입니다(jq 1.6+, bash 4+). JSON을 포맷, 필터링, 변환, 검증할 수 있는 전용 명령줄 JSON 프로세서입니다. 항등 필터 .는 입력을 변경 없이 통과시키지만 포맷된 상태로 출력합니다. jq가 입력을 파싱할 수 없으면 종료 코드 1로 종료합니다 — 이것이 스크립팅에 이상적인 이유입니다: 포맷팅과 검증이 단일 작업으로 완결됩니다.
jq 설치
# macOS brew install jq # Debian / Ubuntu apt-get install -y jq # Fedora / RHEL / CentOS dnf install jq # Alpine (Docker images) apk add --no-cache jq # Verify jq --version # jq-1.7.1
stdin 및 파일에서 포맷하기
# Pipe inline JSON through jq
echo '{"host":"db-prod-01.internal","port":5432}' | jq .
# Format a file directly (prints to stdout)
jq . config/feature-flags.json
# Format with 4-space indentation
jq --indent 4 . config/feature-flags.json
# Format using tabs instead of spaces
jq --tab . config/feature-flags.json포맷된 출력을 파일로 저장하기
# Save formatted output (do NOT redirect back to the same file) jq . compact.json > formatted.json # Compact (minify) — reverse of formatting jq -c . formatted.json
jq는 잘못된 JSON이면 종료 코드 1, 성공 시 0, 사용 오류 시 5로 종료합니다. 스크립트 전반에 걸쳐 if 문과 || exit 1 가드에 이를 활용하세요.키 정렬 및 컬러 출력 제거
# Sort all keys alphabetically (useful for deterministic diffs) jq --sort-keys . config/app-config.json # Disable colour output when writing to a log file jq --monochrome-output . response.json >> deploy.log
jq 옵션 참조
포맷팅 및 검증 워크플로우에서 가장 자주 사용되는 jq 플래그:
Bash 스크립트에서 JSON 검증하기
jq에서 검증과 포맷팅은 동일한 작업입니다 — 출력 전에 파싱합니다. 포맷된 출력 없이 종료 코드만 필요할 때는 stdout을 /dev/null로 리다이렉트합니다. 아래 패턴은 배포 스크립트, pre-commit 훅, CI 파이프라인 전반에서 재사용할 수 있습니다. 인시던트 대응 시 낯선 API 페이로드를 마주치면, 제가 처음 하는 일은 jq로 파이프하는 것입니다 — 압축된 JSON 덩어리를 실제로 읽고 디버깅할 수 있는 형태로 바꿔줍니다.
재사용 가능한 검증 함수
validate_json() {
local file="$1"
if jq . "$file" > /dev/null 2>&1; then
echo "✓ Valid JSON: $file"
return 0
else
echo "✗ Invalid JSON: $file" >&2
return 1
fi
}잘못된 설정 시 배포 중단하기
CONFIG="infra/k8s/app-config.json"
validate_json "$CONFIG" || { echo "Aborting deploy: invalid config" >&2; exit 1; }디렉터리 내 모든 JSON 파일 검증하기
find ./config -name "*.json" | while read -r f; do jq . "$f" > /dev/null 2>&1 || echo "INVALID: $f" done
-e / --exit-status 플래그는 한 단계 더 나아갑니다: 출력이 false 또는 null일 때도 종료 코드 1로 종료합니다. 특정 필드가 참인지 확인하는 데 활용하세요: jq -e '.feature_flags.new_checkout' config.json.파일 및 API 응답에서 JSON 포맷하기
쉘 스크립트에서 JSON의 두 가지 일반적인 소스는 디스크의 파일과 curl을 통한 HTTP API 응답입니다. 각각 처리 패턴이 약간 다릅니다. 파일의 경우 주요 관심사는 안전한 제자리 편집입니다. API 응답의 경우 핵심은 curl의 프로그레스 바를 억제하여 jq의 입력을 오염시키지 않는 것입니다.
파일의 안전한 제자리 포맷팅
# Format and overwrite safely using a temp file tmp=$(mktemp) jq --indent 2 . config/feature-flags.json > "$tmp" && mv "$tmp" config/feature-flags.json echo "Formatted config/feature-flags.json"
curl API 응답 포맷하기
# Format deployment status from API DEPLOY_ID="dep_8f3a2b9c" curl -s \ -H "Authorization: Bearer $DEPLOY_API_TOKEN" \ "https://api.deployments.internal/v1/deploys/$DEPLOY_ID" \ | jq --indent 2 .
포맷과 필터링 동시에 수행하기
# Get formatted + filter to errors only from a monitoring endpoint
curl -s "https://monitoring.internal/api/events?level=error&limit=10" \
| jq '[.events[] | {id, message, timestamp, service}]' \
|| { echo "Failed to fetch or parse events" >&2; exit 1; }여기서 || { ... } 패턴은 매우 중요합니다. 이것 없이는 curl 실패나 잘못된 API 응답이 조용히 통과되어 스크립트의 다음 단계가 빈 데이터나 부분 데이터로 동작하게 됩니다. 필터 표현식을 먼저 작성하지 않고 복잡한 중첩 응답을 살펴봐야 할 경우, 브라우저 기반 JSON Formatter를 사용하면 원시 응답을 붙여넣어 트리를 대화형으로 탐색할 수 있습니다.
CI/CD 파이프라인에서 JSON 포맷하기
CI야말로 JSON 검증 게이트가 가장 중요한 곳입니다 — 프로덕션에 도달한 잘못된 설정을 롤백하는 것은 파이프라인 실패보다 훨씬 더 고통스럽습니다. 대부분의 자료는 jq를 일회성 터미널 사용으로 설명하지만, 아래 패턴은 제가 프로덕션 SRE 워크플로우에서 설정 오류가 배포 슬롯에 도달하기 전에 잡아내기 위해 실제로 사용하는 것들입니다.
GitHub Actions — 모든 JSON 설정 검증하기
- name: Validate JSON configs
run: |
echo "Validating JSON configuration files..."
find . -name "*.json" -not -path "*/node_modules/*" | while read -r f; do
if ! jq . "$f" > /dev/null 2>&1; then
echo "::error file=$f::Invalid JSON syntax"
exit 1
fi
done
echo "All JSON files are valid"Pre-commit 훅 — 스테이징된 JSON 파일 검증하기
#!/usr/bin/env bash
set -euo pipefail
STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep '\.json$' || true)
[ -z "$STAGED" ] && exit 0
for f in $STAGED; do
jq . "$f" > /dev/null 2>&1 || { echo "Invalid JSON: $f"; exit 1; }
done
echo "JSON validation passed"scripts/validate-json.sh에 저장하고, chmod +x scripts/validate-json.sh로 실행 가능하게 만든 다음 심볼릭 링크를 생성하세요: ln -s ../../scripts/validate-json.sh .git/hooks/pre-commit.Bash에서 JSON 변수 및 Heredoc 포맷하기
쉘 스크립트는 환경 변수, git 메타데이터, 또는 계산된 값으로부터 동적으로 JSON 페이로드를 구성하는 경우가 많습니다. 가장 안전한 패턴은 문자열 보간 대신 jq -n --arg / --argjson를 사용하는 것입니다 — 값에 따옴표나 줄바꿈이 포함되는 순간 문자열 보간은 깨집니다. jq로 파이프할 때 JSON의 공백에 의한 단어 분리를 방지하기 위해 항상 변수를 큰따옴표로 감싸세요.
저장된 API 응답 변수 포맷하기
# Always quote "$API_RESPONSE" — whitespace in JSON would break an unquoted expansion echo "$API_RESPONSE" | jq --indent 2 .
jq -n으로 페이로드 구성 및 포맷하기
payload=$(jq -n \
--arg env "production" \
--arg version "$(git describe --tags)" \
--argjson replicas 3 \
'{environment: $env, version: $version, replicas: $replicas}')
# Inspect the built payload
echo "$payload" | jq .
# Post it to an API
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $DEPLOY_API_TOKEN" \
-d "$payload" \
"https://api.deployments.internal/v1/deploys"--arg는 항상 문자열 값을 바인딩합니다. --argjson은 값을 먼저 JSON으로 파싱하므로, 필터 표현식 내에서 따옴표 없이 숫자, 불리언, 배열, 객체를 전달할 수 있습니다.jq 설치 없이 Bash에서 JSON 포맷하기
jq를 사용할 수 없는 경우 — 최소한의 Docker 이미지, 제한된 CI 러너, 또는 패키지를 설치할 수 없는 시스템 — Python의 내장 json.tool 모듈이 동일한 핵심 기능을 제공합니다. Python 표준 라이브러리의 일부이므로 Python 3가 설치되어 있으면 추가 의존성 없이 작동합니다.
# Format from a file python3 -m json.tool config.json # Control indent width python3 -m json.tool --indent 2 config.json # Sort keys alphabetically python3 -m json.tool --sort-keys config.json # Format from stdin (e.g., piped from curl) curl -s https://api.deployments.internal/v1/status | python3 -m json.tool
python3 -m json.tool은 jq보다 더 엄격합니다: 후행 쉼표, 주석, JSON5 확장을 거부합니다. 이 엄격함은 프로덕션 설정 검증에는 바람직하지만, 서드파티 도구의 느슨한 JSON으로 작업할 때는 불편할 수 있습니다. 또한 컬러 출력을 생성하지 않아 대화형 사용에서는 jq보다 터미널 검사가 불편합니다.# Validation with exit code (same semantics as jq)
python3 -m json.tool config.json > /dev/null && echo "valid" || echo "invalid"
# Inline string validation
echo '{"service":"payments-api","healthy":true}' | python3 -m json.tool > /dev/null
echo "Exit code: $?" # 0 = valid구문 강조가 있는 터미널 출력
jq는 기본적으로 출력에 색을 입힙니다 — 키는 파란색, 문자열은 초록색, 숫자는 흰색. 스크롤 가능한 페이저에서 완전한 JSON 구문 강조가 필요하거나 터미널 세션에서 큰 중첩 응답을 디버깅할 때는 bat이 가장 편리한 경험을 제공합니다. 둘 다 디버깅과 대화형 검사에 유용하지만, 파일이나 API 응답에 출력을 쓸 때는 어느 쪽도 사용하지 말아야 합니다.
bat 설치
# macOS brew install bat # Debian / Ubuntu (binary may be named batcat — alias if so) apt-get install -y bat # alias bat=batcat # add to ~/.bashrc if needed # Verify bat --version # bat 0.24.0
구문 강조로 JSON 파일 보기
# Syntax-highlighted JSON in the pager (press q to exit) bat config/app-config.json # Disable paging — print directly to terminal bat --paging=never config/app-config.json # Pipe jq output through bat for coloured inspection jq '.database' infra/app-config.json | bat --language=json --paging=never
스크롤 가능한 페이저에서 컬러 jq 출력 보기
# -C forces color even when stdout is not a tty (e.g. when piping to less) jq -C . logs/deploy-response.json | less -R
bat / jq -C)은 터미널 검사와 디버깅에만 사용하세요. 로그 파일에 쓰거나 다른 도구로 파이프하기 전에 ANSI 컬러 코드를 제거하세요 — jq -M . (--monochrome-output) 또는 bat --plain을 사용하세요.Bash에서 대용량 JSON 파일 처리하기
JSON 파일이 50–100 MB를 초과하면, jq의 기본 모드로 메모리에 로드하면 느려지거나 메모리가 제한된 호스트(예: 512 MB 제한 Docker 컨테이너)에서 OOM이 발생할 수 있습니다. jq --stream은 전체 문서를 버퍼링하지 않고 읽으면서 경로/값 쌍을 증분적으로 출력합니다. NDJSON(한 줄에 하나의 JSON 객체)의 경우 jq에는 더 효율적인 네이티브 접근법이 있습니다.
jq --stream으로 대용량 JSON 파일 스트리밍하기
# --stream emits [path, scalar] pairs as jq reads the input # Extract all "status" fields from a large log archive without loading it fully jq -c --stream 'if length == 2 and (.[0][-1] == "status") then .[1] else empty end' logs/archive-2026-03.json
NDJSON / JSON Lines — 한 줄씩 객체 처리하기
# NDJSON: one JSON object per line — common in Kafka exports, Fluentd, and Logstash
# -R reads raw lines; fromjson? skips lines that are not valid JSON
jq -c -R 'fromjson? | {id: .request_id, status: .http_status, latency: .duration_ms}' logs/access-2026-03-13.ndjson > logs/summary.ndjson# Shell loop alternative — useful when you need per-line error handling
while IFS= read -r line; do
echo "$line" | jq -c '{id: .request_id, status: .http_status}' 2>/dev/null || echo "SKIP: malformed line" >&2
done < logs/access-2026-03-13.ndjsonjq . file.json에서 --stream으로 전환하세요. NDJSON 파이프라인에서는 쉘 while read 루프보다 jq -R 'fromjson?'를 선호하세요 — 줄마다 서브쉘을 생성하지 않아 훨씬 빠릅니다.자주 하는 실수
문제: 쉘은 jq가 입력을 읽기 전에 출력 파일을 열고 잘라버립니다. 소스와 대상이 같은 경로면 jq는 빈 파일을 읽게 됩니다.
해결 방법: 먼저 mktemp 임시 파일에 쓰고, mv로 원본을 원자적으로 교체합니다.
jq --indent 2 . settings.json > settings.json
tmp=$(mktemp) && jq --indent 2 . settings.json > "$tmp" && mv "$tmp" settings.json
문제: 오류 핸들러가 없으면 JSON이 잘못되었을 때 스크립트가 빈 또는 누락된 포맷 파일로 조용히 계속 실행됩니다 — 후속 단계가 혼란스러운 오류로 실패합니다.
해결 방법: 후속 단계에서 사용되는 출력을 생성하는 모든 jq 호출 뒤에 || { echo '...' >&2; exit 1; }를 추가합니다.
jq . response.json > formatted.json
jq . response.json > formatted.json || { echo "Invalid JSON in response.json" >&2; exit 1; }문제: curl은 기본적으로 프로그레스 바를 stderr에 출력합니다. stderr가 stdout과 합쳐질 때(예: 서브쉘이나 로그 캡처), 프로그레스 바 텍스트가 jq의 입력에 나타나 파싱 오류를 일으킵니다.
해결 방법: jq로 파이프할 때 curl에 항상 -s(silent)를 전달하세요. 진단 출력이 필요하면 -v나 --fail-with-body를 별도로 사용하세요.
curl https://api.payments.internal/config | jq .
curl -s https://api.payments.internal/config | jq .
문제: -r / --raw-output 플래그는 최상위 문자열 값에서 JSON 따옴표를 제거할 뿐 — 객체나 배열을 포맷하지 않습니다. 객체 입력에 -r .을 전달하면 들여쓰기된 출력이 아닌 동일한 압축 객체가 생성됩니다.
해결 방법: 포맷팅에는 jq .(-r 플래그 없이)를 사용하세요. -r은 jq -r '.version' config.json 같은 순수 문자열 값 추출에만 사용하세요.
jq -r . config.json
jq . config.json
jq vs python3 vs json_pp — 빠른 비교
도구 선택은 환경에서 무엇을 사용할 수 있는지, 그리고 기본 포맷팅 이상으로 무엇이 필요한지에 달려 있습니다:
대부분의 bash 스크립팅과 CI/CD 작업에서 jq가 올바른 기본 선택입니다 — 런타임 의존성 없이 단일 바이너리로 검증, 포맷, 필터링, 신뢰할 수 있는 종료 코드를 제공합니다. 추가 패키지를 설치할 수 없고 Python이 이미 있을 때는 python3 -m json.tool로 대체하세요.
자주 묻는 질문
bash에서 JSON 파일을 제자리에서 포맷하는 방법은 무엇인가요?
jq의 출력을 같은 파일로 리다이렉트하지 마세요 — 쉘이 jq가 읽기 전에 파일을 잘라버립니다. 대신 임시 파일에 먼저 쓰고, mv로 원본 파일을 원자적으로 교체하세요.
tmp=$(mktemp) jq --indent 2 . config/app-config.json > "$tmp" && mv "$tmp" config/app-config.json echo "파일을 제자리에서 성공적으로 포맷했습니다"
bash 스크립트에서 JSON을 검증하고 오류 시 종료하는 방법은 무엇인가요?
파일을 파이프 또는 인수로 jq에 전달하고 stdout을 /dev/null로 리다이렉트합니다. ||를 사용하여 비정상 종료를 캐치하고 스크립트를 중단합니다. jq는 파싱 오류 시 종료 코드 1로 종료하므로 CI 게이트에 신뢰성 있게 활용할 수 있습니다.
validate_json() {
local file="$1"
if jq . "$file" > /dev/null 2>&1; then
echo "✓ Valid JSON: $file"
return 0
else
echo "✗ Invalid JSON: $file" >&2
return 1
fi
}
validate_json infra/k8s/app-config.json || exit 1jq를 설치하지 않고 bash에서 JSON을 포맷하는 방법은 무엇인가요?
python3의 내장 json.tool 모듈을 사용하세요 — 모든 표준 Python 설치에 포함되어 있으며 jq와 동일한 종료 코드 의미론으로 올바르게 들여쓰기된 출력을 생성합니다.
# Format from a file python3 -m json.tool config.json # Format from stdin (e.g., a curl response) curl -s https://api.internal/status | python3 -m json.tool --indent 2
bash에서 curl 응답을 JSON 포맷으로 처리하는 방법은 무엇인가요?
curl에 항상 -s(silent)를 전달하여 프로그레스 바가 jq의 입력을 오염시키지 않도록 하세요. curl의 stdout을 직접 jq로 파이프하면 됩니다.
DEPLOY_ID="dep_8f3a2b9c" curl -s \ -H "Authorization: Bearer $DEPLOY_API_TOKEN" \ "https://api.deployments.internal/v1/deploys/$DEPLOY_ID" \ | jq --indent 2 .
jq를 사용하여 JSON 파일의 일부만 포맷하는 방법은 무엇인가요?
항등 필터(.) 대신 jq 경로 표현식을 사용하여 중첩된 객체나 배열을 추출하고 포맷합니다. 결과 자체가 포맷된 JSON입니다.
# Format just the database config block
jq --indent 2 '.database' infra/app-config.json
# Format + filter events array to error level only
jq '[.events[] | select(.level == "error") | {id, message, service}]' events.json잘못된 JSON에 대해 jq는 어떤 종료 코드를 반환하나요?
jq는 파싱 오류에 대해 종료 코드 1을 반환하며, -e / --exit-status 플래그가 설정되어 출력이 false 또는 null인 경우에도 종료 코드 1을 반환합니다. 종료 코드 0은 유효한 JSON이 파싱되어 참 값의 출력이 생성되었음을 의미합니다. 종료 코드 5는 시스템이 사용 오류를 만났음을 의미합니다.
# Test exit code directly
echo '{"ok":true}' | jq . > /dev/null 2>&1; echo "exit: $?" # exit: 0
echo '{bad json}' | jq . > /dev/null 2>&1; echo "exit: $?" # exit: 1
# -e flag: exit 1 if output is false/null
echo 'null' | jq -e . > /dev/null 2>&1; echo "exit: $?" # exit: 1관련 도구
bash JSON 포맷팅의 브라우저 기반 대안 및 보완 도구 — 시각적 인터페이스, 공유 가능한 링크가 필요하거나 터미널 밖에서 작업할 때 유용합니다:
Nadia is a site reliability engineer who lives in the terminal. She writes Bash scripts that process logs, transform data, and orchestrate infrastructure across fleets of servers. She is a heavy user of jq, awk, and sed and writes about shell one-liners, text processing pipelines, data serialisation from the command line, and the practical Bash patterns that SREs reach for when speed matters more than elegance.
Erik is a DevOps engineer who has spent years writing and maintaining the shell scripts that hold CI/CD pipelines together. He writes about Bash best practices, portable POSIX shell, encoding and decoding in shell scripts, secret management from the command line, and the patterns that separate reliable automation scripts from brittle ones. He is a strong believer in making shell scripts readable and testable with tools like bats-core.