본문으로 건너뛰기

TanStack Start와 Next.js 비교

TanStack Start와 Next.js 중 하나를 선택할 때는 단순히 기능만 고려해서는 안 됩니다. 각 프레임워크가 무엇을 최적화하며, 그러한 결정이 전체 개발 경험에 어떤 연쇄적 영향을 미치는지 이해해야 합니다.

이 페이지에서는 근본적인 차이점을 설명하고, 흔한 오해를 바로잡으며, 충분한 정보를 바탕으로 결정할 수 있도록 돕습니다. 기능별 비교표는 전체 비교표를 참조하세요. 전환할 준비가 되셨나요? 마이그레이션 가이드를 참조하세요.

벤치마크에 관한 참고 사항: 누군가가 방법론, 앱 복잡도, 호스팅 세부 정보, 구체적인 설정 없이 Start와 Next의 성능 수치를 인용해 비교한다면, 그 수치는 의미가 없습니다. 비교할 때는 양쪽 모두 모범 사례를 따른다고 가정해야 합니다. Next에는 최적화된 사용 방식의 이점을 적용하면서 Start 사용자는 잘못 설정한다고 가정해서는 안 됩니다.

React에 대한 서로 다른 두 가지 비전

두 프레임워크 모두 훌륭한 React 애플리케이션 구축을 돕고자 합니다. 하지만 "훌륭함"의 의미에 대해 서로 다른 가정에서 출발합니다.

Next.js: 플랫폼 중심 접근 방식

Next.js는 Vercel이 그리는 웹의 비전, 즉 서버 우선 렌더링, 긴밀한 플랫폼 통합, 자사 인프라에서 가장 효과적으로 작동하는 자동 최적화에 맞춰 최적화됩니다.

핵심 전제는 대부분의 웹 콘텐츠가 정적이거나 정적에 가깝다는 것입니다. Server Components가 기본값이어야 하며, 상호 작용 기능은 필요할 때 선택하는 예외입니다. 프레임워크가 사용자를 대신해 결정을 내리고, 그 결정은 자사 인프라에 맞게 최적화되어야 합니다.

다음과 같은 경우에 적합합니다:

  • Vercel에 배포하며 긴밀한 플랫폼 통합을 원하는 경우
  • 앱이 콘텐츠 중심이며 일부 영역에 상호 작용 기능이 있는 경우
  • 프레임워크가 사용자를 대신해 아키텍처 결정을 내려주기를 원하는 경우

TanStack Start: 개발자 우선 접근 방식

TanStack Start는 개발자의 제어권과 정확성에 최적화되어 있습니다. 즉, 전반적인 타입 안전성, 암시적 방식보다 명시적 방식, 조합 가능한 프리미티브, 자유로운 배포를 제공합니다.

핵심 전제는 개발자가 프레임워크보다 자신의 앱을 더 잘 안다는 것입니다. 서버 렌더링은 적합한 곳에서 선택적으로 사용하는 최적화입니다. 프레임워크는 강력한 프리미티브를 제공하고 개발자의 작업을 방해하지 않아야 합니다.

다음과 같은 경우에 적합합니다:

  • 배포 대상을 프레임워크가 대신 정하는 것이 아니라 직접 결정하고 싶은 경우
  • 앱의 상호작용성이 매우 높은 경우
  • 타입 안전성과 명시적인 제어를 중시하는 경우
  • 코드가 정확히 무엇을 하는지 파악하고 싶은 경우

멘탈 모델의 차이

이는 반드시 이해해야 할 가장 중요한 차이입니다. 나머지 모든 차이는 여기에서 비롯됩니다.

둘 다 SSR 지원 - 상호작용성에 대한 서로 다른 기본값

분명히 짚고 넘어가겠습니다. 두 프레임워크 모두 기본적으로 SSR을 사용하며, 정적 생성과 React Server Components를 모두 지원합니다. 차이는 기능 자체가 아니라, 해당 기능에 접근하는 방식과 확보할 수 있는 제어권의 정도입니다.

