서버에서 Static하게 만든 페이지와 실제로 클라이언트에서 하이드레이션 한 뒤의 페이지가 보여주는 UI가 다를 때 생기는 문제입니다.
lazy하게 로딩해달라고 다이나믹하게 Import를 해주면 됩니다. 즉 다이나믹하게 Import하는 UI를 만들어주면 됩니다.
변경 전 코드
//PostList.tsx
"use client";
import useSWR from "swr";
import { SimplePost } from "@/model/post";
import { GridLoader } from "react-spinners";
import PostListCard from "@/components/PostListCard";
export default function PostList() {
const {
data: posts,
isLoading: loading,
error,
} = useSWR<SimplePost[]>("/api/post");
return (
<section>
{loading && (
<div className="text-center mt-32">
<GridLoader color="red" />
</div>
)}
{posts && (
<ul>
{posts &&
posts.map((post) => (
<li key={post.id} className="mb-4">
<PostListCard post={post} />
</li>
))}
</ul>
)}
</section>
);
}
변경 후 코드
//GridSpinner.tsx
import React from "react";
import dynamic from "next/dynamic";
const GridLoader = dynamic(
() => import("react-spinners").then((lib) => lib.GridLoader),
{
ssr: false,
},
);
type Props = {
color?: string;
};
export default function GridSpinner({ color }: Props) {
return <GridLoader color={color} />;
}
"use client";
import useSWR from "swr";
import { SimplePost } from "@/model/post";
import PostListCard from "@/components/PostListCard";
import GridSpinner from "@/components/ui/GridSpinner";
export default function PostList() {
const {
data: posts,
isLoading: loading,
} = useSWR<SimplePost[]>("/api/post");
return (
<section>
{loading && (
<div className="text-center mt-32">
<GridSpinner color="red" />
</div>
)}
{posts && (
<ul>
{posts &&
posts.map((post, index) => (
<li key={post.id} className="mb-4">
//priority 속성을 추가
<PostListCard post={post} priority={index < 2} />
</li>
))}
</ul>
)}
</section>
);
}
//PostListCard.tsx
type Props = {
post: SimplePost;
priority?: boolean; // 추가
};
export default function PostListCard({ post, priority = false }: Props) {
const { userImage, username, image, likes, text, createdAt } = post;
return (
<article className="rounded-lg shadow-md border border-gray-200">
<div className="flex items-center p-2">
<Avatar image={userImage} highlight size="medium" />
<span className="text-gray-900 font-bold ml-2">{username}</span>
</div>
<Image
className="w-full object-cover aspect-square"
src={image}
alt={`photo by ${username}`}
width={500}
height={500}
priority={priority} -> 추가
/>
<ActionBar
username={username}
createdAt={createdAt}
likes={likes}
text={text}
/>
<CommentForm />
</article>
);
}