jq로 JSON 다루기 — 터미널에서 API 응답 파싱하기

게시일: 2025년 11월 25일 · 13분 읽기

curl로 받은 JSON을 눈으로 읽고 있었다면, jq 하나를 배우면 세상이 달라진다. 나는 거의 매일 jq를 사용한다. API 응답 확인부터 데이터 변환까지, 모든 것이 가능하다.

jq 설치 및 기초


# macOS
brew install jq

# Ubuntu/Debian
sudo apt-get install jq

# 검증
jq --version

가장 기본적인 사용법


# JSON을 pretty print
echo '{"name":"John","age":30}' | jq .

# 특정 필드만 추출
echo '{"name":"John","age":30}' | jq .name

기본 필터

필드 접근


# 단일 필드
jq '.name' user.json

# 중첩된 필드
jq '.user.address.city' user.json

# 선택적 접근 (필드가 없을 수도 있음)
jq '.user.phone?' user.json

# 배열 인덱싱
jq '.items[0]' data.json

# 마지막 요소
jq '.items[-1]' data.json

배열 순회


# 배열의 모든 요소 순회
jq '.items[]' data.json

# 필터 결합
jq '.items[] | .name' data.json

파이프 연산자

파이프는 왼쪽의 출력을 오른쪽의 입력으로 전달한다.


# 여러 필터를 조합
jq '.user | .name' data.json

# 더 복잡한 파이프라인
jq '.items[] | .user | .name' data.json

배열 연산

map - 배열의 각 요소에 함수 적용


# 모든 사용자의 이름만 추출
jq '.users | map(.name)' data.json

# 더 복잡한 변환
jq '.users | map({name: .name, email: .email})' data.json

select - 조건에 맞는 요소 필터링


# 나이가 30 이상인 사용자만
jq '.users[] | select(.age >= 30)' data.json

# 특정 역할의 사용자만
jq '.users[] | select(.role == "admin")' data.json

# 여러 조건 조합
jq '.users[] | select(.age >= 30 and .role == "admin")' data.json

sort_by - 정렬


# 나이 기준으로 정렬
jq '.users | sort_by(.age)' data.json

# 역순 정렬
jq '.users | sort_by(.age) | reverse' data.json

# 여러 기준으로 정렬
jq '.users | sort_by(.age, .name)' data.json

group_by - 그룹화


# 역할별로 그룹화
jq '.users | group_by(.role)' data.json

# 그룹화한 후 각 그룹의 크기 계산
jq '.users | group_by(.role) | map({role: .[0].role, count: length})' data.json

객체 생성 및 변환

객체 구성


# 새로운 객체 생성
jq '.users[] | {name, email}' data.json

# 필드명 변경
jq '.users[] | {user_name: .name, user_email: .email}' data.json

# 계산된 필드
jq '.products[] | {name, price, tax: (.price * 0.1)}' data.json

배열을 객체로 변환


# 배열을 key-value 맵으로 변환
jq 'map({key: .id, value: .name}) | from_entries' data.json

# 또는
jq 'reduce .[] as $item ({}; .[$item.id | tostring] = $item.name)' data.json

조건부 로직

if-then-else


# 나이에 따라 분류
jq '.users[] | {name, age, category: (if .age < 18 then "미성년자" elif .age < 65 then "성인" else "노년층" end)}' data.json

조건부 필터링


# status가 "active"이면 포함, 아니면 empty (결과에서 제외)
jq '.items[] | if .status == "active" then . else empty end' data.json

내장 함수

length - 길이


# 배열의 길이
jq '.users | length' data.json

# 문자열의 길이
jq '.name | length' data.json

keys와 values


# 객체의 모든 키
jq 'keys' data.json

# 객체의 모든 값
jq 'values' data.json

# 키-값 쌍
jq 'to_entries' data.json

type - 타입 확인


# 값의 타입 반환
jq '.value | type' data.json

