JSON 최소화는 gzip 뒤에서 8%만 남는다
들여쓰기를 지우면 44% 줄지만 압축 후에는 8%입니다. 크기와 경로 찾기를 실측으로 정리했습니다.
API 응답에서 들여쓰기를 빼면 얼마나 줄어드는지 재 봤습니다.
44%와 8%
사용자 200명 배열입니다.
들여쓴 것 50,482바이트 → gzip 2,753
최소화 28,474바이트 → gzip 2,531
최소화만 놓고 보면 44% 줄었습니다. gzip을 거친 뒤에는 차이가 **8%**입니다.
들여쓰기는 같은 공백의 반복이라 압축기가 거의 공짜로 지웁니다. 전송 압축이 켜져 있다면 최소화의 실질 이득은 그 8%입니다.
그래도 8%가 의미 있는 자리가 있습니다 — 압축을 못 거는 경로, 로컬 저장, 메모리에 올려 두는 문자열입니다. 반대로 압축이 이미 걸려 있고 응답이 작다면 개발 중에는 들여쓴 편이 읽기 편합니다.
최소화가 바꾸지 않는 것
입력 : {"a":1.0,"b":1e3,"c":" 공백 ","d":null}
출력 : {"a":1,"b":1000,"c":" 공백 ","d":null}
1.0이 1이 됩니다. JSON에는 정수와 실수의 구분이 없어서, 파서가 숫자로 읽은 뒤 다시 쓰면 표기가 정규화됩니다. 소수점을 유지해야 하는 곳에서는 문자열로 보내야 합니다.
1e3도 1000이 됩니다. 값은 같지만 문자열은 다릅니다.
문자열 안의 공백은 그대로입니다. 최소화가 지우는 것은 구조의 공백뿐입니다. 이 구분이 없는 도구는 데이터를 망가뜨립니다.
서명한 JSON을 최소화하면 안 되는 이유
위의 정규화가 서명과 만나면 문제가 됩니다.
서명은 바이트에 대해 계산됩니다. {"a":1.0}과 {"a":1}은 같은 데이터지만 다른 바이트이므로 서명이 달라집니다. JSON을 파싱했다가 다시 직렬화하는 미들웨어가 중간에 있으면 서명 검증이 실패합니다.
웹훅 서명을 검증할 때 파싱된 객체가 아니라 원본 바이트를 써야 하는 이유입니다.
큰 JSON에서 값 찾기
중첩이 깊어지면 눈으로 따라가기 어렵습니다. 평탄화하면 경로가 보입니다.
order.id "A-1"
order.items[0].sku "X"
order.items[0].qty 2
order.items[0].price.amount 1200
order.items[0].price.currency "KRW"
order.items[1].sku "Y"
order.buyer.name "김"
order.buyer.address.city "서울"
order.buyer.address.zip "04524"
원본 구조에서는 price.amount를 찾으려면 괄호를 세어야 하는데, 평탄화하면 한 줄입니다. 그리고 그 경로를 그대로 코드에 옮길 수 있습니다.
data.order.items[0].price.amount
로그에서 특정 필드만 뽑거나, API 응답 두 개의 구조 차이를 볼 때 쓰는 방식입니다.
배열 인덱스는 안정적이지 않다
평탄화한 경로를 저장해 두고 나중에 다시 쓸 때 주의할 점이 있습니다. items[0]은 그 시점의 첫 번째 항목이고, 항목이 추가되거나 순서가 바뀌면 다른 값을 가리킵니다.
식별자가 있는 배열이라면 인덱스 대신 그 값으로 찾는 편이 안전합니다. JSON Pointer(RFC 6901)나 JSONPath 같은 표기도 같은 한계를 갖습니다.
확인 순서
- 전송 압축이 켜져 있는지 먼저 봅니다. 켜져 있다면 최소화의 이득은 작습니다
- 서명·해시 대상 JSON은 파싱했다 다시 쓰지 않습니다. 원본 바이트를 유지합니다
- 소수점 표기를 유지해야 하는 값(금액·좌표)은 문자열로 보냅니다
- 최소화 도구가 문자열 안의 공백을 건드리지 않는지 확인합니다
- 평탄화 경로를 저장한다면 배열 인덱스가 바뀔 수 있다는 점을 감안합니다
tools.onuel.dev의 JSON 최소화는 불필요한 공백과 줄바꿈을 지우고 몇 바이트가 줄었는지 함께 보여 줍니다. JSON 뷰어는 중첩 구조를 점 표기 경로로 펴서 필요한 값의 경로를 그대로 복사할 수 있게 합니다. 읽기 좋게 되돌릴 때는 JSON 포매터, 구조가 맞는지 볼 때는 JSON 검증기, 두 응답을 대조할 때는 JSON 비교를 씁니다.
- #JSON
- #성능
- #API