# Progressive JSON

> Clean Markdown view of GeekNews topic #21226. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=21226](https://news.hada.io/topic?id=21226)
- GeekNews Markdown: [https://news.hada.io/topic/21226.md](https://news.hada.io/topic/21226.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2025-06-02T10:01:56+09:00
- Updated: 2025-06-02T10:01:56+09:00
- Original source: [overreacted.io](https://overreacted.io/progressive-json/)
- Points: 15
- Comments: 1

## Summary

**Breadth-first 방식**의 **Progressive JSON**은 점진적 JPEG처럼, JSON 데이터도 불완전한 상태로 먼저 전송하여 클라이언트가 점차 내용 전체를 활용할 수 있는 방식입니다. 클라이언트가 데이터 전체 도착을 기다리지 않고, **Promise** 기반 플레이스홀더를 활용하여 비동기로 준비되는 청크마다 일부 정보부터 즉시 사용할 수 있게 합니다. 이렇게 하면 개발자는 서버의 느린 부분에 영향을 덜 받으며, **점진적 UI 렌더링**과 병목 현상 완화를 동시에 실현할 수 있습니다.

## Topic Body

- **점진적 JPEG**처럼, JSON 데이터도 불완전한 상태로 먼저 전송하여 클라이언트가 점차 내용 전체를 활용할 수 있는 방식 소개  
- 기존 **JSON 파싱 방식**은 전체 데이터가 완전히 수신되기 전까지 아무런 작업이 불가능한 비효율성 문제 있음  
- **Breadth-first 방식**으로 데이터를 여러 청크(부분)로 구분하여, 아직 준비되지 않은 부분은 Promise로 표시하고 준비되는 대로 점진적으로 채워, 클라이언트가 미완성 데이터도 활용 가능함  
- 이 개념은 **React Server Components(RSC)** 의 핵심 혁신이며, `&lt;Suspense&gt;`를 통해 의도된 단계별 로딩 상태를 제어함  
- 데이터 스트리밍과 의도적 UI 로딩 흐름을 분리하여 더욱 유연한 사용자 경험 제공 가능   
  
---  
  
### 점진적(Progressive) JPEG과 점진적 JSON의 아이디어  
  
- 점진적 JPEG는 이미지를 위에서 아래로 한 번에 불러오는 대신, 흐릿한 상태로 전체를 먼저 보여주고 점차 선명해지는 방식임  
- 이와 유사하게, JSON 전송에도 점진적 방식을 적용함으로써 **전체가 완성될 때까지 기다리지 않고 일부 데이터를 즉시 사용**할 수 있음  
- 예시 JSON 데이터 구조에서, 일반적 방식은 마지막 바이트까지 모두 받아야지만 파싱이 가능함  
- 이로 인해 클라이언트는 서버의 느린 부분(예: 느린 DB에서 comments 불러옴)까지 **모두 전송될 때까지 대기**해야 하며, 이는 매우 비효율적인 현재의 표준임  
  
### 스트리밍 JSON 파서의 한계  
  
- 스트리밍 JSON 파서를 도입하면 **불완전한(중간) 데이터 객체 트리**를 생성 가능  
- 하지만, 각 객체의 필드(예: footer, 여러 개의 comment 목록 등)가 일부만 전달되는 경우 **타입이 불일치하고, 완성 여부 파악이 어려워 활용도 저하** 문제 발생  
- HTML의 스트리밍 렌더링과 유사하게, **순서대로 스트림 처리 시 하나의 느린 부분이 전체 결과를 지연**시키는 문제가 동일하게 발생함  
- 일반적으로 스트리밍 JSON의 활용이 드문 이유임  
  
### Progressive JSON 구조 제안  
  
- 기존 방식의 깊이 우선 스트리밍(즉, 트리구조 하위까지 내부 순회 전송하는 방식)이 아닌, **Breadth-first(너비 우선) 방식** 도입  
- 최상위 객체만 먼저 전송하고, 하위 값들은 **Promise와 같은 플레이스홀더**로 두고 준비되는 대로 한 번에 각각의 청크로 채워나감  
- 예를 들어 서버가 비동기로 데이터 로딩이 끝날 때마다 대응하는 청크를 전송, 클라이언트는 준비된 만큼만 활용 가능  
- **비동기적 데이터 수신(이른 로드)** 이 가능해지고, 여러 느린 부분이 모두 처리가 끝날 때까지 전체 대기하지 않아도 됨  
- **클라이언트를 청크별 비순차 및 부분 순차 수신에 강하게 구성**하면, 서버는 다양한 청크 분할 전략을 유연하게 적용 가능  
  
### Inlining과 Outlining: 효율적 데이터 전송  
  
- 점진적 JSON 스트리밍 포맷은 재사용 객체(예: 동일 userInfo를 여러 군데에서 참조)도 중복 저장 없이 **한 개의 청크로 따로 추출하여 각 위치에서 동일 참조 가능**  
- 느린 부분만 분리해 placeholder로 전송하고, 나머지는 바로 채워 효율적 데이터 스트림 구현  
- 동일한 객체가 여러 번 등장하는 경우, **한 번만 전송하고 재활용(Outlining) 가능**  
- 이러한 방식으로 **순환 참조(객체가 자기 자신을 참조하는 구조)** 도 일반 JSON처럼 곤란하지 않고 청크 내 간접 참조 구조로 자연스럽게 직렬화 가능  
  
### React Server Components(RSC)의 점진적 스트리밍 구현  
  
- 실제 React Server Components는 **점진적 JSON 스트리밍** 모델을 적용한 대표적 예시임  
  - 서버가 외부 데이터(예: Post, Comments)를 비동기로 불러오는 구조를 사용  
  - 클라이언트에선 **아직 도착하지 않은 부분을 Promise로 두고,** 준비되는 순서대로 점진적 UI 렌더링  
- **React의 `&lt;Suspense&gt;`로 의도적인 로딩 상태를 설정**  
  - **사용자 경험상 불필요한 화면 점프 방지**를 위해, Promise 상태(구멍)를 바로 보여주지 않고, `&lt;Suspense&gt;` fallback으로 단계별 로딩 연출 가능  
  - 데이터가 빠르게 도착해도 실제 UI는 설계된 단계에 맞춰 점진적으로 노출되게 개발자가 제어 가능   
  
### 요약 및 시사점  
  
- React Server Components의 핵심 혁신은 **컴포넌트 트리 속성(props)을 외곽부터 점진적으로 스트리밍**하는 방식에 있음  
- 따라서, 서버가 완전히 모든 데이터를 준비할 때까지 기다릴 필요 없이, 주요 부분부터 점차 보여주며 로딩 대기 상태도 세밀하게 제어 가능  
- **스트리밍 자체뿐 아니라, 이를 활용하는 프로그래밍 모델(React의 `&lt;Suspense&gt;`) 같은 구조적 지원이 함께 필요**함  
- 이를 통해, 느린 데이터 한 부분이 전체를 지연시키는 문제 등 기존 전송 방식의 병목 현상 완화 가능

## Comments



### Comment 39634

- Author: neo
- Created: 2025-06-02T10:01:56+09:00
- Points: 1

###### [Hacker News 의견](https://news.ycombinator.com/item?id=44147945) 
- 일부 사람들이 이 글을 너무 글자 그대로 받아들이는 경향이 보이는데, Dan Abramov가 Progressive JSON이라는 새로운 포맷을 제안하고 있는 건 아니라는 설명
  - 이 글은 React Server Components의 아이디어와, 컴포넌트 트리를 자바스크립트 객체 형태로 표현한 뒤 이를 스트림 형태로 전송하는 과정을 설명하는 내용
  - 이 방식은 React 컴포넌트 트리에 ‘구멍’을 둘 수 있게 해서, 처음에는 로딩 상태나 스켈레톤 UI를 보여주고, 서버에서 실제 데이터를 받을 때 해당 부분을 완전히 렌더링하는 구조 가능
  - 이렇게 하면 더 세밀한 단계에서 로딩 표시와 빠른 첫 화면 표시가 가능
- 내가 생각하기에 사람들이 이 아이디어를 확장해서 다른 방법에 적용하는 것도 괜찮다고 봄
  - RSC의 데이터를 직렬화하는 방식을 React에만 국한하지 않고, 더 일반적인 패턴으로 설명하려고 했던 의도
  - React Server Components에서 발견한 여러 아이디어가 다른 기술이나 생태계에도 녹아들었으면 하는 바람
- 나는 progressive loading 방식, 특히 콘텐츠가 계속 움직이는(점프하는) 경험이 썩 마음에 들지 않음
  - 로딩 중에 텅 빈 상태 UI를 보여주는 패턴이 특히 신경 쓰임
- 얼마 전까지 Ember를 썼을 때도 비슷한 방식이 있었고, Ajax 엔드포인트 작성이 매우 고통스러웠던 기억
  - 트리 구조 재배치로 일부 자식 요소가 파일 끝에 위치하도록 해서 DAG(비순환 그래프) 처리를 효율적으로 하려던 의도였던 듯
  - SAX 스타일의 스트리밍 파서를 쓰면 데이터가 부분적으로 도착할 때 페인팅을 먼저 시작할 수 있음
  - 하지만 단일 스레드 VM에서 작업 순서를 잘못 설계하면 오히려 문제만 커지는 위험 존재
- 나는 이미 AI 툴과의 연결에서 스트리밍 partial JSON(Progressive JSON) 방식을 실제로 사용 중
  - 이 방식은 RSC뿐 아니라 다양한 곳에서 활용되고 실무적으로도 클라이언트, 서버 모두에 가치 있는 방법이라는 실제 경험
- Dan의 "2 computers" 발표와 최근 RSC 관련 포스팅을 다 챙겨봤음
  - Dan은 React 생태계에서 최고의 설명가이지만, 기술을 이렇게까지 어렵게 설명해야 한다면
    1. 진짜로 필요 없는 기술이거나
    2. 추상화에 문제가 있는 것
  - 대다수 프론트엔드 개발자가 여전히 RSC 개념을 완전히 이해하지 못함
  - Vercel은 Next.js를 기본 React 프레임워크로 만들었고, RSC 채택도 이 바람을 타고 확산
  - Next.js를 쓰는 사람조차 Server Component 경계를 명확히 이해하지 못하고 일종의 ‘카고 컬트’식 채택이 많음
  - React가 Vite 관련 PR을 받아들이지 않은 것도 의심스러움. RSC 푸시는 실질적으로 유저나 디벨로퍼를 위한 것이 아니라 플랫폼 업체의 호스팅 플랫폼 판매 전략일지도 모른다는 생각
  - 되짚어 보면, Vercel이 React 오리지널 팀을 대거 영입한 것도 React 미래를 주도하려는 의도처럼 보임
  - 역사적 동기나 배경에 대한 판단이 틀렸다는 지적과 함께, Vite 지원 관련 현황에 대해서 설명
  - Vite 통합은 DEV 환경에서 번들링이 필요하다는 기술적인 제약 때문에 현재 Vite 팀이 개선 진행 중이라는 언급
    - 관련 작업 진행 상황: [https://github.com/facebook/react/pull/33152](https://github.com/facebook/react/pull/33152)
  - 사람들이 RSC를 이해하지 못한다는 논지는 논리적으로 순환적인 주장이라는 견해
  - RSC를 싫어할 수도 있지만, 그 안에는 다른 기술에 차용할 만한 흥미로운 아이디어도 충분히 있음
  - 설득보다는, 신기하고 쓸모 있는 부분을 각자가 취해갔으면 하는 바람
- 물론 여전히 SPA를 정적 사이트로 만들어서 CDN에 올릴 수도 있고, Next.js도 “다이나믹” 모드로 셀프 호스팅이 가능
  - 다만 Next.js의 서버리스 렌더링 전체 기능을 완전하게 Vercel 말고 다른 곳에 구현하기가 어려운 현실 존재(undocumented “magic” 때문)
  - 이 문제 역시 다중 플랫폼에서 일관된 API 제공을 위해 어댑터 도입을 공식적으로 제안한 상황: [https://github.com/vercel/next.js/discussions/77740](https://github.com/vercel/next.js/discussions/77740)
  - 나는 RSC 푸시가 기업 이익 때문이라기보다는, 기존 웹사이트 빌드 패턴(SSR + 클라이언트 약간의 progressive enhancement)이 실제로 많은 장점이 있다는 깨달음에서 비롯됐다는 입장
  - SSR만으로도 비즈니스 로직이 불필요하게 클라이언트로 많이 옮겨가는 문제 존재
- RSC 자체는 흥미로운 기술이지만 실전에서는 그다지 합리적이지 않다는 생각
  - 복잡한 컴포넌트를 렌더링하기 위해 Node/Bun 백엔드 서버를 대규모로 유지하는 부담 존재
  - 차라리 정적 페이지나 React SPA + Go API 서버 조합이 훨씬 효율적
  - 비슷한 결과를 훨씬 작은 리소스로 만들 수 있음
- 새로운 기술의 설명이 복잡하다고 꼭 필요 없는 기술이나 잘못된 추상화라고 단정하기 어렵고, 복잡성을 감수할 만한 가치가 있는 문제도 존재
  - 앞으로 어떻게 이 기술이 진화할지 지켜보는 관점
- RSC의 코드 구조를 활용해 작은 조각으로 HTML/CSS/JS를 나누는 정적 페이지 빌드도 가능하다는 생각
  - 글에서 제안한 ‘$1’ 플레이스홀더를 URI로 치환해도 서버가 필요 없을 수 있음(대부분 동적 SSR이 반드시 필요한 건 아님)
  - 단점은, 이런 방식은 콘텐츠가 변경될 때 업데이트 파이프라인(특히 S3에 컴파일된 스태틱 사이트의 스트리밍 배포)에 속도 확보가 중요
  - 예를 들어 많은 기사가 프리랜더링되어 있는 신문 사이트처럼, 콘텐츠 일부만 바뀔 때 효율적으로 해당 부분만 다시 빌드하는 등 스마트한 콘텐츠 diff 처리 필요
- 실무에서 성능 최적화라며 밀리세컨드 단위로 프론트엔드에서 여러 MB 데이터를 불러오고 복잡한 로직을 처리하는 반면, 실제로는 BFF나 아키텍처 개선, 더 Lean한 API 구축이 훨씬 생산적인 솔루션이라는 깨달음
  - GraphQL, http2 등을 통한 시도가 있었으나 결국 본질적인 문제 해결은 아니었고, 웹 표준의 진화 없이는 패러다임 변화 없을 것이라는 의견
  - 새로운 프레임워크 역시 이 한계는 동일
- RSC는 글 말미에서 설명되는 것처럼 본질적으로 BFF(Backend for Frontend) 역할이라는 설명
  - API 로직을 컴포넌트화한 구조
  - 내 긴 글 참고: [https://overreacted.io/jsx-over-the-wire/](https://overreacted.io/jsx-over-the-wire/) (BFF는 첫 섹션 중간 참고)
- "페이지 로딩 ms 단축"의 의미에 따라 다르다는 의견
  - Time to first render, time to visually complete를 최적화한다면, 비어있는 skeleton UI를 먼저 보내고 API로 데이터 받아서 hydrate하는 방식이 체감상 가장 빠름
  - 반대로 time to first input, time to interactive을 빠르게 하려면 유저 데이터를 바로 렌더링해 줄 수 있어야 하고, 이 경우 백엔드가 훨씬 유리(네트워크 호출 최소화)
  - 대부분의 경우 유저는 이쪽을 더 선호하고, CRUD SaaS 앱은 서버 측 렌더링이, Figma처럼 디자인이 중요한 앱은 클라이언트에서 정적 데이터+추가 데이터 fetch가 적합
  - "모든 문제에 통하는 한 가지 솔루션"이란 없고, 최적화 지점은 주관적 선택
  - 개발 경험, 팀 구조 등 기술 선택에 영향을 주는 다양한 요소 존재
- 덕분에 내가 왜 Facebook 로딩할 때 핵심 콘텐츠가 늘 마지막에 렌더되는지 이해하게 됨
- 여기서 말하는 BFF가 뭔지 궁금하다는 질문 등장
- 약어가 너무 많아서 FE와 BFF가 뭔지 궁금해하는 반응
- Progressive JSON 아이디어를 직접 쓰고 싶진 않고, 대안이 여러 개 존재한다고 생각
  - 가장 간단한 해결책은 거대한 하나의 JSON 객체를 여러 개로 분할, 즉 ‘JSON lines’로 전송
  - 헤더 정보는 한 번, 거대한 배열은 한 줄씩 전송해서 스트림 처리 효율화
  - 객체가 더 크면 이 방식을 재귀적으로 적용할 수도 있지만, 너무 복잡해질 수 있음
  - 속성의 순서를 서버가 명시적으로 보장해서 progressive parsing과 분리도 가능
  - 결국 정말 대용량 구조에서는 유용하지 않겠지만, 가장 흔한 대규모 JSON 다루는 상황에선 꽤 실용적인 도구
- 명시적으로 구멍(holes)을 표시하지 않아도 스트리밍 메시지를 순차로 전송하며 델타(diff)만 보내는 방식이 가능
  - ‘Mendoza’라는 델타 포맷을 사용하면 Go, JS/Typescript에서 아주 컴팩트하게 패치(diffs) 전송 가능: [https://github.com/sanity-io/mendoza](https://github.com/sanity-io/mendoza), [https://github.com/sanity-io/mendoza-js](https://github.com/sanity-io/mendoza-js)
  - zstd의 바이너리 디프 방식이나 Mendoza처럼, 직렬화 데이터의 일부만 메모리에 저장하여 효율적 패치 진행
  - React 역시 차이점 비교나 변동사항만 주입하는 방식이 필요하기에 의미 있는 접근
- UI 데이터 스트리밍에서 비어 있는 배열이나 null만으로는 부족하고, 현재 어떤 데이터가 미도착(pending) 상태인지 별도 정보가 필요
  - GraphQL 스트리밍 페이로드는 유효한 데이터 스키마와 미도착 정보, 그리고 이후 패치 처리라는 혼합 방식을 선택
- 어느 부분이 ‘구멍’인지 알아야 로딩 상태를 보여주기 쉬움
- 클라이언트에서 JSON을 progressively decode하려면 jsonriver라는 라이브러리를 소개: [https://github.com/rictic/jsonriver](https://github.com/rictic/jsonriver)
  - 매우 간단한 API, 성능도 좋고 테스트도 충분함
  - 스트리밍된 문자열 조각을 점점 더 완성도 높은 값으로 파싱해줌
  - 최종 결과는 JSON.parse와 동일함을 보장
- 트리 데이터라면 어떤 구조에서도 적용 가능한 재밌는 방식이라는 의견
  - 트리 데이터는 parent, type, data 벡터와 string table로 표현하면, 나머지는 모두 소수의 정수로 축소 가능
  - string table과 type info를 헤더로 upfront 전송하고, parent, data 벡터 청크를 노드 단위로 스트리밍
  - depth-first/breadth-first 스트리밍은 청크 순서만 바꿔도 충분
  - 네트워크 상의 앱에서 로드 시간 UX를 많이 개선할 수 있을 것 같음
  - 테이블과 노드 청크를 번갈아 전송하며 트리를 어떤 순서로든 웹에 시각화할 수 있음
  - preorder traversal과 depth 정보만 있으면 node id도 없이 트리 구조 복원 가능
  - 이 아이디어로 작은 라이브러리 만드는 것도 가치 있는 시도
- 대부분의 앱은 이런 ‘정교한’ 로딩 시스템이 필요 없고, 정말 간단히 여러 번 API 호출로 대체 가능한 경우가 대부분이라는 주장
  - 본인이 RSC wire 프로토콜 작동 방식을 설명하려던 것뿐이며, 실제로 이런 걸 직접 구현하라고 권하는 의도는 아니라는 반론
  - 다양한 툴 간 원리 파악은 결국 다양한 곳에서 아이디어를 차용하거나 리믹스하는 데 도움
  - 여러 번 호출하는 전략이 n*n+1문제가 있다고 생각하지만, 객체를 OOP/ORM 스타일로 중첩해서 전송하는 대신, 주석의 경우처럼 평평하게 전송하는 방식도 가능
  - 이럴 바엔 차라리 protobuf 등 타입이 명확한 엔드포인트 구성도 장점
  - comments를 분리하면 2번 호출로 충분히 처리가능(페이지+글, 댓글 따로), 이렇게 하면 pre-render 최적화도 가능
  - 옵션의 실제 구현 복잡도 자체를 너무 높이지 않고, 미리 정의된 좋은 툴이 있다면 일부러 딥 커스터마이즈할 필요성 적음
  - 과도하게 복잡한 기능이 결국 사용자나 개발자에게 독이 될 수 있음을 인지해야 한다는 입장
  - 640K도 충분하다는 얘기처럼, progressive/partial reads가 확실히 WASM 시대 UX 속도에 실질적으로 큰 역할을 할 수 있다고 생각
  - protobuf 같은 바이너리 인코딩 방식에 partial read와 well-defined streaming이 붙으면 엔지니어 부담은 늘지만 결과 UX는 크게 성장할 가능성
- Progressive JPEG는 미디어 파일 특성상 필요하지만, Text/HTML에선 굳이 필요 없고, JS 번들이 커진 결과로 복잡성만 더해지는 자기모순적 상황이라는 견해
  - 실제 느린 원인이 단순히 데이터의 ‘크기’ 때문만이 아니라는 점을 지적
  - 서버 데이터 쿼리 자체에 시간이 오래 걸리거나, 네트워크가 느릴 때도 점진적 노출(progressive reveal)이 의미 있음
  - 전체 데이터 완성까지 대기하는 대신, 적당한 타이밍에 로딩 UI 보여주는 의도적 단계별 렌더링이 실제로 사용자 경험에 이득
- 엔드포인트 분리 전략이 이미 다양한 장점(Head of line blocking 방지, 필터 옵션 향상, 라이브 업데이트, 독립적인 성능 개선 등) 갖춘 솔루션이라는 생각
  - 애플리케이션을 문서 플랫폼(document platform)으로 다루려는 시도가 근본적인 문제라고 보는 입장
  - 실제 애플리케이션은 ‘문서’처럼 작동하지 않고, 이 괴리를 해소하려 많은 부가 코드와 인프라가 필요해지는 상황
  - 별도 엔드포인트 채택의 진짜 단점과 진화 방향에 대한 두 편의 긴 글로 보완 설명: [https://overreacted.io/one-roundtrip-per-navigation/](https://overreacted.io/one-roundtrip-per-navigation/), [https://overreacted.io/jsx-over-the-wire/](https://overreacted.io/jsx-over-the-wire/)
  - 요약하면, 엔드포인트는 결국 서버/클라이언트 간 '공식' API 계약이되고, 점점 코드가 모듈화되면서 성능에 손해(워터폴 현상 등) 발생 가능
  - 결정들을 서버에서 한 번에 처리(coalescing)가 성능과 구조적 유연성에서 더 나은 대안이 될 수 있음
