🖍️ 서버 컴포넌트 자세히 알아보자 1.0
렌더링
Markup 이 완료되면 사용자는 컨텐츠를 볼 수 있음
Hydration
Dehydration & Rehydration
📰 CODE 예시
- Next.js 13 이후 (APP Routing)
- useQuery 사용 전제
- Typescript 사용 전제
1단계, Server Component 설정
import {fetchPosts} from "@/app/services/PostService";
import PostList from "@/app/components/PostList";
import { dehydrate, Hydrate, QueryClient } from "@tanstack/react-query";
import { ReactNode } from "react";
export const revalidate = 0;
async function getDehydratedState() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery('post', fetchPosts);
return dehydrate(queryClient);
}
export default async function PostsPage() {
const dehydrateState = await getDehydratedState();
return (
<Hydrate state={dehydrateState}>
<PostList />
</Hydrate>
);
}
2단계, useQuery 를 사용한 Client Component
'use client';
import React from 'react';
import { useQuery } from '@tanstack/react-query';
import { fetchPosts } from '@/app/services/PostService';
import styled from 'styled-components';
interface Post {
id: number;
title: string;
body: string;
}
const PostList: React.FC = () => {
const {data: posts, isLoading, error} = useQuery<Post[]>('posts', fetchPosts);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Rrror loading posts</div>;
return (
<List>
{posts?.map(post => (
<ListItem key={post.id}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</ListItem>
))}
</List>
);
};
export default PostList;
const List = styled.ul`
list-style-type: none;
padding: 0;
`;
const ListItem = styled.li`
padding: 10px;
border-bottom: 1px solid #ccc;
h2 {
margin: 0;
}
`;
📢 SSR 의 Hydration ERROR
- 서버에서 렌더링된 HTML 과 클라이언트에서 렌더링된 HTML 불일치
- 서버와 클라이언트 간의 렌더링 논리 차이
- 동적 데이터 가져오기와 관련된 타이밍 문제 등
디버깅 (원인)
- 서버와 클라이언트의 타이밍 문제 (HTML 불일치, 원인)
- 서버와 클라이언트에서 렌더링에 사용되는 상태 불일치
- 서버와 클라이언트에서 조건부로 다르게 렌더링되는 컴포넌트