Next.js는 Server Components를 기본값으로 사용합니다. "use client"를 추가하지 않는 한 모든 컴포넌트가 Server Component입니다. Server Components에서는 상태, 이펙트 또는 이벤트 핸들러를 사용할 수 없으므로 상호작용성을 구현하려면 프레임워크의 암시적 경계, 캐싱 계층, 직렬화 규칙을 이해해야 합니다.

TanStack Start는 상호작용 가능한 컴포넌트(전통적인 React)를 기본값으로 사용합니다. 컴포넌트는 SSR과 하이드레이션을 거치며, 별도의 설정 없이 상태와 이벤트 핸들러를 사용할 수 있습니다. 무거운 정적 콘텐츠를 처리하거나, 비밀 정보를 서버 측에 유지하거나, 번들 크기를 줄이는 등 Server Components가 가치를 제공하는 곳에서 선택적으로 사용합니다.

두 접근 방식 모두 같은 목적지에 도달합니다. 중요한 질문은 여러분의 앱에서 어느 방향이 흐름을 거스르는 것처럼 느껴지는가입니다.

다음을 고려해 보세요:

  • 대부분의 컴포넌트에 상태, 이펙트 또는 이벤트 핸들러가 필요하다면 Next의 기본값 때문에 계속해서 "use client" 주석을 추가하고 직렬화 경계를 고려해야 합니다
  • 콘텐츠 대부분이 정적이라면 Start에서는 해당 컴포넌트에 서버 전용 렌더링을 명시적으로 적용할 수 있으며, 캐싱과 하이드레이션을 더 명확하게 제어할 수 있습니다
  • 어느 쪽이든 Start에서는 단순히 어디에서 렌더링할지뿐 아니라 어떻게 렌더링할지도 더 세밀하게 제어할 수 있습니다

암시적 방식과 명시적 방식

두 프레임워크 모두 코드 분할, 캐싱, SSR, 정적 최적화라는 기본 기능을 처리합니다. 차이는 가시성과 예측 가능성입니다. Next.js에는 암시적 동작이 여러 겹으로 쌓여 있습니다. 여기에는 호환성을 깨뜨리는 변경과 커뮤니티의 불만이 반복되어 온 다계층 서버 측 캐싱, 파일 구조에 종속된 데이터 가져오기 규칙, 재정의하려면 프레임워크 내부 구조를 이해해야 하는 최적화가 포함됩니다.

TanStack Start는 장황하지 않으면서도 명시적입니다. 로더 함수, 캐시 설정, 미들웨어 체인은 규칙 뒤에 숨지 않고 코드에 드러납니다. 이는 코드가 더 많다는 의미가 아니라, 작성한 코드가 런타임에 일어나는 동작과 직접 대응한다는 의미입니다.

아키텍처 심층 분석

빌드 파이프라인

Next.js는 맞춤형 빌드 시스템(과거에는 Webpack, 현재는 Turbopack)을 사용합니다. 이 시스템은 Next.js의 아키텍처와 긴밀하게 통합되어 있어 최적화가 가능하지만 유연성은 제한됩니다. Turbopack의 개발 속도는 개선되었지만, 여전히 Vite나 Rsbuild에는 미치지 못합니다.

TanStack Start는 Vite와 Rsbuild를 지원합니다. 이는 다음을 의미합니다:

  • 더 빠른 개발 서버 시작
  • 더 빠른 HMR
  • 최신 플러그인 생태계 이용
  • 다른 프로젝트에도 적용할 수 있는 표준 도구

런타임

Next.js는 Server Components, 서버 측 캐싱, 자동 최적화, App Router 규칙을 지원하기 위해 상당한 규모의 런타임을 제공합니다. 이 런타임은 실제로 무겁습니다.

TanStack Start는 최소한의 런타임을 제공합니다. 라우터는 강력하면서도 가볍습니다. 서버 함수는 얇은 RPC 래퍼입니다. 개발자와 React 사이에 프레임워크의 마법 같은 계층이 없습니다.

