개발을 하다 보면 하이드레이션 문제를 마주칠 수 있습니다. 이러한 경고를 무시하지 마세요.
하이드레이션 불일치는 단순한 경고가 아니라, 애플리케이션을 망가뜨릴 수 있는 심각한 문제의 신호입니다.
개발 중 브라우저 콘솔에서 Vue가 하이드레이션 불일치 경고를 로그로 남깁니다:

문제: 서버 사이드 렌더링 중에 브라우저 전용 API를 사용하는 경우.
<template>
<div>User preference: {{ userTheme }}</div>
</template>
<script setup>
// 이것은 하이드레이션 불일치를 일으킵니다!
// localStorage는 서버에 존재하지 않습니다!
const userTheme = localStorage.getItem('theme') || 'light'
</script>
해결책: useCookie를 사용할 수 있습니다:
<template>
<div>User preference: {{ userTheme }}</div>
</template>
<script setup>
// 이것은 서버와 클라이언트 모두에서 동작합니다
const userTheme = useCookie('theme', { default: () => 'light' })
</script>
문제: 서버와 클라이언트 간의 서로 다른 데이터.
<template>
<div>{{ Math.random() }}</div>
</template>
해결책: SSR 친화적인 상태를 사용하세요:
<template>
<div>{{ state }}</div>
</template>
<script setup>
const state = useState('random', () => Math.random())
</script>
문제: SSR 중에 클라이언트 전용 조건을 사용하는 경우.
<template>
<div v-if="window?.innerWidth > 768">
Desktop content
</div>
</template>
해결책: 미디어 쿼리를 사용하거나 클라이언트 측에서 처리하세요:
<template>
<div class="responsive-content">
<div class="hidden md:block">Desktop content</div>
<div class="md:hidden">Mobile content</div>
</div>
</template>
문제: DOM을 수정하거나 브라우저 의존성이 있는 라이브러리(태그 매니저에서 이런 일이 매우 자주 발생합니다).
<script setup>
if (import.meta.client) {
const { default: SomeBrowserLibrary } = await import('browser-only-lib')
SomeBrowserLibrary.init()
}
</script>
해결책: 하이드레이션이 완료된 후 라이브러리를 초기화하세요:
<script setup>
onMounted(async () => {
const { default: SomeBrowserLibrary } = await import('browser-only-lib')
SomeBrowserLibrary.init()
})
</script>
문제: 현재 시간에 따라 변경되는 콘텐츠.
<template>
<div>{{ greeting }}</div>
</template>
<script setup>
const hour = new Date().getHours()
const greeting = hour < 12 ? 'Good morning' : 'Good afternoon'
</script>
해결책: NuxtTime 컴포넌트를 사용하거나 클라이언트 측에서 처리하세요:
<template>
<div>
<NuxtTime :date="new Date()" format="HH:mm" />
</div>
</template>
<template>
<div>
<ClientOnly>
{{ greeting }}
<template #fallback>
Hello!
</template>
</ClientOnly>
</div>
</template>
<script setup>
const greeting = ref('Hello!')
onMounted(() => {
const hour = new Date().getHours()
greeting.value = hour < 12 ? 'Good morning' : 'Good afternoon'
})
</script>
useFetch, useAsyncData, useStateClientOnly 컴포넌트를 사용하세요onMounted로 옮기세요