데이터 연동하기
시리즈 1편 과 시리즈 2편 에서 미리 데이터 연동에 대한 준비를 했었다. 서버는 준비되어 있는 상태이고 클라이언트와 서버 사이에 연결만 해주면 된다. 이 부분은
react-query
를 이용할 생각이다.시리즈 3편 이후로 따로 포스트에는 적지 않았지만 꽤 많은 부분이 이미 개발되었다. 예를들어 리덕스 모듈이나 스타일 테마 등 사실 이전에 블로그에 꽤 포스팅을 했었고, 다른 블로그에서 더 상세한 가이드가 많다. 어찌됐든 이 포스트는 가이드가 아니라 개발 일지 일뿐이니 생략하려고 한다. 나중에 기회가 되면 스타일 테마는 따로 포스트로 적어도 괜찮을것 같다.
react-query
yarn add react-query
최근에 클라이언트와 서버사이에 안정적인 통신을 하기 위해 쓰는 꽤 핫한 라이브러리이다.
유저는 클라이언트와 서버 사이를 연결하는 일종의 규칙? 정도만 작성해주면 실제로 필요한 많은 부분들을 담당해준다. 예를들어 비동기처리나 에러핸들링이나 이러한 부분을 정의만 해주면 알아서 처리해주기 때문에 꽤 많은 사람들이 이용하는듯 하다.
이 포스트에서는 아주 기본적인 내용만 적으려고 한다. 실제 코드는 약간 더 복잡할 수 있지만 이 블로그 깃헙에서 확인하길…
// pages/_app.tsx const queryClient = new QueryClient({ defaultOptions: { queries: { refetchOnMount: false, refetchOnReconnect: false, refetchOnWindowFocus: false // onError: handlerError }, mutations: { // onError: handlerError } } }); export default function App({ Component, pageProps }: AppProps) { return ( <Provider store={store}> <PersistGate persistor={persistor} loading={null}> <QueryClientProvider client={queryClient}> <ThemeProvider theme={theme}> <DefaultLayout> <LayoutInner> <Component {...pageProps} /> </LayoutInner> </DefaultLayout> </ThemeProvider> </QueryClientProvider> </PersistGate> </Provider> ); }
지난번 코드와 비교하면 꽤나 많은 부분이 변했지만 생략한다.
react-query
를 사용하기 위해서는 일단 QueryClientProvider
로 앱을 한번 래핑해준다.api 선언하기
// core/api/posts.ts import { PageObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import { request } from './'; export const postApis = { getPosts: (params = {}) => request<PageObjectResponse[]>({ url: '/posts', method: 'GET', params }) };
core
폴더에 api
에 posts api를 작성해준다. 이것도 비약적으로 생략되긴 했지만, axios
를 이용하여 선언해둔 모습이다. request
함수는 자신의 입맛에 맞게 커스텀이 가능하다. 단지 return 이 axios request 일 뿐이다.query 작성하기
일반적으로
react-query
의 가장 좋은 이용사례는 api마다 커스텀훅을 만들어 사용하는것이다. 일반적으로 react-query
는 쿼리 키 라는 개념이 있는데, 이 쿼리 키로 react-query
의 데이터의 유효성을 조절할 수 있다. 데이터의 유효성이 사라지면 react-query
는 기본적으로 데이터를 다시 새롭게 가져온다.// core/queries/posts.ts import { useQuery, useQueryClient, UseQueryOptions } from 'react-query'; import { postApis } from '../apis/posts'; import type { PageObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import type { Response } from '../../interfaces'; export const postsQueryKey = { posts: () => ['posts'] as const }; export const useGetPosts = ( options?: UseQueryOptions<Response<PageObjectResponse[]>> ) => { return useQuery( postsQueryKey.posts(), () => postApis.getPosts(), options as any ); };
일단은 이렇게 커스텀 훅을 만들었다.
// components/Posts/Posts.tsx import { PageObjectResponse } from '@notionhq/client/build/src/api-endpoints'; import { useGetPosts } from '../../core/queries/posts'; import type { Response } from '../../interfaces'; interface IPostsProps { initialPosts: Response<PageObjectResponse[]>; } const Posts: React.FC<IPostsProps> = ({ initialPosts }) => { const { data } = useGetPosts({ initialData: initialPosts }); return ( <div className="posts-wrapper"> <ul>{}</ul> </div> ); }; export default Posts;
이런식으로 위에서 정의한
useGetPosts
를 사용한다. 여기서 주의할 점이 있는데 initialPosts
라는 데이터이다. 만든 커스텀훅에 옵션으로 initialData
에 넣어주게 되어있는데, 나는 블로그로써의 기능을 하기 위해 SSR을 지원해야 하기 때문에 서버단계에서 이미 페이지가 생성된다. 그 때 필요한 데이터가 바로 initialData
이다.SSR 설정하기
// pages/index.tsx import Home from '../components/Home'; import { postApis } from '../core/apis/posts'; import type { Response } from '../interfaces'; import type { PageObjectResponse } from '@notionhq/client/build/src/api-endpoints'; export default function ({ data }: { data: Response<PageObjectResponse[]> }) { return <Home initialPosts={data} />; } export const getServerSideProps = async () => { const res = await postApis.getPosts(); return { props: { data: res } }; };
홈 화면에서 포스트를 바로 보여줄 것이기에
index.tsx
파일에서 작업한다.아래쪽에 있는
getServerSideProps
를 이용하면 서버사이드에 필요한 데이터를 미리 가져올 수 있다. 사실 이 부분에서 에러처리나 404페이지로 리다이렉트 등 기능을 추가할 수 있다. 이 부분은 나중에 개발이 완료되고 나서 안정화 시킬때 하면 될듯?주의사항으로
getServerSideProps
에서 fetch 하는 url은 항상 절대주소를 이용해야 한다. 이 코드는 서버사이드에서 실행되는데 상대주소를 이용하면 이것 때문인지 사용할 수 없다고 에러가 발생한다.마무리
SSR은 어렵다 항상 CSR만 개발하다보니 생각치도 못한곳에서 에러가 발생한다. 나중에 이런 시행착오도 적어두면 도움이 되려나???