번들 크기가 전부는 아니지만, 다른 모든 요소가 구축되는 기반입니다. Start의 아키텍처는 런타임 오버헤드를 최소화하도록 설계되어 RSC를 지원하더라도 런타임이 가볍게 유지됩니다. Next의 번들 용량 중 상당 부분은 기능의 무게가 아니라 아키텍처로 인한 비용입니다.

타입 시스템

Next.js는 TypeScript를 지원합니다. TanStack Start는 TypeScript를 중심으로 구축됩니다.

이 차이는 중요합니다:

  • Next.js에서는 클라이언트와 서버 사이의 경계로 인해 타입 공백이 생깁니다. Server Actions는 TypeScript가 예상하는 형식과 일치하지 않는 데이터를 받을 수 있습니다. 안전을 보장하려면 런타임 검증이 필요합니다.
  • Start에서는 서버 함수가 엔드투엔드 타입 안전성을 제공합니다. 입력 검증, 반환 타입, 미들웨어 컨텍스트, 라우트 매개변수가 모두 컴파일 시점에 검사됩니다.

AI 지원 개발 시대에는 엔드투엔드 타입 안전성을 반드시 보장해야 합니다. 타입은 단순히 "DX에 유용한 것"이 아니라 프로덕션 오류를 방지하는 정확성 보장 수단입니다.

라우팅

이 부분에서 TanStack은 가장 빛을 발합니다. Start의 기반인 TanStack Router는 모든 프레임워크를 통틀어 가장 강력한 타입 안전 라우터입니다.

Next.js에는 없는 기능:

  • 타입 안전 검색 매개변수 - 완전한 타입 추론으로 검색 매개변수를 정의하고 검증하며 파싱합니다. Next는 문자열 형태의 ?foo=bar을 제공하지만, Start는 검증되고 타입이 지정된 객체를 제공합니다
  • 검색 매개변수 검증 및 미들웨어 - 라우트 수준에서 검색 매개변수를 변환하고 검증합니다
  • 타입 안전 경로 매개변수 - 경로 매개변수는 단순한 string | string[]이 아니라 추론되고 검증됩니다
  • 타입 안전 라우트 컨텍스트 - 중첩된 라우트를 따라 타입이 지정된 컨텍스트를 전달합니다
  • 경로 매개변수 검증 및 맞춤형 파싱 - /users/123을 숫자로 파싱하고 존재 여부를 검증합니다
  • 라우트 마운트/전환/언마운트 이벤트 - 라우트 수명 주기에 연결합니다
  • 코드 기반 라우트 - 파일 기반 방식이 적합하지 않을 때 코드에서 라우트를 정의합니다
  • 내장 개발자 도구 - 라우터 상태, 캐시, 대기 중인 탐색을 검사합니다
  • 요소 스크롤 복원 - 페이지뿐만 아니라 특정 요소의 스크롤 위치도 복원합니다
  • 탐색 차단 - 저장하지 않은 변경 사항을 경고하는 useBlocker을 제공합니다

실제로 작동하는 타입 안전성:

  • 라우트 경로는 완전히 타입이 지정됩니다 - 오타는 컴파일 오류가 됩니다
  • 링크는 대상을 검증합니다 - 프로덕션에서 깨진 링크가 없습니다
  • 로더는 해당 라우트 컨텍스트를 압니다 - 어떤 데이터를 사용할 수 있는지 추측할 필요가 없습니다
  • 검색 매개변수는 URL부터 컴포넌트까지 엔드투엔드로 타입이 지정됩니다

Next.js에는 제대로 작동하는 파일 기반 라우팅이 있지만, 타입 안전성은 TanStack Router의 컴파일 타임 보장과 비교하면 피상적입니다(링크 힌트를 제공하는 IDE 플러그인 수준입니다).

일급 통합:

