이전포스팅들과 달리 포스팅이 짧을 것같다.
개념과,이해를 설명하는데 중점적으로 설명했으며, 이제는 그것들을 가지고 적용과 활용을 할 차례이기 때문이다.
const axiosInstance_SSR = axios.create({
baseURL,
});
/**
*
* @param ctx ServerSideProps를 사용할 때 호출하는 함수입니다. 각 로그인시 데이터 통신을 할 때 사용할 axios instance 입니다.
* @returns
*/
export const getAuthorizedAxios = async (ctx: GetServerSidePropsContext) => {
if (!isServer) return; // 클라이언트에서 실행하지 않음
if (axiosInstance_SSR.defaults.headers.Authorization || axiosInstance_SSR.defaults.headers.refreshToken) {
// axiosInstance_SSR의 default header안에 token이 있으면 그냥 return 합니다.
// 의미없는 비동기 통신안하려고 넣어놓은 조건문 입니다.
return axiosInstance_SSR;
}
const req = ctx.req;
try {
const session = await getToken({ req });
if (session) {
axiosInstance_SSR.defaults.headers.Authorization = `Bearer ${session.accessToken}`;
axiosInstance_SSR.defaults.headers.refreshToken = session.refreshToken;
}
} catch (error) {
console.error('Error fetching session:', error);
}
return axiosInstance_SSR;
};
위와 같이 따로 axios를 사용하여 SSR
에서 통신할 함수를 만들었다.
export default function App({ Component, pageProps: { session, ...pageProps } }: AppProps) {
return (
<SessionProvider session={session}>
<QueryClientProvider client={queryClient}>
<HydrationBoundary state={pageProps.dehydratedState}>
<main className={cn(myFont.variable)}>
<CommonLayout>
<Component {...pageProps} />
</CommonLayout>
</HydrationBoundary>
</QueryClientProvider>
</SessionProvider>
export const getServerSideProps: GetServerSideProps = async (ctx: GetServerSidePropsContext) => {
const queryClient = new QueryClient();
// accessToken, refreshToken을 탑재한 axios instance 입니다.
const axiosInstance = await getAuthorizedAxios(ctx);
if (axiosInstance)
try {
await queryClient.prefetchQuery({
// 본인이 사용할 페이지에서 SSR로 불러오고 싶은 queryKey를 넣어주세요,
queryKey: ['SCHEDULE_QUERY'],
queryFn: async () => {
const { data } = await axiosInstance.get('schedule');
return data;
},
});
} catch (err) {
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
if (!axiosInstance)
return {
redirect: {
destination: '/',
permanent: false,
},
};
return { props: { dehydratedState: dehydrate(queryClient) } };
};
// _app.tsx
우리 프로젝트에서 Calendar.tsx
에서 SSR을 해줘야 한다고 했어서 임시로 만들어줬다.
Next.js Page Router에서도 Session값을 SSR을 통해 ' 미리 ' 받을 수가 있다.
next-auth
를 사용했기에, next-auth에서 인증/인가 유지는 cookie
를 통해서 값을 받아와 세션 유지를 진행하므로,
페이지를 호출할 때 next-auth
의 메소드를 통해 server로부터 유저 세션을 받아올 수 있기 때문이다.
// index.tsx
const HomePage = () => { ... }
export const getServerSideProps = (async (ctx: GetServerSidePropsContext) => {
const session = await getSession(ctx);
// Pass data to the page via props
return { props: { session: session } };
}) satisfies GetServerSideProps<{ session: Session | null }>;
// _app.tsx
<SessionProvider session={session}> // Component 컴포넌트에 session 내려줌
<Component {...pageProps} />
//...생략
이렇게 하면, Server로부터 받아온 값을 <SessionProvider session={session}>
에 props로 전달 해줄 수 있기 때문이다.
그리고 Layout.tsx
에
const Layout = ()=> {
const session = useSession()
//...로직
}
해주면 pre-fetch를 받아오기에 useQuery
등 인가 통신에 필요한 값을 빠르게 받아올 수 있다. 그리고 새로고침시에도 깜빡임(조건부)도 사라지게 된다.
SSR 로직을 작성하면서
Next.js는 기본적으로 서버로부터 파일을 만들기에, sessionStorage
, localStorage
가 없다는 점을 다시 한번 알게 되었다.
뿐만 아닌, document
, window
와 같은 전역객체를 불러들이려면 useEffect
와같은 훅, 또는 server가 아닐 때의 조건부 로직으로 처리해야 한다는 점을 다시 한번 더 깨닫게 되었다.
hydration과 serializing, React-Query의 cache등 여러 가지를 배우면서
SWR
을 사용할 지 react-query
를 사용할 지를 고민 하는 것보단 react-query
의 전반적인 내용을 안 이후에 SWR
을 도전하는 것이 낫겠다라는 생각도 든다.
서버단에서 pre-fetch하여 react query와 같이 사용할 때 가장 문제가 되는 점은 캐싱이 되지 않는겁니다. 참고 바랍니다.
https://until.blog/@love/서버-컴포넌트와-tanstack-query-사용기