[Next.js] Next.js 학습

김민석·2025년 10월 26일
0
post-thumbnail

인피니티 스크롤

서버에서 받아온 데이터를 패칭 React-query 사용하여 인피니티 스크롤
npm i @tanstack/react-query@5
npm i @tanstack/react-query-devtools@5 -D
react-query와 devtool을 설치해주고 Provider를 만든다.
Provider는 children을 통해 감싸주어 그 안에서 react-query를 공유하고 사용할 수 있게 할 수 있다.

revalidateTag,revalidatePath

서버액션과 라우트 핸들러에서만 사용가능
cache:no-store가 기본값 force-cache하면 캐시함

Request Memoization

같은 주소에 fetch가 여러번 있을떄 한번만 실행하게 해줌 (한명이 여러번)
처음 값을 Request Memoization에 저장한 후 다음에 재사용함.

Data Cache

여러 사람이 같은 주소로 요청햇을 때 백엔드에 한번만 요청을 보내게 캐싱하는 것

ReactQuery

데이터를 가져오는 것 캐싱을 하기 좋음.
캐싱 : 한번 가져온 데이터를 DB에서 다시 가져오지 않고 캐시에서 가져옴.
로딩 성공 실패를 쉽게 구현이 가능함.
querykey 구조를 잘 갖춰 놓으면 갱신 및 관리가 편함.

  • fresh : 캐시에 들어있는 상태
  • stale : 상한 상태 다시 데이터를 서버(DB)에서 불러와야함.
    fresh설정을 안해주면 stale이됨.
  • Inactive 해당 화면에서 데이터를 사용하고 있지 않은것들
function RQProvider({ children }: Props) {
  const [client] = useState(
    new QueryClient({
      defaultOptions: {
        // react-query 전역 설정(옵션)
        queries: {
          refetchOnWindowFocus: false, //다른 창 옮겨갔다가 오면 
          retryOnMount: true, // 컴포넌트가 마운트 될 시에 
          refetchOnReconnect: false,
          retry: false, // 다시 시도 
        },
      },
    })
  );

  return (
    <QueryClientProvider client={client}>
      {children}
      <ReactQueryDevtools
        initialIsOpen={process.env.NEXT_PUBLIC_MODE === "local"}
      />
    </QueryClientProvider>
  );
}

export default RQProvider;

QueryClientProvider로 children을 감싸 RQProvider가 감싸고 있는 layout 및 page는 모두 reactquery를 사용할 수 있게 하는 코드이다.

GCTIme(캐시타임) : 5분이 기본값. Inactive 일떄 GC타임이 돌아감.
GCTIme은 staleTime보다 무조건 커야함. Inactive이면 GC타임이 흘러가는데
Stale타임이 더 크면 GC타임이 더 작다면 Stale타임이 아직 다 흐르지 않았는데 Inactive Query가 사라지면 Stale타임의 의미가 없어짐.

enabled 속성

ReactQuery에는 enabled라는 속성이 존재하여 조건을 줄 수 있다.
이 코드는 현재 useSession을 사용해 유저가 로그인 상태가 아니면 data를 불러오지 않는 것

const { data: session } = useSession();
const { data } = useQuery<Hashtag[]>({
    queryKey: ["trends"],
    queryFn: getTrend,
    staleTime: 60000,
    enabled: !!session?.user,
  });

Error,loading 처리

 const {
    data: user,
    error,
    isLoading,
  } = useQuery<User, object, User, [_1: string, _2: string]>({
    queryKey: ["users", username],
    queryFn: getUser,
    staleTime: 60000,
  });
  if (!user) {
    return null;
  }
  if (error) {
    return (
      <>
        <div className="flex gap-10 items-center">
          <ArrowLeftCircle className="w-8 h-8" />
          <h3 className="text-xl font-semibold">{user.nickname}</h3>
        </div>
        <div className="flex justify-between items-center">
          <Image src={""} alt={user.nickname} width={150} height={150} />
          <div className="flex-1">
            <p className="text-lg font-semibold">{user.id}</p>
            <p>@{user.nickname}</p>
          </div>
          <div className="text-2xl">계정이 존재하지 않음.</div>
        </div>
      </>
    );
  }

UseQuery에는 error 속성과 isLoading 속성이 있어 error 혹은 loading 상태일때 다른 값을 보여줄 수 있다. 혹은 error loading 처리

ReactQuery 값 공유

"use client";

import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Post as IPost } from "@/model/Post";
import Post from "../../_components/Post";
import { getUserPosts } from "../_lib/getUserPosts";

export default function UserPosts({ username }: { username: string }) {
  const { data } = useQuery<
    IPost[],
    object,
    IPost[],
    [_1: string, _2: string, _3: string]
  >({
    queryKey: ["posts", "users", username],
    queryFn: getUserPosts,
    staleTime: 60000,
  });
  const queryClient = useQueryClient();
  const user = queryClient.getQueryData(["users", username]);
  if (user) return data?.map((post) => <Post key={post.postId} post={post} />);
}

1) useQueryClient()로 전역 QueryClient 인스턴스를 가져옴.
2) 같은 queryKey(["users", username])로 이미 캐싱된 사용자 데이터를 getQueryData()로 동기 조회한 후 네트워크 요청 없이 즉시 반환됨.
3) 캐시에 없으면 undefined가 오므로 분기 처리.
4) 동일한 queryKey를 쓰는 모든 컴포넌트가 캐시를 공유함.

  • 캐시를 갱신하고 싶으면 invalidateQueries(["users", username])를 호출하면 이 캐시가 무효화되고 재요청됨.

0개의 댓글