TanStack Router는 처음부터 동형성과 하이드레이션을 고려하여 설계되었습니다. 따라서 TanStack Query 및 기타 데이터 가져오기 라이브러리와의 일급 통합을 위한 기반이 됩니다. Next에서는 Query를 수동으로 연결하지만, Start에서는 공식 통합을 통해 지원되는 패턴입니다.

전체 기능 비교표는 전체 라우터 비교를 참조하세요.

캐시에 관한 문제

캐시는 철학적 차이가 가장 뚜렷하게 드러나는 부분입니다.

Next.js: 암시적이며 서버 측에서 작동합니다

Next.js는 기본적으로 적극적으로 캐시합니다(적어도 과거에는 그랬으며, 이 동작은 여러 번 변경되었습니다). 캐시는 다음 계층에서 이루어집니다:

  • 요청 메모이제이션
  • 데이터 캐시
  • 전체 라우트 캐시
  • 라우터 캐시

각 계층에는 고유한 무효화 의미 체계가 있습니다. 이 시스템은 예측하기 어렵다는 커뮤니티의 비판 속에서 여러 번 다시 작성되었습니다. Next 15에서는 기본값이 단순해졌지만, 서버 측 RSC 스트림 캐시의 근본적인 복잡성은 여전히 남아 있습니다.

사람들이 "컴포넌트 캐시"를 이야기할 때는 서버에서 직렬화된 RSC 스트림을 캐시하는 것을 의미합니다. 이는 대부분이 생각하는 것보다 더 복잡합니다:

  • 복잡한 무효화 의미 체계를 지닌 텍스트 스트림을 캐시합니다
  • 자체 제약 조건이 있는 람다 방식 환경에서 수행됩니다
  • 세분화된 무효화에는 명시적인 태그 설정이 필요합니다
  • 캐시 미스는 항상 예측 가능한 것은 아닌 방식으로 컴포넌트 트리 전체에 연쇄적으로 전파됩니다

서버 측 컴포넌트 캐시가 특별하다고 주장하는 사람에게 물어보세요: "데이터 종속성이 변경될 때 캐시된 RSC 스트림을 어떻게 무효화합니까?" 대부분 명확하게 답하지 못합니다.

TanStack Start: RSC는 단지 데이터입니다

Start는 Server Component 출력을 앱을 통해 흐르는 다른 모든 데이터와 동일하게 취급합니다. 고유한 의미 체계를 가진 특별한 "컴포넌트 캐시"는 없습니다. RSC 페이로드는 데이터이며, 원하는 방식으로 데이터를 캐시할 수 있습니다:

  • TanStack Router - staleTimegcTime를 사용하는 내장 SWR 방식 캐시
  • TanStack Query - 모든 기능을 갖춘 비동기 상태 관리
  • CDN 캐시 - 엣지에서 사용하는 표준 HTTP 캐시 헤더
  • Redis, 데이터베이스 등 무엇이든 - 단지 데이터이므로 기존 인프라를 사용합니다
export const Route = createFileRoute('/posts/$postId')({
loader: async ({ params }) => fetchPost(params.postId),
staleTime: 10_000, // Fresh for 10 seconds
gcTime: 5 * 60_000, // Keep in memory for 5 minutes
})

이는 TanStack Query가 수백만 개의 애플리케이션에서 실전 검증한 것과 동일한 SWR 패턴입니다. 새로운 멘탈 모델이 없습니다. 학습해야 할 프레임워크별 캐시 의미 체계도 없습니다. 실체 없는 문제를 쫓을 필요도 없습니다.

캐시 계층은 잘 정립되어 있으며 자연스럽게 조합됩니다:

  • 클라이언트 측 SWR 캐시(라우터에 내장됨)
  • 엣지의 CDN 캐시(표준 HTTP 의미 체계)
  • 원하는 위치의 서버 측 캐시(명시적인 옵트인 방식)

데이터를 캐시하는 방법은 이미 알고 있습니다. Start를 사용하면서 새로운 방식을 배울 필요는 없습니다.

RSC에 대한 결론

