3.9 KiB
3.9 KiB
2. Rendering 흐름 & Lifecycle
SSR → Hydration → CSR 전환 흐름
Nuxt SSR은 세 단계로 구성된다.
[브라우저 요청]
↓
[서버] setup() 실행 → HTML 생성 → 클라이언트로 전송
↓
[브라우저] HTML 즉시 표시 (사용자에게 보임)
↓
[브라우저] Vue JS 로드 → Hydration (서버 HTML에 이벤트 연결)
↓
[브라우저] onMounted() 실행 → 완전한 SPA처럼 동작
Lifecycle 훅 실행 위치
| 훅 | 서버 | 클라이언트 | 설명 |
|---|---|---|---|
setup() |
✅ | ✅ | 컴포넌트 초기화, 양쪽 모두 실행 |
onServerPrefetch() |
✅ | ❌ | 서버에서만 실행, SSR용 데이터 페칭 |
onBeforeMount() |
❌ | ✅ | DOM 마운트 직전 |
onMounted() |
❌ | ✅ | DOM 마운트 완료, 브라우저 API 사용 가능 |
onBeforeUnmount() |
❌ | ✅ | 컴포넌트 제거 직전 |
onUnmounted() |
❌ | ✅ | 컴포넌트 제거 완료 |
실전 코드 예시
setup()에서의 서버/클라이언트 분기
<script setup>
// setup()은 서버와 클라이언트 양쪽에서 실행됨
const config = useRuntimeConfig()
// 서버/클라이언트 환경 확인
if (import.meta.server) {
console.log('서버에서만 출력')
}
if (import.meta.client) {
console.log('클라이언트에서만 출력')
}
</script>
onServerPrefetch — 서버에서 데이터 미리 준비
<script setup>
const data = ref(null)
// 서버에서 먼저 데이터를 가져오고, 클라이언트로 상태 전달
onServerPrefetch(async () => {
data.value = await $fetch('/api/products')
})
</script>
useAsyncData/useFetch가 내부적으로onServerPrefetch를 사용한다. 직접 쓸 일은 드물지만, 동작 원리 이해에 중요하다.
onMounted — 브라우저 전용 작업
<script setup>
// ❌ 잘못된 예: setup()에서 window 접근 → 서버에서 오류 발생
// const width = window.innerWidth
// ✅ 올바른 예: onMounted에서 브라우저 API 사용
const windowWidth = ref(0)
onMounted(() => {
windowWidth.value = window.innerWidth
// 이벤트 리스너, setTimeout, localStorage 등 모두 여기서
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
// 반드시 정리해야 메모리 누수를 막는다
window.removeEventListener('resize', handleResize)
})
</script>
Hydration이란?
서버가 생성한 정적 HTML에 Vue의 반응형 시스템과 이벤트 핸들러를 연결하는 과정이다.
서버 HTML: <button>클릭</button> ← 이벤트 없음
↓ Hydration
클라이언트: <button @click="handleClick">클릭</button> ← 이벤트 연결됨
Hydration 불일치(Mismatch) 주의
서버와 클라이언트에서 다른 결과를 렌더링하면 경고가 발생한다.
<script setup>
// ❌ 문제: 서버(undefined)와 클라이언트(실제값) 결과가 다름
const isClient = ref(typeof window !== 'undefined')
// ✅ 해결: ClientOnly 컴포넌트 사용
</script>
<template>
<!-- 브라우저에서만 렌더링해야 하는 컴포넌트 -->
<ClientOnly>
<BrowserOnlyComponent />
<template #fallback>
<p>로딩 중...</p>
</template>
</ClientOnly>
</template>
Nuxt 전용 훅 (useNuxtApp)
<script setup>
const nuxtApp = useNuxtApp()
// 페이지 전환 이벤트
nuxtApp.hook('page:start', () => {
console.log('페이지 전환 시작')
})
nuxtApp.hook('page:finish', () => {
console.log('페이지 전환 완료')
})
</script>
요약
서버 실행: setup() → onServerPrefetch()
↓
HTML 생성 및 전송
↓
클라이언트 수신: HTML 즉시 표시
↓
Hydration (JS 연결)
↓
setup() → onBeforeMount() → onMounted()
↓
이후 SPA처럼 동작