일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 자바스크립트 알고리즘
- 리액트
- lodash
- 자바스크립트 알고리즘 문제
- Next
- JS
- 자바스크립트 문제
- next13
- JavaScript
- HTML
- 타입스크립트
- Baekjoon
- stack문제
- 제로베이스
- NPM
- 자바스크립트 문제 풀이
- 자바스크립트코딩테스트
- Next.js13
- til
- 알고리즘문제풀이
- 리액트쿼리
- CSS
- 프로그래머스
- 프론트엔드
- leetcode문제풀이
- leetcode
- 자바스크립트 문제풀이
- 자바스크립트 연결리스트
- react
- 자바스크립트
- Today
- Total
코드노트
React-Query의 SSR 사용방법에 대한 고민 본문
이번 프로젝트에서 Next.js를 사용하기로 했다.
Next를 사용하기로 한 이유는?
- 현재 프로젝트는 생각보다 규모가 크다. 페이지도 많다. 간편하게 사용할 수 있는 next 라우팅을 사용하기로 했다.
- SEO 최적화, 프론트엔드에서 중요하게 작용하는! 메타데이터를 쉽게 설정할 수 있고, 페이지별로 지정할 수 있기 때문에 안쓸이유가 없었다.
- 리액트세계에만 있었던 사람이라면 Next세계로 들어오는 순간 다양한 렌더링 방식에서도 감탄을 하게 되는거 같다. 초기에 긴 로딩시간이 해결되지 않았다면 정적 파일을 제공하여 사이트의 성능도 높이고 로딩 속도도 높일 수 있다.
- 프레임워크임에도 React 기반으로 어려움없이 사용할 수 있다. 그외 등등...
아무튼!
이번 글에 주제인 React-Query를 사용하기로 기술스택을 고민하며 확정했다.
여기서 문제가 발생했다.
생각지도 못한 Next.js 13에서의 React-Query SSR구현...
- 찾고 찾아도 나오지않는 레퍼런스들.. 13이라 그런지 아직 Next.js 13 + React-query의 내용들이 많이 없었다. 있었지만 이해부족..
- gpt chat 똥멍청이다
- 가장 좋은건 역시나 공식문서!
- 강사님, 멘토님 공식문서를 보라는 이유가 있어... 아무리 찾아도 안나오는거 공식문서를 찾아보니 답이 있었다.
- 앞으로는 무적권 공식문서 -> 구글링 -> gpt선생님...
SSR
- React-Query는 서버에서 데이터를 미리 가져와 queryClient에 전달하는 2가지 방법을 지원한다.
props pre-fetch,
- 데이터를 직접 프리패치하며 초기값으로 전달한다.
- 쉽게 사용이 가능하지만 props drilling이 생긴다.
- 동일한 쿼리를 사용하여 여러 클라이언트 구성요소에 props 전달
- 페이지가 로드된 시점을 기준으로 리패치
hydrate pre-fetch
- 설정해야하는것들이 많다.
- props drilling없이 자식 컴포넌트에서 사용할 수 있다.
- 서버에서 프리패치된 시점을 기준으로 리패치
Hydrate
- next를 하면서도 알게되었지만 리액트쿼리에서도 사용된다.
- 서버 사이드 렌더링 시에 초기 데이터 상태를 클라이언트로 전달하여 클라이언트에서도 동일한 데이터를 사용할 수 있도록 해준다.
- 서버에서 직렬화된 상태를 전달받은 클라이언트에서 Hydrate 컴포넌트를 사용하여 해당 상태를 역직렬화하고 리액트쿼리의 queryClient를 초기화한다. 이를 통해서 클라이언트는 서버에서 초기 데이터를 사용할 수 있다.
* 리액트쿼리는 클라이언트 사이드 렌더링을 위한 라이브러리임을 잊지 말자.
- React-query 에서의 Hydrate 컴포넌트는 서버 사이드 렌더링과 클라이언트의 초기 데이터 전달을 효율적으로 관리하는데 사용!
props pre-fetch
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { PropsWithChildren, useState } from "react";
export default function ReactQueryProvider({ children }: PropsWithChildren) {
const [queryClient] = useState(() => new QueryClient());
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
}
- QueryClient 구성요소로 트리를 감싸고 QueryClientProvider 인스턴스를 전달
// app/layout.jsx
import ReactQueryProvider from "./ReactQueryProvider";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className={inter.className}>
<ReactQueryProvider>{children}</ReactQueryProvider>
</body>
</html>
);
}
- RootLayout 컴포넌트에서 body안에 children을 감싸준다.
import Post from "./Post";
import { getPosts } from "./api/post";
export default async function Home() {
const initialData = await getPosts();
return (
<>
{/* @ts-expect-error Async Server Component */}
<Post posts={initialData} />
</>
);
}
- initialData 초기값을 지정해준다.
- 초기값을 props로 내려준다.
"use client";
import React from "react";
import { getPosts } from "./api/post";
import { useQuery } from "@tanstack/react-query";
import PostList, { Post } from "./PostList";
async function Post(props: PostProps) {
const { data } = useQuery<Post[]>({
queryKey: ["posts"],
queryFn: getPosts,
initialData: props.posts,
});
return (
<div>
<PostList data={data} />
</div>
);
}
export default Post;
- 상위에 컴포넌트에서 초기 데이터를 받아와 useQuery에서 초기값을 props로 지정해준다.
import React from "react";
export interface Post {
id: number;
title: string;
body: string;
}
function PostList({ data }: { data: Post[] }) {
console.log("나는 서버");
return (
<div>
{data.map(post => (
<div key={post.id}>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
))}
</div>
);
}
export default PostList;
- props drilling이 발생하며, console.log도 클라이언트에서 사용되는걸 볼 수 있다.
- 초기값을 서버에서 불러오고 그 뒤로는 클라이언트에서 데이터가 리패치된다.( staleTime 등을 사용하여 리패치 조정 가능 )
hydrate pre-fetch
import { QueryClient } from "@tanstack/query-core";
import { cache } from "react";
const getQueryClient = cache(() => new QueryClient());
export default getQueryClient;
- 먼저 QueryClient 요청 범위 내에서 인스턴스를 만든다.
import React from "react";
import { getPosts } from "./api/post";
import { Hydrate, dehydrate } from "@tanstack/react-query";
import PostList, { Post } from "./PostList";
import getQueryClient from "./getQueryClient";
async function Post() {
const queryClient = getQueryClient();
await queryClient.prefetchQuery(["posts"], getPosts);
const dehydratedState = dehydrate(queryClient);
return (
<Hydrate state={dehydratedState}>
<PostList />
</Hydrate>
);
}
export default Post;
- getQuertClient함수를 통해 queryClient 인스턴스를 만든 후,
prefetchQuery로 미리 "posts" 쿼리키와 getPosts API를 요청하여 데이터를 불러온다.
- dehydrate함수를 통하여 queryClient를 직렬화 한다. (저장 가능한 형태로 변환하는 함수)
"use client";
import { useQuery } from "@tanstack/react-query";
import React from "react";
import { getPosts } from "./api/post";
function PostList() {
const { data } = useQuery({
queryKey: ["posts"],
queryFn: getPosts,
staleTime: 60000,
});
return (
<div>
{data?.map(post => (
<div key={post.id}>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
))}
</div>
);
}
export default PostList;
- 그 후 하위 컴포넌트에서 useQuery를 통해 미리 불러온 데이터를 사용하기 때문에 SSR로 사용가능해진다. ( staleTime 등을 사용하여 리패치 조정 가능
- props로 전달하지 않고 queryKey에 data가 저장되어있는걸 볼 수 있다.
이번 프로젝트에서 Next.js 13을 사용하면서 막히는부분들이 많이 있지만 새로운것들을 많이 알게되는거 같아서 재미있는거 같다.
이제 시작인데 이번 프로젝트를 끝날때쯤에는 조금 더 성장할 수 있기를...
'Code note > codenote' 카테고리의 다른 글
SWR에서 Optimistic UI 사용방법 기록 (0) | 2023.08.04 |
---|---|
CacheStorage 사용 기록, 라이브러리 없이 HTTP 캐싱 정리 (0) | 2023.07.19 |
Next.js v.13 기능 정리 1 (2) | 2023.05.24 |
Next.js 에 대해서... 개념정리 , feat. CSR, SSG, ISR, SSR (0) | 2023.05.21 |
React-Query와 Zustand의 차이, 사용하는 목적 정리 (0) | 2023.05.10 |