# 타입 필터링
jq '.[] | select(type == "number")' data.json

has - 필드 존재 확인


# email 필드가 있는지 확인
jq '.users[] | select(has("email"))' data.json

실전 예제

GitHub API로 저장소 정보 가져오기


# 사용자의 모든 저장소 이름 출력
curl -s https://api.github.com/users/torvalds/repos | jq '.[].name'

# 별이 많은 저장소 순서로 정렬
curl -s https://api.github.com/users/torvalds/repos |   jq 'sort_by(.stargazers_count) | reverse | .[0:5] | .[] | {name, stars: .stargazers_count}'

# 언어별 저장소 그룹화
curl -s https://api.github.com/users/torvalds/repos |   jq 'group_by(.language) | map({language: .[0].language, count: length})'

AWS CLI 출력 파싱


# EC2 인스턴스 목록에서 ID와 상태만 추출
aws ec2 describe-instances |   jq '.Reservations[].Instances[] | {InstanceId, State: .State.Name}'

# 실행 중인 인스턴스만 필터링
aws ec2 describe-instances |   jq '.Reservations[].Instances[] | select(.State.Name == "running") | .InstanceId'

JSON 로그 분석


# 파일에서 모든 에러 로그 추출 (각 줄이 JSON)
cat app.log | jq 'select(.level == "error")'

# 에러 메시지와 타임스탐프만 추출
cat app.log | jq 'select(.level == "error") | {timestamp, message}'

# 시간대별로 에러 개수 세기
cat app.log |   jq 'select(.level == "error") | .timestamp' |   sort |   uniq -c

고급 기능

reduce - 복합 계산

reduce는 배열을 순회하면서 누적값을 계산한다.


# 모든 가격의 합계
jq 'reduce .items[] as $item (0; . + $item.price)' data.json

# 사용자별로 주문 합계
jq 'reduce .orders[] as $order ({}; .[$order.user_id | tostring] = (. + $order.amount))' data.json

재귀적 탐색

.. 를 사용하면 모든 중첩된 값에 접근할 수 있다.


# 객체 내의 모든 "id" 필드 찾기
jq '.. | .id?' data.json

스트링 연산


# 문자열 연결
jq '.first_name + " " + .last_name' data.json

# 문자열 분할
jq '.email | split("@")[0]' data.json

# 정규표현식 매칭
jq '.email | test("^[^@]+@gmail\.com$")' data.json

# 정규표현식으로 추출
jq '.email | capture("(?[^@]+)@(?.+)")' data.json

팁과 트릭

다중 입력 처리


# 여러 JSON 파일 처리
jq -s '.' file1.json file2.json  # 배열로 변환

# 또는
jq -s 'add' file1.json file2.json  # 객체들을 병합

Raw output


# JSON 따옴표 없이 텍스트로 출력
jq -r '.name' data.json

Compact output


# 한 줄로 출력
jq -c '.' data.json

색상 출력 비활성화


# 파일로 리다이렉트할 때 유용
jq -M '.' data.json

성능 팁

큰 데이터셋을 다룰 때는 효율성이 중요하다.


# 스트리밍 모드 (한 번에 모든 데이터를 메모리에 로드하지 않음)
jq --stream '.' large_file.json

# select 대신 map 사용 (더 효율적)
jq 'map(select(.active))' data.json  # 더 나음
jq '.[] | select(.active)' data.json  # 덜 효율적

# 불필요한 map 제거
jq '.[].name' data.json  # 충분함
jq '.[] | map(.name)' data.json  # 불필요하게 복잡

jq는 처음에는 어려워 보이지만, 자주 사용하다 보면 강력함을 느낀다. 터미널에서 JSON을 다루는 작업이 얼마나 쉬워질 수 있는지 경험해보자.

iL
ian.lab

실무 개발자입니다. 현장에서 겪은 문제와 해결 과정을 기록합니다. 오류 제보는 연락처로 보내주세요.