본문으로 건너뛰기

하이드레이션 오류

발생하는 이유

  • 불일치: 하이드레이션 중 서버 HTML이 클라이언트 렌더링 결과와 다릅니다
  • 일반적인 원인: Intl(로케일/시간대), Date.now(), 무작위 ID, 반응형 전용 로직, 기능 플래그, 사용자 환경설정

전략 1 — 서버와 클라이언트를 일치시킵니다

  • 서버에서 결정론적인 로케일/시간대를 선택하고 클라이언트에서도 동일하게 사용합니다
  • 신뢰할 수 있는 원본: 쿠키(권장) 또는 Accept-Language 헤더
  • 서버에서 한 번만 계산하고 초기 상태로 하이드레이션합니다
// src/start.ts
import { createStart, createMiddleware } from '@tanstack/solid-start'
import {
getRequestHeader,
getCookie,
setCookie,
} from '@tanstack/solid-start/server'

const localeTzMiddleware = createMiddleware().server(async ({ next }) => {
const header = getRequestHeader('accept-language')
const headerLocale = header?.split(',')[0] || 'en-US'
const cookieLocale = getCookie('locale')
const cookieTz = getCookie('tz') // set by client later (see Strategy 2)

const locale = cookieLocale || headerLocale
const timeZone = cookieTz || 'UTC'

setCookie('locale', locale, { path: '/', maxAge: 60 * 60 * 24 * 365 })

return next({ context: { locale, timeZone } })
})

export const startInstance = createStart(() => ({
requestMiddleware: [localeTzMiddleware],
}))
// src/routes/index.tsx (example)
import { createFileRoute } from '@tanstack/solid-router'
import { createServerFn } from '@tanstack/solid-start'
import { getCookie } from '@tanstack/solid-start/server'

export const getServerNow = createServerFn().handler(async () => {
const locale = getCookie('locale') || 'en-US'
const timeZone = getCookie('tz') || 'UTC'
return new Intl.DateTimeFormat(locale, {
dateStyle: 'medium',
timeStyle: 'short',
timeZone,
}).format(new Date())
})

export const Route = createFileRoute('/')({
loader: () => getServerNow(),
component: () => {
const serverNow = Route.useLoaderData() as string
return <time dateTime={serverNow}>{serverNow}</time>
},
})

전략 2 — 클라이언트가 환경을 알려주도록 합니다

  • 첫 방문 시 클라이언트 시간대가 포함된 쿠키를 설정하며, 그전까지 SSR은 UTC을 사용합니다
import { ClientOnly } from '@tanstack/solid-router'

function SetTimeZoneCookie() {
if (typeof window !== 'undefined') {
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone
document.cookie = `tz=${tz}; path=/; max-age=31536000`
}
return null
}

;<ClientOnly fallback={null}>
<SetTimeZoneCookie />
</ClientOnly>

전략 3 — 클라이언트 전용으로 만듭니다

  • SSR/하이드레이션을 피하려면 <ClientOnly> 또는 Solid의 <NoHydration>을 사용합니다
import { ClientOnly } from '@tanstack/solid-router'
import { NoHydration } from 'solid-js/web'

<ClientOnly fallback={<span></span>}>
<RelativeTime ts={someTs} />
</ClientOnly>

<NoHydration>
<time>{new Date().toLocaleString()}</time>
</NoHydration>

전략 4 — 라우트의 SSR을 비활성화하거나 제한합니다

  • 서버에서 컴포넌트를 렌더링하지 않으려면 선택적 SSR을 사용합니다
export const Route = createFileRoute('/unstable')({
ssr: 'data-only', // or false
component: () => <ExpensiveViz />,
})

전략 5 — 최후의 수단으로 억제합니다

  • 위 도구를 우선 사용하고, 불일치를 숨기기보다 방지합니다

체크리스트

  • 결정론적 입력: 로케일, 시간대, 기능 플래그
  • 클라이언트 컨텍스트에는 쿠키를 우선 사용하고, Accept-Language을 대안으로 사용합니다
  • 본질적으로 동적인 UI에는 <ClientOnly>/<NoHydration>을 사용합니다
  • 서버 HTML을 안정적으로 유지할 수 없으면 선택적 SSR을 사용합니다

함께 보기: 실행 모델, 코드 실행 패턴, 선택적 SSR, 서버 함수