JSONPath 쿼리 가이드: 전문가처럼 데이터 추출하기
JSONPath는 XPath가 XML에 대해 하는 것처럼 JSON을 위한 쿼리 언어입니다. API의 복잡하고 깊게 중첩된 JSON 응답을 다룰 때, JSONPath를 사용하면 루프와 조건문을 작성하지 않고 정확히 필요한 데이터를 추출할 수 있습니다. 이 가이드에서는 구문, 연산자, 실제 패턴을 다룹니다.
왜 JSONPath인가?
중첩 데이터가 있는 일반적인 API 응답을 고려하세요:
{
"store": {
"books": [
{ "title": "Clean Code", "author": "Robert Martin", "price": 32.99, "tags": ["programming", "best-practices"] },
{ "title": "Design Patterns", "author": "Gang of Four", "price": 44.99, "tags": ["programming", "architecture"] },
{ "title": "The Pragmatic Programmer", "author": "Hunt & Thomas", "price": 39.99, "tags": ["programming", "career"] }
],
"music": [
{ "title": "Kind of Blue", "artist": "Miles Davis", "price": 12.99 }
]
}
}
모든 책 제목을 얻으려면 루프를 작성하거나 JSONPath를 사용할 수 있습니다: $.store.books[*].title
기본 구문
| 표현식 | 설명 |
|---|---|
$ | 루트 객체 |
. | 자식 연산자 |
.. | 재귀 하강 (모든 레벨 검색) |
[*] | 와일드카드 (모든 요소) |
[n] | 배열 인덱스 (0 기반) |
[n,m] | 다중 인덱스 |
[start:end:step] | 배열 슬라이스 |
[?()] | 필터 표현식 |
@ | 현재 요소 (필터에서) |
점 표기법 vs 괄호 표기법
두 표기법 모두 속성에 접근하지만, 특수 문자에는 괄호 표기법이 필요합니다:
# 점 표기법
$.store.books[0].title
# 괄호 표기법 (동일)
$['store']['books'][0]['title']
# 특수 문자가 있는 키에 필수
$['store']['price-range']
배열 연산
인덱싱
$.store.books[0] # 첫 번째 책
$.store.books[-1] # 마지막 책
$.store.books[0,2] # 첫 번째와 세 번째 책
슬라이싱
$.store.books[0:2] # 처음 두 권 (인덱스 0과 1)
$.store.books[1:] # 첫 번째를 제외한 모든 책
$.store.books[:2] # 처음 두 권
$.store.books[::2] # 하나 건너 하나씩
와일드카드
$.store.books[*].title # 모든 책 제목
$.store.* # 모든 스토어 컬렉션 (books, music)
$..title # 모든 깊이의 모든 제목
$..price # 모든 깊이의 모든 가격
재귀 하강 연산자(..)는 계층 구조에서의 위치에 관계없이 값을 추출하는 데 특히 강력합니다.
필터 표현식
필터는 조건에 기반하여 요소를 선택합니다:
# $40 미만의 책
$.store.books[?(@.price < 40)]
# 특정 저자의 책
$.store.books[?(@.author == 'Robert Martin')]
# 태그가 2개 이상인 책
$.store.books[?(@.tags.length > 2)]
# 'price' 속성이 있는 책
$.store.books[?(@.price)]
필터 결합
# $40 미만이면서 programming 태그인 책
$.store.books[?(@.price < 40 && @.tags[0] == 'programming')]
실제 예시
API 응답에서 추출
GitHub API — 모든 저장소 이름 가져오기:
$[*].name
날씨 API — 오늘의 온도 가져오기:
$.daily[0].temp.day
이커머스 API — 모든 제품 이미지 가져오기:
$.products[*].images[0].url
설정 추출
Docker Compose — 모든 서비스 이름 가져오기:
$.services.*~
Package.json — 모든 종속성 이름 가져오기:
$.dependencies.*~
JSONPath vs jq
JSONPath와 jq는 모두 JSON 쿼리 도구이지만, 다른 상황에서 사용됩니다:
| 기능 | JSONPath | jq |
|---|---|---|
| 환경 | 라이브러리, API | 커맨드 라인 |
| 구문 | XPath 영감 | 커스텀 함수형 |
| 변환 | 쿼리만 | 쿼리 + 변환 |
| 표준 | RFC 9535 | 사실상 표준 |
커맨드 라인 JSON 처리에는 jq가 더 강력합니다. 애플리케이션에 쿼리를 임베딩하거나 웹 도구에서 사용하는 경우 JSONPath가 더 널리 지원됩니다.
JSON Path 탐색기로 JSONPath 표현식을 인터랙티브하게 시도해보세요. JSON을 붙여넣고, 쿼리를 작성하고, 결과를 즉시 확인하세요.
구현 예시
JavaScript (jsonpath-plus)
const { JSONPath } = require('jsonpath-plus');
const result = JSONPath({
path: '$.store.books[?(@.price < 40)].title',
json: data
});
// ["Clean Code", "The Pragmatic Programmer"]
Python (jsonpath-ng)
from jsonpath_ng import parse
expr = parse('$.store.books[*].title')
titles = [match.value for match in expr.find(data)]
RFC 9535: JSONPath 표준
2024년 2월, JSONPath가 RFC 9535로 공식 표준화되었습니다. 이는 구현 간 역사적 불일치를 해결합니다. 핵심 표준화된 동작:
- 배열 인덱스는 0 기반
- 필터 표현식에서
@는 현재 요소 - 문자열 비교는 대소문자 구분
- 결과는 항상 일치하는 값의 배열
JSONPath 라이브러리를 선택할 때 일관된 동작을 위해 RFC 9535를 지원하는 것을 선호하세요.
자주 묻는 질문
JSONPath와 JSON Pointer의 차이점은 무엇인가요?
JSONPath는 와일드카드와 필터를 사용하여 여러 값을 매칭할 수 있는 쿼리 언어입니다. JSON Pointer (RFC 6901)는 정확히 하나의 값을 가리키는 간단한 경로 구문입니다: /store/books/0/title. 검색하거나 필터링해야 할 때는 JSONPath를, 정확한 경로를 알고 있을 때는 JSON Pointer를 사용하세요.
JSONPath로 JSON 데이터를 수정할 수 있나요?
표준 JSONPath는 읽기 전용입니다 — 추출만 하고 수정하지 않습니다. 일부 라이브러리는 set/delete 연산으로 JSONPath를 확장하지만, 이는 비표준입니다. 변환에는 jq (커맨드 라인)를 고려하거나 애플리케이션 코드를 작성하세요. JSON 구조를 보고 탐색하는 데는 JSON 포맷터가 쿼리 작성 전에 데이터를 이해하는 데 도움을 줍니다.
관련 리소스
- JSON Path 탐색기 — 브라우저에서 JSONPath 표현식 테스트
- JSON 포맷팅 모범 사례 — 쉬운 쿼리를 위한 JSON 구조화
- JSON Schema 유효성 검사 가이드 — JSONPath 쿼리가 기대하는 구조 검증