TanStack Start가 React Server Components를 프레임워크 중심 규약이 아닌 가져오고, 캐시하고, 렌더링할 수 있는 원시 구성 요소로 다루는 방식과 Composite Components를 소개합니다.
검색...
K
자동
블로그이 페이지에서
페이지 복사
2026년 4월 13일 Manuel Schiller, Tanner Linsley, Jack Herrington 작성.

TanStack에서는 늘 90%의 사용 사례를 쉽게 다룰 수 있으면서도, 고급 사용 사례를 위해서는 틀을 벗어날 수 있는 유연성을 제공하는 도구를 만들기 위해 노력해 왔습니다. 왜냐하면 일이 정말 중요해질 때, 무엇이 애플리케이션에 가장 적합한지는 여러분이 가장 잘 알고 있으며, 직접 제어할 자유를 누릴 자격이 있다고 우리는 믿기 때문입니다.
이것이 언제나 TanStack의 철학이었고, React Server Components에서도 동일한 경험을 제공하기 위해 시간을 들인 우리를 여러분이 믿어주셨다는 점이 기쁩니다.
네... 아닙니다. 이 글은 서버 컴포넌트 입문 강좌가 아닙니다. 아직 Server Components에 익숙하지 않다면, 잠시 공식 React Server Components 문서를 읽고 개념을 익혀 주세요.
RSC는 무겁거나 비용이 큰 렌더링 로직을 클라이언트에서 서버로 옮기기 위한 필수적인 원시 구성 요소입니다.
특히 정적이거나 자주 바뀌지 않는 콘텐츠처럼 일관되고 세밀하게 캐시할 수 있는 경우에 더욱 그렇습니다.
Markdown 파서, 문법 강조기, 날짜 포맷팅 라이브러리, 검색 인덱싱, 콘텐츠 변환 등은 모두 훌륭한 사용 사례이며, 물론 이것만이 전부는 아닙니다.
이것은 _매우 강력한 원시 구성 요소_입니다.
이제 대부분의 사람들은 RSC를 서버 우선 방식으로 생각합니다. 서버가 트리를 소유하고, 'use client'가 상호작용이 필요한 부분을 표시하며, 프레임워크 규약이 전체 조합 방식을 결정합니다.
이 모델은 분명 매력적일 수 있습니다. 스트리밍, 서버 렌더링, 그리고 함께 배치된 서버 측 작업이 처음부터 내장된 것처럼 느껴지게 만들기 때문입니다.
하지만 동시에 RSC는 유용한 원시 구성 요소에서 벗어나, 앱 전체가 그 주위를 돌아야 하는 존재가 되어 버립니다. 결국 프레임워크가 RSC를 어떻게 생성하는지, 어디서 렌더링하는지, 상호작용 경계를 어떻게 정의하는지, 데이터나 사용자 동작이 바뀔 때 UI를 어떻게 재조합하는지까지 소유하게 됩니다.
우리가 계속 걸렸던 부분이 바로 이것입니다. 우리는 RSC의 가치를 얻기 위해 처음부터 그 전체 모델을 받아들여야 한다고 생각하지 않습니다.
클라이언트에서 JSON을 가져오듯이, RSC도 그만큼 세밀하게 사용할 수 있다면 어떨까요? 더 나아가, 서버에서 렌더링된 UI를 어떻게 가져오고, 캐시하고, 조합할지를 처음부터 클라이언트가 결정한다면 어떨까요?
TanStack Start의 핵심 아이디어는 RSC를 그저 데이터 스트림으로 보는 것입니다. 즉, 서버가 소유한 컴포넌트 트리가 아니라, 클라이언트에서 원하는 시점에 원하는 방식으로 가져오고, 캐시하고, 렌더링할 수 있는 대상이라는 뜻입니다. 이 한 가지 전환만으로도, 작동 방식의 본질은 바꾸지 않으면서 훨씬 더 조합 가능해집니다.
TanStack Start에서 RSC는 그저 React Flight 스트림입니다. 입 밖으로 내기에는 너무 당연한 말처럼 들릴 수 있지만, 바로 그것이 핵심입니다. 우리는 이것이 모든 프레임워크의 동작을 바꿔 버리는 특수 규칙, API, 네트워크 효과를 가진 블랙박스 규약으로 감싸이길 원하지 않았습니다.
우리는 RSC가 다른 서버 데이터 조각처럼 동작하길 원했습니다. 즉 프레임워크가 이것을 지원하기 위해 특별한 일을 할 필요가 없어야 하며, 단지 스트리밍을 일급 시민으로 지원하기만 하면 된다는 뜻입니다.
실제로 이것이 의미하는 바는 다음과 같습니다.
다음은 TanStack Start에서의 RSC 예시입니다.
자연스럽게, 서버 동기화 로직을 줄이기 위해 TanStack Query로 이를 관리하겠습니다!
tsx
import { createServerFn } from '@tanstack/react-start'
import {
createFromReadableStream,
renderToReadableStream,
} from '@tanstack/react-start/rsc'
// 서버 함수를 생성합니다
const getGreeting = createServerFn().handler(async () => {
// RSC readable stream을 생성합니다
return renderToReadableStream(
// JSX를 반환합니다
<h1>Hello from the server</h1>,
)
})
function Greeting() {
const query = useQuery({
queryKey: ['greeting'],
queryFn: async () =>
// 스트림에서 렌더 가능한 요소를 생성합니다
createFromReadableStream(
// 서버 함수를 호출해 스트림을 가져옵니다
await getGreeting(),
),
})
// 렌더링!
return <>{query.data}</>
}
import { createServerFn } from '@tanstack/react-start'
import {
createFromReadableStream,
renderToReadableStream,
} from '@tanstack/react-start/rsc'
// 서버 함수를 생성합니다
const getGreeting = createServerFn().handler(async () => {
// RSC readable stream을 생성합니다
return renderToReadableStream(
// JSX를 반환합니다
<h1>Hello from the server</h1>,
)
})
function Greeting() {
const query = useQuery({
queryKey: ['greeting'],
queryFn: async () =>
// 스트림에서 렌더 가능한 요소를 생성합니다
createFromReadableStream(
// 서버 함수를 호출해 스트림을 가져옵니다
await getGreeting(),
),
})
// 렌더링!
return <>{query.data}</>
}
원시 수준에서 API 표면은 의도적으로 작게 유지됩니다.
내부적으로 이야기는 단순합니다. React가 서버에서 Flight 스트림으로 렌더링하고, 클라이언트가 그 스트림을 다시 React 요소 트리로 디코딩합니다.
여기에는 숨겨진 비밀 프로토콜이 없습니다. 이것이 바로 그 원시 구성 요소입니다. 표준 Flight 스트림이 들어오고, 표준 React 요소가 나옵니다.
이것만으로도 많은 것을 구축할 수 있습니다. RSC 출력을 앱 안의 다른 비동기 리소스처럼 다룰 수 있으며, 모든 결정을 특별 취급되는 프레임워크 소유 규약을 통해 우회할 필요가 없습니다.
불필요한 프레임워크 규약 이야기로 돌아가 보죠. 캐싱은 새로 발명해야 할 무언가가 아니며, RSC도 예외가 아닙니다.
RSC가 "그저 데이터"가 되면 캐싱 이야기는 훨씬 단순해집니다. 그리고 이것들이 단지 세밀한 스트림으로서 HTTP 위에서 평범하게 전달되고 렌더링 중에 투명하게 처리되기 때문에, 클라이언트에서 캐시하기 쉬울 뿐 아니라 서버 쪽 경로 어디에서든 캐시하기 쉽습니다. 메모리, 데이터베이스, CDN, 혹은 여러분의 아키텍처가 이미 바이트, 응답, 데이터를 캐시하는 어떤 곳이든 가능합니다.
이것은 클라이언트에서 여러분이 이미 잘 알고 있을 가능성이 큰 캐싱 계층에도 동일하게 적용되며, 새롭고 낯선 접근 방식이나 사고방식 전환을 요구하지 않습니다.
설명해 보겠습니다.
TanStack Query는 이를 아주 잘 보여줍니다. 특별한 "RSC 모드"가 필요하지 않습니다. RSC 페이로드가 비동기 쿼리의 일부가 되면, 여전히 명시적인 캐시 키, staleTime, 백그라운드 재가져오기, 그리고 Query의 다른 도구들을 그대로 사용할 수 있습니다. 정적 콘텐츠라면 staleTime: Infinity만 설정하면 끝입니다.
tsx
import { createServerFn } from '@tanstack/react-start'
import {
createFromReadableStream,
renderToReadableStream,
} from '@tanstack/react-start/rsc'
const getGreeting = createServerFn().handler(async () => {
return renderToReadableStream(<h1>Hello from the server</h1>)
})
function PostPage({ postId }: { postId: string }) {
const { data } = useSuspenseQuery({
queryKey: ['greeting-rsc', postId],
queryFn: async () => ({
Greeting: await createFromReadableStream(await getGreeting()),
}),
staleTime: 5 * 60 * 1000,
})
return <>{data.Greeting}</>
}
import { createServerFn } from '@tanstack/react-start'
import {
createFromReadableStream,
renderToReadableStream,
} from '@tanstack/react-start/rsc'
const getGreeting = createServerFn().handler(async () => {
return renderToReadableStream(<h1>Hello from the server</h1>)
})
function PostPage({ postId }: { postId: string }) {
const { data } = useSuspenseQuery({
queryKey: ['greeting-rsc', postId],
queryFn: async () => ({
Greeting: await createFromReadableStream(await getGreeting()),
}),
staleTime: 5 * 60 * 1000,
})
return <>{data.Greeting}</>
}
TanStack Router는 더 멋집니다. 스트림을 기본적으로 지원하기 때문에, 라우트 로더 안의 RSC 페이로드도 다시 한 번 "그저 데이터"일 뿐입니다.
await할 수도 있고, 스트리밍할 수도 있으며(네, 스트림을 스트리밍하는 것입니다), 결과를 다른 로더 출력처럼 자연스럽게 라우터 캐시에 저장할 수 있습니다.
tsx
const getGreeting = createServerFn().handler(async () => {
return renderToReadableStream(<h1>Hello from the server</h1>)
})
export const Route = createFileRoute('/hello')({
loader: async () => ({
greeting: getGreeting(),
}),
component: function HelloPage() {
const { greeting } = Route.useLoaderData()
return <>{greeting}</>
},
})
const getGreeting = createServerFn().handler(async () => {
return renderToReadableStream(<h1>Hello from the server</h1>)
})
export const Route = createFileRoute('/hello')({
loader: async () => ({
greeting: getGreeting(),
}),
component: function HelloPage() {
const { greeting } = Route.useLoaderData()
return <>{greeting}</>
},
})
/posts/abc에서 /posts/xyz로 이동하면 로더가 다시 실행됩니다. 다시 /posts/abc로 돌아오면 Router는 캐시된 결과를 즉시 제공할 수 있습니다. 뒤로 가기 버튼이 빠릿하게 느껴지는 경험은 여러분이 이미 사용 중인 동일한 로더 캐싱 모델에서 자연스럽게 나옵니다.
GET server functions도 내부적으로는 결국 HTTP일 뿐이므로, CDN 계층에서 응답 자체를 캐시할 수도 있습니다.
tsx
import { createServerFn } from '@tanstack/react-start'
import { renderToReadableStream } from '@tanstack/react-start/rsc'
import { setResponseHeaders } from '@tanstack/react-start/server'
const getGreeting = createServerFn({ method: 'GET' }).handler(async () => {
setResponseHeaders(
new Headers({
'Cache-Control': 'public, max-age=0, must-revalidate',
'Netlify-CDN-Cache-Control':
'public, max-age=300, durable, stale-while-revalidate=300',
}),
)
return renderToReadableStream(<h1>Hello from the server</h1>)
})
import { createServerFn } from '@tanstack/react-start'
import { renderToReadableStream } from '@tanstack/react-start/rsc'
import { setResponseHeaders } from '@tanstack/react-start/server'
const getGreeting = createServerFn({ method: 'GET' }).handler(async () => {
setResponseHeaders(
new Headers({
'Cache-Control': 'public, max-age=0, must-revalidate',
'Netlify-CDN-Cache-Control':
'public, max-age=300, durable, stale-while-revalidate=300',
}),
)
return renderToReadableStream(<h1>Hello from the server</h1>)
})
이것은 우리가 여기서 블로그와 문서 콘텐츠에 사용하는 것과 동일한 패턴입니다. 브라우저 캐시 규칙은 보수적으로 유지하면서도, CDN은 server function 응답을 훨씬 더 공격적으로 캐시할 수 있습니다.
Start에서는 RSC가 여러분이 이미 사용 중인 동일한 데이터 워크플로 안에 자연스럽게 들어갑니다.
아마 최근 RSC 스택 주변의 CVE를 보셨을 것입니다.
우리는 'use server' actions를 의도적으로 지원하지 않습니다. 기존 공격 벡터 때문이기도 하고, 그것들이 매우 암묵적인 네트워크 경계를 만들 수 있기 때문이기도 합니다.
TanStack Start는 createServerFn을 통한 명시적인 RPC를 요구합니다. 클라이언트-서버 경계는 의도적으로 설계되며, 직렬화, 검증, 미들웨어 의미 체계가 강화되어 있어 모든 사용자 입력을 기본적으로 신뢰할 수 없는 것으로 다루도록 유도합니다.
이렇게 하면 통신 패턴이 명시적이기 때문에 공격 표면이 더 작게 유지됩니다. 그래도 다른 API 표면과 마찬가지로 server function은 인증하고, 검증하고, 의존성을 최신 패치 상태로 유지해야 합니다.
RSC를 원시 구성 요소로 다루면 TanStack Start는 모든 프런트엔드 사용 사례를 포괄할 수 있습니다. 정말로 모든 경우를 말합니다.
서버 컴포넌트를 전혀 사용하지 않습니다. 클라이언트 우선, SPA 스타일입니다. RSC는 도움이 될 때 추가하는 최적화일 뿐, 반드시 중심에 두고 설계해야 하는 패러다임이 아닙니다. 오늘날 대부분의 "앱"은 이미 여기에 속합니다.
정적 셸, 데이터가 많은 영역, SEO가 중요한 콘텐츠에는 서버 컴포넌트를 쓰고, 상호작용이 중요한 곳에는 클라이언트 컴포넌트를 둡니다. 혼합형 "앱"과 "사이트" 프로젝트(제품 + 마케팅)에 특히 잘 맞습니다. 실제로는 "앱" 측보다 "사이트" 측에 더 자주 도움이 되는 경향이 있습니다.
주로 정적 콘텐츠를 서버 측에서 RSC로 파싱하고 렌더링하지만, 필요한 곳에는 클라이언트 상호작용 조각들(예: 댓글, 검색, 동적 위젯)을 얹은 강력하고 hydration된 SPA이기도 합니다. 블로그, 문서, 마케팅 페이지를 떠올리면 됩니다.
빌드 시점에 모든 것을 프리렌더링합니다. 그저 HTML만 제공하면 됩니다. hydration이 전혀 필요 없다면 여기서는 RSC조차 필요하지 않을 수 있습니다!
하나의 프레임워크. 하나의 사고방식. 전체 스펙트럼.
"인터랙티브 프레임워크"를 고르거나, "정적 프레임워크"를 고르거나, "RSC 프레임워크"를 고를 필요가 없습니다.
라우트별, 컴포넌트별, 사용 사례별로 패턴을 선택하면 됩니다. 아키텍처는 그 모든 것을 지원합니다. 그리고 다시 말하지만, 여러분의 앱에 가장 적합한 것은 여러분이 가장 잘 알고 있기 때문입니다.
우리는 감으로 주장하고 싶지 않았기 때문에 tanstack.com의 콘텐츠 중심 영역을 실제로 마이그레이션하고 측정했습니다.
결과는 우리가 바라던 그대로였고, 동시에 과장된 기대가 암시하는 것보다는 더 제한적이기도 했습니다.
가장 효과가 컸던 페이지들은 의미 있게 더 작아졌습니다.
그리고 실제 환경 수치도 그에 따라 움직였습니다.
/blog/react-server-components는 Lighthouse에서 52 -> 74로 올랐습니다.
/router/latest/docs/overview는 78 -> 81로 올랐습니다.
핵심은 이것입니다. 무거운 클라이언트 작업이 더 이상 클라이언트로 전달되지 않았습니다. Markdown 파싱이 사라졌습니다. 문법 강조가 사라졌습니다. 브라우저는 더 적은 JavaScript를 받고, 더 적은 일을 하게 되었습니다. 부수 효과로, 같은 렌더링 로직의 두 버전을 동시에 유지하는 대신 예전의 클라이언트 markdown 및 highlighting 경로를 제거할 수도 있었습니다.
하지만 RSC는 성능을 위한 만능 쿠폰 코드가 아닙니다. 어떤 랜딩 페이지는 거의 변화가 없었고, 몇몇은 약간 더 나빠지기도 했습니다. 이미 상호작용 UI 셸이 지배적인 페이지는, 트리 어딘가에 서버 컴포넌트를 끼워 넣었다고 해서 자동으로 더 빨라지지 않습니다.
이것이 바로 트레이드오프입니다.
이것이 우리가 RSC가 중요하다고 생각하는 이유입니다. 모든 라우트가 서버 컴포넌트가 되어야 해서가 아닙니다. 알맞은 곳에 사용했을 때, 효과가 측정 가능하고 결코 미묘하지 않기 때문입니다.
지금까지의 내용만으로도 충분히 의미가 있습니다. TanStack Start가 RSC를 가져올 수 있고, 캐시할 수 있고, 렌더링할 수 있는 데이터로 다루기만 했더라도, 우리는 이미 그것이 더 나은 RSC 기반이라고 생각했을 것입니다.
하지만 우리는 한 가지 질문을 계속 붙들고 있었습니다. 서버가 UI의 모든 클라이언트 형태 부분을 꼭 결정해야만 할까?
그 질문이 우리를 완전히 새로운 무언가로 이끌었습니다. 바로 Composite Components입니다.
서버가 의도적으로 클라이언트 컴포넌트를 렌더링하고 싶을 때 TanStack Start에서도 use client는 동일하게 작동합니다. use server는 그렇지 않습니다. Start는 대신 명시적인 Server Functions를 사용합니다.
Composite Components는 use client를 대체하는 것이 아닙니다. 비슷한 조합 문제를 반대 방향에서 해결합니다. 서버가 어느 위치에 어떤 클라이언트 컴포넌트가 렌더링될지 결정하는 대신, 서버는 결합 지점을 열어 두고 클라이언트가 트리를 소유하며 그 자리를 무엇으로 채울지 결정하게 할 수 있습니다.
우리에게 정말 새롭게 느껴지는 부분이 바로 이것입니다.
Composite Component는 서버 UI를 렌더링하면서도 클라이언트 콘텐츠를 위한 슬롯을 노출할 수 있습니다. 슬롯은 여러분이 이미 알고 있는 평범한 React 패턴을 사용합니다.
클라이언트가 컴포넌트 트리를 소유하기 때문에, 슬롯에 전달하는 컴포넌트는 일반적인 클라이언트 컴포넌트입니다. 'use client' 지시어는 필요하지 않습니다. 서버는 이것들을 불투명한 플레이스홀더로 배치하지만, 검사하거나, 복제하거나, 변환할 수는 없습니다. 바로 그것이 핵심입니다. 서버는 그것이 무엇인지 알 필요 없이 "여기에 무언가가 들어갑니다"라고 요청할 수 있습니다.
tsx
import { createCompositeComponent } from '@tanstack/react-start/rsc'
const getPost = createServerFn().handler(async ({ data }) => {
const post = await db.posts.get(data.postId)
const src = await createCompositeComponent(
(props: {
children?: React.ReactNode
renderPostActions?: (data: {
postId: string
authorId: string
}) => React.ReactNode
}) => (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
{/* 서버가 이 링크를 직접 렌더링합니다 */}
<Link to="/posts/$postId" params={{ postId: post.nextPostId }}>
Next Post
</Link>
{/* 슬롯: 서버가 여기서 클라이언트 UI를 요청합니다 */}
<footer>
{props.renderPostActions?.({
postId: post.id,
authorId: post.authorId,
})}
</footer>
{/* 슬롯: 클라이언트가 children으로 이것을 채웁니다 */}
{props.children}
</article>
),
)
return { src }
})
import { createCompositeComponent } from '@tanstack/react-start/rsc'
const getPost = createServerFn().handler(async ({ data }) => {
const post = await db.posts.get(data.postId)
const src = await createCompositeComponent(
(props: {
children?: React.ReactNode
renderPostActions?: (data: {
postId: string
authorId: string
}) => React.ReactNode
}) => (
<article>
<h1>{post.title}</h1>
<p>{post.body}</p>
{/* 서버가 이 링크를 직접 렌더링합니다 */}
<Link to="/posts/$postId" params={{ postId: post.nextPostId }}>
Next Post
</Link>
{/* 슬롯: 서버가 여기서 클라이언트 UI를 요청합니다 */}
<footer>
{props.renderPostActions?.({
postId: post.id,
authorId: post.authorId,
})}
</footer>
{/* 슬롯: 클라이언트가 children으로 이것을 채웁니다 */}
{props.children}
</article>
),
)
return { src }
})
tsx
import { CompositeComponent } from '@tanstack/react-start/rsc'
function PostPage({ postId }) {
const { data } = useSuspenseQuery({
queryKey: ['post', postId],
queryFn: () => getPost({ data: { postId } }),
})
return (
<CompositeComponent
src={data.src}
renderPostActions={({ postId, authorId }) => (
// 완전한 클라이언트 상호작용: hooks, state, context
<PostActions postId={postId} authorId={authorId} />
)}
>
<Comments postId={postId} />
</CompositeComponent>
)
}
import { CompositeComponent } from '@tanstack/react-start/rsc'
function PostPage({ postId }) {
const { data } = useSuspenseQuery({
queryKey: ['post', postId],
queryFn: () => getPost({ data: { postId } }),
})
return (
<CompositeComponent
src={data.src}
renderPostActions={({ postId, authorId }) => (
// 완전한 클라이언트 상호작용: hooks, state, context
<PostActions postId={postId} authorId={authorId} />
)}
>
<Comments postId={postId} />
</CompositeComponent>
)
}
서버는 <Link>를 직접 렌더링하고 클라이언트를 위한 결합 지점을 남겨 둡니다.
슬롯 이름은 그저 props일 뿐입니다. renderPostActions는 예시 코드이지, 특별한 API 문법이 아닙니다.
Composite Component도 여전히 데이터이기 때문에, 클라이언트는 이것을 빌딩 블록처럼 다룰 수도 있습니다.
사고방식은 일반적인 React 조합과 같습니다. 차이는 서버가 더 이상 트리의 흥미로운 모든 부분을 미리 결정할 필요가 없다는 점입니다.
RSC 지원은 TanStack Start RC에서 실험적 기능이며, 초기 v1까지도 계속 실험적 상태로 남을 예정입니다.
직렬화: 이번 릴리스는 React의 기본 Flight 프로토콜을 사용합니다. 현재로서는 TanStack Start의 일반적인 직렬화 기능을 서버 컴포넌트 내부에서 사용할 수 없습니다.
API 표면: 현재의 헬퍼들은 사용하기에 충분히 안정적이지만, 기능이 실험적인 동안에는 다듬어질 여지가 있습니다. 문서는 API가 발전함에 따라 최신 상태를 유지할 것입니다.
거친 부분을 발견했다면 이슈를 열어 주세요 또는 Discord에 참여해 주세요.
질문을 많이 받습니다. 여기 답변이 있습니다.
Next.js App Router는 서버 우선입니다. 컴포넌트 트리가 기본적으로 서버에 있고, 'use client'를 통해 클라이언트 상호작용을 선택적으로 활성화합니다.
TanStack Start는 동형 우선입니다. 트리는 의미가 맞는 곳 어디에나 있을 수 있습니다. 가장 기본적인 수준에서 RSC 출력은 전체 트리를 소유하는 대신, 의미가 맞는 곳에서 가져오고, 캐시하고, 렌더링할 수 있습니다. 더 나아가고 싶다면 Composite Components를 통해 클라이언트가 서버 소유 트리를 단순히 받아들이는 대신 최종 트리를 조립할 수 있습니다.
직접적으로는 불가능합니다. TanStack Start는 자체 프레임워크입니다. 하지만 이미 TanStack Query나 Router를 사용하고 있다면, 그 사고방식은 그대로 옮겨갈 수 있습니다.
아니요. RSC는 완전히 선택 사항입니다. 완전한 클라이언트 측 라우트(ssr: false 포함)를 구축할 수도 있고, 서버 컴포넌트 없이 전통적인 SSR을 사용할 수도 있으며, 완전한 정적 방식으로 갈 수도 있습니다.
이것은 도구 상자의 또 하나의 도구일 뿐, 새롭고 의무적인 중심축이 아닙니다.
Server Components 문서를 보세요. 설정, 헬퍼 API, 예제, 제약 사항, 그리고 이 글에서 의도적으로 생략한 저수준 세부 사항까지 다룹니다.
위의 보안: 단방향 데이터 흐름 섹션을 참고하세요. 짧게 말하면, TanStack Start의 아키텍처는 클라이언트에서 Flight 데이터를 파싱하지 않으므로 다른 RSC 프레임워크에 영향을 준 최근 CVE는 여기에는 해당되지 않습니다.
우리는 이 글을 단순한 생각 하나로 시작했습니다. 여러분의 애플리케이션 아키텍처에 가장 적합한 것은 여러분이 가장 잘 안다는 것입니다. 그래서 TanStack Start의 RSC 모델도 규정적인 방식이 아니라 유연하게 유지되도록 설계했습니다.
생태계의 너무 많은 부분이 RSC를 앱 아키텍처 그 자체가 되어야 하는 것처럼 다룹니다. 우리는 그것이 원시 구성 요소로서 더 잘 작동한다고 생각합니다. 그리고 더 나아가고 싶을 때, Composite Components는 대부분의 RSC 시스템이 시도조차 하지 않는 조합 모델을 열어 줍니다. 완전히 인터랙티브한 SPA를 원하나요? 좋습니다. 무거운 작업을 위해 서버 컴포넌트를 조금씩 섞고 싶나요? 쉽습니다. 완전 정적으로 가고 싶나요? 그것도 가능합니다. 여러분의 앱은 획일적이지 않기 때문에, 아키텍처는 그 모든 것을 지원하고 프레임워크도 그래야 합니다.
TanStack Start의 RSC 모델은 지금 실험적 기능으로 사용할 수 있습니다. 여러분이 이것으로 무엇을 만들지 기대됩니다.
함께 놀라운 무언가를 만들어 봅시다.
StartRouterQueryTableFormDBAIIntent
React Server Components, 원하는 방식대로 Apr 13, 2026TanStack Router, Start, Query에서의 Solid 2.0 Beta 지원 Apr 10, 2026Code Mode: AI가 단순히 도구를 호출하는 것이 아니라 프로그램을 작성하게 하세요 Apr 8, 2026