Untitled

문제원인

서버에서 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>
  );
}