React SSR 프레임워크 비교: TanStack Start, React Router, Next.js를 고부하 환경에서 비교하기
TMThttps://blog.platformatic.dev/react-ssr-framework-benchmark-tanstack-start-react-router-nextjs
성능 벤치마크는 끝판왕 심사가 아니라, 특정 시점의 스냅샷일 뿐입니다. 결과는 특정 워크로드, 규모, 제약에 따라 달라지며, 프레임워크의 ‘가치’를 순위로 매기는 게 아닙니다. Next.js는 광범위한 도입 사례, 강력한 호환성, 수백만 명이 신뢰하는 거대한 생태계 덕분에 두드러집니다. TanStack은 신생 프로젝트로서 과감한 아키텍처 선택을 했습니다. React Router는 성숙도 곡선에서 또 다른 위치를 차지합니다. 각 프레임워크는 각자의 맥락에서 승자입니다.
중요한 건 숫자 자체가 아니라 그 숫자에 대한 ‘대응’입니다. 모든 팀이 우리가 공유한 데이터를 바탕으로 실제 수정 사항을 내놓았습니다. 공개된 데이터, 공유한 플레임그래프, 상류(upstream) 레벨의 수정 덕분에 Node.js는 엔터프라이즈 팀에게 장기적으로 안전한 선택지가 됩니다.
TL;DR
Claude Code의 도움을 받아, 동일한 eCommerce 앱을 세 가지 SSR 프레임워크로 구현한 뒤 AWS EKS에서 초당 1,000 요청 환경에서 테스트했습니다. 각 프레임워크는 Watt 위에서, 그리고 쿠버네티스에 직접 올린 방식 둘 다로 실행했습니다.
그 결과, 큰 성능 차이가 드러났고 몇 가지 핵심적인 포인트가 드러났습니다:
- Node 서비스들을 Watt 위에서 돌리면 평균 레이턴시가 개선됩니다.
- TanStack 팀은 훌륭한 작업을 해왔습니다. 우리가 테스트한 프레임워크 중 TanStack Start가 가장 큰 차이로 다른 프레임워크들을 앞질렀습니다.
- Next.js 팀은 눈에 띄는 성능 개선을 이뤄냈습니다. v15에서 v16 canary로 업그레이드하자 처리량(throughput)이 두 배 이상 늘고, 레이턴시는 6배 가까이 감소했습니다. 이 협업 과정에서 React의 RSC 역직렬화(deserialization) 속도를 75% 향상시키는 수정도 이루어졌고, 이는 React를 사용하는 모든 이들에게 이득입니다.
TanStack과 Next.js 팀은 모두 platformatic/flame을 사용해 벤치마크에서 드러난 핵심 성능 병목을 찾아내고 해결했습니다. 아래에서 더 자세히 다룹니다.
TanStack Start는 React Router보다 처리량이 25% 높고, 레이턴시는 35% 더 낮게 나왔습니다. 두 프레임워크 모두 100% 성공률을 기록했는데, 이는 모든 요청이 10초 타임아웃 내에 HTTP 200 응답을 받았다는 의미입니다. 이런 엄격한 정의 덕분에 비교가 공정해지고, 실제 SLA 기대치에도 부합합니다. Next.js는 우리의 벤치마크 부하에서 애를 먹었지만, v15.5.5에서 v16.2.0-canary.66으로 올리자 처리량이 322 → 701 rps로 두 배 이상 증가했고 평균 레이턴시는 6배 감소했습니다.
일반적인 엔터프라이즈 eCommerce 시나리오를 최대한 반영하기 위해, 이번 테스트에서는 캐싱을 전혀 사용하지 않았습니다. 실제로 이 영역에서는 공격적인 개인화(personalization)와 A/B 테스트 때문에 캐시가 자주 배제됩니다. 대규모 e-commerce 환경에서는 개별 사용자 뷰의 중복도가 매우 낮고(종종 5% 미만), 캐시 적중률이 낮은 반면, 무효화(invalidation) 오버헤드는 상당하기 때문에, 캐시로 얻는 이득이 크지 않은 경우가 많습니다. 이런 명시적인 트레이드오프 때문에, 많은 회사들이 캐싱에서 얻을 수 있는 잠재적인 이득보다, 동적인 사용자 경험을 우선시하는 선택을 합니다.
협업 메모: 우리는 TanStack과 Next.js 팀에 벤치마크 데이터와 플레임그래프를 (platformatic/flame으로 생성) 공유했습니다. TanStack 팀은 이 데이터를 바탕으로 치명적인 병목을 발견해 252배 빠른 응답 시간을 만들어냈습니다. Next.js 팀의 Tim Neutkens는 우리의 플레임그래프를 통해 React Server Components에서 JSON.parse reviver가 일으키는 오버헤드를 찾아냈고, 그 결과 React 자체에 RSC 역직렬화 속도를 75% 개선하는 패치가 머지되었습니다.
이번 벤치마크는 Next.js 의 카나리 릴리스를 기준으로 수행했지만, Next.js 16.2.0이 아주 가까운 시일 내에 출시되면서 이 이점을 그대로 가져가게 됩니다.
공정한 ‘동급 비교’를 위한 도전과제
SSR 성능(혹은 성능 전반)을 프레임워크 간에 비교하는 건 악명 높게 까다로운 작업입니다. 대부분의 팀은 자신들이 선택한 단일 프레임워크에만 앱을 작성하고 배포하기 때문에, ‘진짜로 비슷한 수준의 앱’끼리 공정하게 비교할 기회가 거의 없기 때문입니다.
다행히도 지금은 코드를 작성하는 비용이, LLM에게 몇 토큰을 던져줄 정도로 저렴한 시대입니다. 그래서 우리는 우리의 좋은 친구 Claudio의 도움을 받아, 사실상 동일한 eCommerce 샘플 애플리케이션을 세 개의 프레임워크로 만들어보았습니다(코드는 여기에서 직접 확인할 수 있습니다).
카드마켓(CardMarket)란 어떤 앱인가?
이번 벤치마크를 위해 우리는 트레이딩 카드 마켓플레이스 앱을 만들었습니다. TCGPlayer나 CardMarket를 좀 더 단순화한 버전이라고 생각하면 됩니다. 데이터 모델은 다음을 포함합니다: 5개의 게임(포켓몬, 매직 더 개더링, 유희왕, 디지몬, 원피스), 50개의 카드 세트(게임당 10개), 10,000장의 카드(세트당 200장), 평점과 위치 정보가 있는 100명의 판매자, 그리고 가격·상태·수량 정보가 포함된 50,000개의 리스팅.
앱에는 현실적인 e-commerce 경험을 만들기 위해 여러 종류의 페이지와 라우트가 포함되어 있고, 모두 Claude Code로 생성했습니다:
- 홈페이지: 추천 게임, 트렌딩 카드, 신규 출시 항목을 보여줍니다.
- 검색 페이지: 전문 검색, 필터링, 페이지네이션을 제공합니다.
- 게임 상세 페이지: 각 게임에 대한 정보와 해당 게임의 세트들을 보여주고, 세트 상세 페이지에서는 카드 목록을 페이지네이션과 함께 보여줍니다.
- 카드 상세 페이지: 카드 정보와 판매자 리스팅을 나타냅니다.
- 판매자 목록 페이지: 모든 판매자와 그들의 평점을 보여주며, 각 판매자는 프로필·리스팅 페이지를 가지고 있습니다.
- 장바구니 페이지: 정적인 쇼핑 카트를 제공합니다.
프레임워크별 구현을 최대한 일관되게 유지하기 위해 다음과 같은 설계 선택을 했습니다:
- 모든 데이터는 JSON 파일에서 가져오며, 모든 프레임워크가 동일한 데이터를 사용합니다.
- 실제 데이터베이스 지연 시간을 흉내 내기 위해 각 요청에 1~5ms의 랜덤 지연을 추가했습니다.
- 모든 라우트는 클라이언트 측 데이터 패칭 없이, 전체 SSR을 사용합니다.
- 모든 버전이 동일한 UI 컴포넌트, 레이아웃, Tailwind CSS 스타일링을 공유합니다.
프레임워크
이 애플리케이션을 다음 세 가지 프레임워크로 구현했습니다:
- TanStack Start (v1.157.16) – TanStack Router를 기반으로 Vite for SSR에 사용하는 가장 신생 프레임워크
- React Router (v7) – 전통적인 라우팅 라이브러리에서 출발했지만, 이제는 SSR을 1급 기능으로 지원합니다.
- Next.js (v15, 이후 v16 canary로 업데이트) – React SSR의 사실상 표준 리더
각 구현은 프레임워크가 권장하는 ‘정석적인 패턴’을 따릅니다:
- TanStack Start: loader 함수를 사용하는 createFileRoute
- React Router: loader export를 가진 Route 모듈
- Next.js: 서버 컴포넌트를 활용한 App Router
런타임
각 프레임워크에 대해 다음 두 가지 런타임 구성을 테스트했습니다:
- Node.js – 단일 스레드, 1 CPU를 할당한 6개의 파드
- Watt – SO_REUSEPORT를 사용하는 멀티 워커 구조, 파드당 2 CPU를 할당한 3개의 파드, 각 파드에는 이 6개의 CPU를 최대한 활용하기 위한 워커 2개
모든 구성은 총 6코어라는 동일한 CPU 리소스를 배정받아, 공정한 비교가 가능하도록 했습니다.
테스트 방법
인프라
- EKS 클러스터: m5.2xlarge 인스턴스 4개(각 8 vCPU, 32GB RAM)
- 부하 테스트 인스턴스: c7gn.2xlarge (8 vCPU, 16GB RAM, 네트워크 최적화)
- 리전: us-west-2
- 부하 테스트 도구: Grafana k6
소프트웨어 버전
모든 버전은 재현 가능한 벤치마크를 위해 package.json에 고정되어 있습니다.
부하 테스트 설정
각 테스트는 다음 프로토콜을 따랐습니다:
- NLB 워밍업: 초당 10 → 500 요청까지 60초 동안 램프업
- 사전 워밍업: 중간 수준 부하로 20초
- 쿨다운: 메인 테스트 전 60초
- 메인 테스트: 60초 동안 초당 1,000 요청까지 램프업 후, 120초 동안 지속 부하
- 테스트 간 쿨다운: 480초
현실적인 트래픽 분포
부하 테스트는 실제 e-commerce 트래픽 패턴을 시뮬레이션했습니다.
결과
TanStack Start: 성능 1티어 리더
업데이트 이후(v1.157.16 기준)
TanStack Start는 우리가 테스트한 프레임워크 중 가장 높은 처리량과 가장 낮은 레이턴시를 보여줬습니다. Watt 위에서 초당 1,000 요청 상황에서도 평균 응답 시간이 13ms 아래에 머물렀습니다.
React Router: 탄탄하고 믿을 만한 선택지
React Router는 해당 부하를 안정적으로 처리했고, 실패 요청은 0건이었습니다. Watt를 사용하자, 순수 Node.js 단독 실행에 비해 응답 시간이 38% 더 빨라졌습니다.
Next.js: 고부하에선 힘들지만, 계속 나아지는 중
초기 벤치마크(Next.js 15.5.5, Watt 3.32.0 기준)
Next.js는 초당 1,000 요청을 충분히 처리하지 못했습니다. 평균 응답 시간은 8~11초 수준이었고, 약 40%의 요청이 실패했습니다. Watt의 최적화를 적용했음에도, Next.js는 더 가벼운 프레임워크들에 비해 뒤처졌습니다.
업데이트된 벤치마크 (Next.js 16.2.0-canary.66, Watt 3.39.0)
이후 우리는 Next.js 최신 카나리 버전과 Watt 3.39.0으로 업그레이드한 뒤, 상황이 나아졌는지 확인하기 위해 다시 벤치마크를 수행했습니다:
Next.js 버전 업그레이드 효과(Watt 런타임 기준)
Next.js 15.5.5에서 16.2.0-canary.66으로, Watt 3.39.0을 함께 적용하자 다음과 같은 큰 개선이 있었습니다:
- 처리량이 두 배 이상 증가
- 평균 응답 시간이 6배 이상 감소
- 레이턴시가 83% 감소
성공률은 약간만 개선되어(여전히 약 36% 요청이 실패) 아쉬웠지만, 성공한 요청들의 속도는 눈에 띄게 빨라졌습니다. 성공 요청의 중앙값(median) 응답 시간이 수 초 단위에서 431ms로 떨어졌습니다.
이는 분명한 진전입니다. 여전히 이 부하 수준에서는 Next.js가 세 프레임워크 중 가장 느리지만, 격차는 줄어들고 있으며, 추가적인 개선도 예정되어 있습니다.
프레임워크 협업: 벤치마크가 불씨가 된 변화
이번 프로젝트에서 가장 좋았던 점 중 하나는 프레임워크 팀과 직접 협업할 수 있었다는 사실입니다. 실제 벤치마크 데이터, 특히 시간이 어디에서 소비되는지 보여주는 플레임그래프를 공유한 덕분에, 추상적인 ‘성능 논의’가 실제 수정 사항으로 이어질 수 있었습니다. (웹 성능 팀에 계시다면, 연락을 주시면 정말 기쁩니다.)
Next.js 팀과의 협업: RSC 역직렬화 병목 해결기
초기 Next.js 벤치마크에서 응답 시간이 수 초에 이르는 결과를 얻은 뒤, 우리는 부하 테스트에서 수집한 플레임그래프를 Next.js 팀의 Tim Neutkens에게 공유했습니다. 플레임그래프는 initializeModelChunk라는 함수가 명확한 핫스팟이라는 점을 보여줬습니다. 이 함수는 React Server Components(RSC)의 청크 역직렬화 과정에서 reviver 콜백을 동반한 JSON.parse를 호출합니다.
근본 원인은 잘 알려진 V8의 성능 특성이었습니다. JSON.parse는 C++로 구현되어 있는데, reviver 콜백을 넘기면 파싱되는 모든 key-value 쌍마다 C++ → JavaScript 경계를 넘나드는 호출이 발생합니다. 아무 일도 하지 않는 사소한 reviver (k, v) => v를 써도, reviver 없는 순수 JSON.parse에 비해 약 4배 정도 느려집니다. initializeModelChunk는 SSR 동안 모든 RSC 청크마다 호출되기 때문에, 서버 컴포넌트가 많은 페이지에서는 이 오버헤드가 순식간에 누적됩니다.
Tim은 이 문제를 파악하고 React에 직접 수정 PR을 제출했습니다: facebook/react#35776 (2026년 2월 19일 머지). 이 변경에서는 reviver 콜백을 사용하는 대신, 순수 JSON.parse() 호출 뒤에 재귀적인 트리 순회를 순수 JavaScript로 수행하는 2단계 접근으로 바꾸었고, 그 결과 RSC 청크 역직렬화에서 약 75%의 속도 향상을 얻었습니다:
이 수정은 Next.js뿐 아니라, Server Components를 사용하는 모든 React 프레임워크에 도움이 됩니다. 실제 워크로드 기반의 프로파일링이 마이크로벤치마크로는 포착하기 어려운 최적화 기회를 어떻게 드러낼 수 있는지 보여주는 사례이기도 합니다.
이 개선은 이미 우리가 재실행한 Next.js 벤치마크(v16.2.0-canary.66)에 반영되어 있으며, 이 최적화와 기타 변경 사항이 안정 버전에 반영되면 추가적인 이득이 있을 것으로 기대하고 있습니다.
TanStack의 반전 드라마: 초고속 최적화 사례
흥미롭게도 TanStack 팀과도 비슷한 여정을 겪었습니다. 초기 벤치마크는 TanStack Start v1.150.0을 기준으로 했는데, 결과가 꽤 심각했습니다. 요청 타임아웃, 75% 수준의 성공률, 3초를 넘기는 평균 응답 시간 등이 관측됐습니다. 우리는 이 결과를 TanStack 팀에 공유했고, 그들은 SSR 요청 처리 파이프라인의 치명적인 병목을(역시 @platformatic/flame을 통해) 빠르게 찾아냈습니다.
이후 7개의 마이너 버전을 거치는 동안, 그들은 해당 병목을 해결하는 패치를 릴리스했습니다. 그리고 v1.157.16으로 다시 벤치마크를 실행해보니, 결과는 말 그대로 극적으로 변했습니다:
v1.150에서의 수치는 프레임워크가 사실상 ‘버거운 상태’에 있다는 사실을 그대로 보여줍니다. p(95) 레이턴시가 정확히 10,001ms에 걸치는 데는 이유가 있었는데, 요청들이 우리의 10초 타임아웃 한계에 그대로 부딪히고 있었기 때문입니다. 네 요청 중 하나는 아예 실패했습니다.
초당 1,000 요청에서 이 프레임워크는 완전히 물에 잠겨 있었습니다.
하지만 수정 이후 TanStack Start는 우리 벤치마크에서 가장 빠른 프레임워크가 되었습니다. 응답 시간은 수 초에서 밀리초 단위로 떨어졌고, 타임아웃 ‘절벽’도 사라졌으며, 모든 요청이 성공했습니다.
이 개선이 특히 돋보이는 이유는, 이게 **런타임에 독립적(runtime-agnostic)**이었다는 점입니다. Watt와 Node.js 모두 거의 동일한 향상을 경험했습니다. Watt에서는 평균 응답 시간이 3,228ms에서 12.79ms로, Node.js에서는 3,171ms에서 13.73ms로 줄었습니다. 이는 병목이 순전히 프레임워크 코드에 있었고, 이 수정이 배포 전략에 상관없이 모든 사용자에게 동일하게 이득이 된다는 점을 보여줍니다.
런타임 비교: Watt vs Node.js
Watt의 SO_REUSEPORT 이점
Watt는 Linux 커널의 SO_REUSEPORT를 사용해 워커들이 직접 커넥션을 수신할 수 있게 합니다:
- 커널이 커넥션을 적합한 워커에게 분배합니다.
- 워커가 요청을 처리합니다.
마스터 프로세스의 조정이나 IPC 오버헤드가 없습니다. 커널이 효율적으로 부하 분산을 처리하는 구조입니다.
Watt가 특히 빛나는 순간은 언제인가?
프레임워크 순위
Watt 런타임 기준
Node.js 런타임 기준
이 벤치마크를 직접 다시 돌려보려면
전체 벤치마크 인프라는 다음에서 확인할 수 있습니다:
https://github.com/platformatic/k8s-watt-performance-demo/tree/ecommerce
벤치마크를 실행하려면:
# Benchmark TanStack Start
AWS_PROFILE=<profile-name> FRAMEWORK=tanstack ./benchmark.sh
# Benchmark React Router
AWS_PROFILE=<profile-name> FRAMEWORK=react-router ./benchmark.sh
# Benchmark Next.js
AWS_PROFILE=<profile-name> FRAMEWORK=next ./benchmark.sh
# Benchmark all frameworks
AWS_PROFILE=<profile-name> ./benchmark-all.sh이 스크립트는 일시적인(ephemeral) EKS 클러스터를 생성하고, 세 가지 런타임 구성(Node, PM2, Watt)을 모두 배포한 뒤, 부하 테스트를 실행하고, 마지막으로 인프라를 자동으로 정리(teardown)합니다. PM2 결과는 이전에 보고된 결과와 유사하기 때문에 블로그 포스트에서는 생략했습니다(자세한 내용은 93% Faster Next.js in (your) Kubernetes를 참고하세요).
핵심 요약
- Watt는 어떤 프레임워크를 쓰든 꾸준히 성능을 올려주는 안전한 선택지
Watt는 모든 프레임워크에서 순수 Node.js 단독 실행에 비해 성능을 개선했습니다. TanStack에서는 7%에서, React Router에서는 38%까지 다양한 수준의 이득을 보여줬습니다. 모든 경우에 도움이 되는, 리스크가 비교적 낮은 최적화 옵션입니다. - TanStack Start는 이제 실서비스에 써도 될 정도로 충분히 성숙한 상태
가장 신생 프레임워크임에도, TanStack Start는 최고의 성능을 보여줬습니다. 252배 성능 향상을 7개 버전 안에 이뤄낸 빠른 대응은, 팀이 개발과 최적화에 매우 집중하고 있다는 것을 보여줍니다. - 의존성 업데이트는 성능 최적화의 핵심 루틴
TanStack과 Next.js 모두의 결과는 의존성을 최신 상태로 유지하는 것이 얼마나 중요한지 보여줍니다. TanStack은 7개 버전 동안 성공률을 75%에서 100%로 끌어올렸고, Next.js는 v15에서 v16 canary로 넘어가며 처리량을 두 배로 늘렸습니다. 이런 성능 향상은 업그레이드를 해야만 얻을 수 있습니다. - 런타임 선택보다 프레임워크 선택이 훨씬 큰 변수
TanStack Start와 Next.js 사이의 차이는(처리량 3배, 레이턴시 690배 차이) 같은 프레임워크에서 Watt와 Node.js를 비교했을 때의 차이보다 훨씬 큽니다. 어떤 런타임을 고를지보다, 어떤 프레임워크를 고를지가 훨씬 중요합니다. - Next.js에는 캐싱이 사실상 필수다
초당 1,000 요청 수준에서 Next.js는 고전했습니다. 고부하 SSR 워크로드에서는, ISR, 에지 캐싱, 컴포넌트 캐싱 같은 공격적인 캐시 전략을 고려해야 합니다. Next.js는 이를 위한 훌륭한 프리미티브들을 제공하고 있고, Watt 위에서도 그대로 사용할 수 있습니다. 다만, 대부분의 e-commerce(혹은 엔터프라이즈) 시나리오에서 우리는 캐싱을 적용하지 않았습니다. 회사들은 수천 개의 실험을 동시에 돌리는 공격적인 개인화 전략과 A/B 테스트를 원하기 때문입니다. 그럼에도, v15에서 v16 Canary로의 도약은 의미 있는 개선을 보여주며, 이런 추세가 이어진다면 격차는 계속 좁혀질 것입니다.
만약 기술 스택 선택에서 성능을 중요한 기준으로 삼고 싶다면, 프레임워크를 고르거나 개발을 시작하기 전에 각 라우트별로 명확한 레이턴시 예산을 미리 정해두는 방법을 추천합니다. 구체적인 성능 목표를 초기에 잡아두면 아키텍처나 도구 선택에 영향을 주고, 실제 요구사항을 충족하는 스택을 갖추는 데 도움이 됩니다. 라우트별 레이턴시를 미리 계획해두면, 캐싱·프레임워크 선택·런타임 튜닝 같은 요소가 사용자 경험에 어떤 영향을 줄지 더 명확히 볼 수 있습니다.
결론
이번 벤치마크는 동일한 앱을 고부하 환경에서 실행했을 때, SSR 프레임워크 간에 상당한 성능 차이가 있음을 보여줍니다:
- TanStack Start는 초당 1,000 요청을 평균 13ms 레이턴시로 처리하며 성능 리더로 떠올랐습니다.
- React Router는 실패 없이 안정적인 성능을 제공했습니다.
- Next.js는 이 부하 수준에서는 어려움을 겪었지만, v16 canary로 업그레이드한 뒤 처리량이 두 배, 레이턴시는 6배 개선되었습니다.
숫자를 넘어, 이 프로젝트는 “보이지 않는 것은 고칠 수 없다”는 사실을 다시 한 번 증명했습니다. 우리는 내부 성능 테스트에 platformatic/flame을 사용하고 있으며, 벤치마크 데이터를 프레임워크 팀과 공유한 덕분에 실제 개선이 이루어졌습니다. TanStack 팀이 7개 버전 동안 252배 개선을 이끌어낸 사례, Next.js 팀이 React의 RSC 역직렬화를 75% 더 빠르게 만든 작업 모두, 공개된 성능 데이터가 특정 프레임워크나 프로젝트에 그치지 않고 생태계 전체에 도움이 된다는 점을 잘 보여줍니다.
SSR 프레임워크를 선택하는 팀에게 이번 결과는 다음과 같이 시사합니다:
- 고처리량이 핵심인 경우: TanStack Start 또는 React Router를 고려해보세요.
- 이미 Next.js 프로젝트를 사용 중이라면, 최신 버전으로 업그레이드해 큰 성능 향상을 얻을 수 있습니다. Watt를 함께 사용하면 처리량을 한층 더 끌어올릴 수 있습니다.
- 런타임 최적화 측면에서: Watt는 어떤 프레임워크를 사용하더라도 일관된 성능 향상을 제공합니다.