Start는 Next.js와 기능적으로 동등한 완전한 RSC 지원을 제공합니다. 차이는 멘탈 모델과 인지적 부담에 있습니다.

Next에서 RSC는 패러다임입니다. RSC를 중심으로 구축하고, 끊임없이 고려하며, 모든 곳에서 그 경계를 관리합니다. Start에서 RSC는 또 하나의 데이터 기본 요소일 뿐입니다. TanStack Query와 Router에서 이미 익힌 패턴을 사용해 가져오고, 캐시하고, 조합합니다.

이 접근 방식을 Composite Components라고 부릅니다. 서버에서 생성되어 클라이언트가 가져오고, 캐시하고, 스트리밍하고, 조립할 수 있는 React 컴포넌트입니다. 구성은 클라이언트가 담당하고, 서버는 UI 조각을 전달합니다. 새로운 멘탈 모델도, 프레임워크별 캐싱 의미 체계도 필요 없습니다. 이미 이해하고 있는 도구를 통해 데이터가 흐를 뿐입니다.

전체 심층 설명은 Composite Components: 클라이언트 주도 구성 방식의 Server Components를 참고하세요.

서버 함수와 Server Actions 비교

두 프레임워크 모두 클라이언트에서 서버 코드를 호출할 수 있습니다. 하지만 접근 방식은 크게 다릅니다.

Next.js Server Actions

'use server'

export async function createPost(formData: FormData) {
const title = formData.get('title')
// No compile-time type safety on inputs
return db.posts.create({ title })
}

Server Actions는 폼 및 전환과 통합됩니다. 간단한 경우에는 편리하지만 경계에서 제공하는 타입 안전성은 제한적입니다.

TanStack 서버 함수

export const createPost = createServerFn({ method: 'POST' })
.validator(z.object({ title: z.string().min(1) }))
.middleware([authMiddleware])
.handler(async ({ data, context }) => {
// data is typed and validated
// context comes from middleware, also typed
return db.posts.create({ title: data.title })
})

서버 함수는 다음을 제공합니다:

  • 입력 검증 - 스키마를 정의하면 런타임 검증과 컴파일 타임 타입을 얻습니다
  • 미들웨어 - 조합할 수 있으며 클라이언트와 서버 모두에서 작동합니다
  • 완전한 타입 추론 - 입력부터 출력, 오류 처리까지 타입을 추론합니다
  • 명시적 RPC - 어떤 일이 일어나는지 명확한 멘탈 모델을 제공합니다

보안 참고: Start의 아키텍처는 서버에서 Flight 데이터를 파싱하지 않습니다. 페이로드는 한 방향(서버에서 클라이언트)으로 흐릅니다. 최근 React 보안 권고에서 다룬 RSC 직렬화 취약점은 Start의 모델에 적용되지 않습니다.

Next.js가 우위에 있는 부분

Next.js는 더 오래되었습니다. 이는 기술적 이점은 아니지만 실질적인 생태계 효과를 만들어 냅니다:

더 많은 콘텐츠 - 더 많은 튜토리얼, 더 많은 Stack Overflow 답변, 더 많은 블로그 게시물, 더 많은 예제 저장소가 있습니다. 문제를 Google에서 검색하면 Next에 특화된 답변을 찾을 가능성이 더 높습니다.

인지도 - 많은 커뮤니티에서 Next는 기본적으로 추천됩니다. 이는 더 많은 개발자가 사용했다는 뜻이고, 그 결과 더 많은 콘텐츠가 만들어져 이 순환이 강화됩니다.

Vercel 통합 - Next.js는 Vercel에서 개발하므로 새로운 Vercel 플랫폼 기능은 Next.js 지원과 함께 먼저 출시되는 경우가 많습니다. 그렇지만 Start도 Vercel에서 훌륭하게 작동합니다. Start를 선택해도 미리보기 배포나 엣지 함수를 포기하지 않습니다. 단지 Vercel만이 유일한 일급 옵션이 되도록 종속되지 않을 뿐입니다.

내장 이미지/글꼴 최적화 - Start는 플러그형 솔루션(예: Unpic)을 통해 이미지 최적화를 지원하지만 내장 기능은 아닙니다. "내장" 방식이 "플러그형" 방식보다 나은지는 프레임워크가 그 선택을 대신하기를 원하는지에 따라 달라집니다.

이 중 어느 것도 기술적 우위가 아니라 생태계와 비즈니스 모델의 이점입니다. 기술적 장점만 놓고 보면 Start의 접근 방식에 확신이 있습니다.

Start가 더 잘하는 점

RSC를 지원하므로 질문은 "Start에 무엇이 부족한가?"가 아니라 **"왜 Next의 API 설계를 위해 Start의 라우터, 타입 안전성, 캐싱 설계, 더 단순한 멘탈 모델을 포기해야 하는가?"**입니다.

타입 안전성 - 나중에 덧붙인 것이 아닌 엔드투엔드 타입 안전성을 제공합니다. 이를 통해 버그를 방지하고 확신을 가지고 리팩터링할 수 있습니다.

라우팅 - 모든 프레임워크를 통틀어 가장 강력한 타입 안전 라우터입니다. 검색 매개변수, 경로 매개변수, 로더, 미들웨어에 모두 완전한 타입이 지정됩니다.

캐싱 모델 - TanStack Query를 사용해 본 적이 있다면 이미 이해하고 있는 명시적 SWR 프리미티브를 제공합니다. 디버깅해야 할 암시적 계층이 없습니다.

개발 경험 - Vite와 Rsbuild는 빠릅니다. 즉시 시작되고 HMR이 빠르며 리소스 사용량이 적습니다. 이러한 이점은 하루 업무 시간 동안 누적되며, AI 에이전트가 짧은 주기로 코드를 반복 수정할 때는 더욱 커집니다.

기능으로서의 배포 - Start는 배포 유연성을 핵심 기능으로 취급합니다. Cloudflare, Netlify, AWS, Fly, Railway, 자체 서버를 모두 동등하게 지원합니다. 플랫폼별 최적화가 아닌 표준을 기반으로 구축되므로 앱은 어디서나 동일하게 작동합니다. 따라서 다음을 수행할 수 있습니다:

  • 가격, 성능 또는 사용자와의 근접성을 기준으로 호스팅을 선택합니다
  • 배포 로직을 다시 작성하지 않고 제공업체를 전환합니다
  • 서로 다른 플랫폼의 개발, 스테이징, 프로덕션 환경에서 동일한 코드를 실행합니다
  • 나중에 마찰을 일으키는 플랫폼별 패턴이 누적되는 것을 방지합니다

미들웨어 - 클라이언트와 서버 모두에서 요청 수준과 서버 함수 수준 양쪽에 작동하는 조합 가능한 미들웨어입니다.

디버깅 - 실행을 예측할 수 있습니다. 문제가 발생하면 추적할 수 있습니다. 동작을 숨기는 추상화 계층이 없습니다.

요약

측면TanStack StartNext.js
철학개발자 제어, 명시적 패턴플랫폼 통합, 규칙
컴포넌트기본적으로 대화형이며 RSC는 선택 적용기본적으로 Server Components 사용
타입 안전성엔드투엔드, 컴파일 시점경계에 공백이 있는 TypeScript 지원
서버 함수타입 지정, 검증, 미들웨어 지원타입이 없는 경계, 미들웨어 없음
캐싱명시적 SWR 프리미티브다중 계층 암시적 캐싱
빌드 도구Vite 또는 RsbuildTurbopack/Webpack
배포모든 환경을 동등하게 지원Vercel에 최적화
라우팅최고 수준의 타입 안전성파일 기반, 기본적인 타입
RSC지원됨지원됨
성숙도2년 이상, 1.0에 근접8년 이상, API가 역사적으로 불안정함

Start를 사용해 볼 준비가 되셨나요? 시작하기 가이드를 참조하거나 Next.js에서 마이그레이션하세요.