이번에는 최상위 컴포넌트에서 데이터를 랜더링 시키고 하위 컴포넌트에서 이를 재사용할 수 없을까에 대한 궁긍즘으로 조사를 시작했습니다. 먼저 참교한 공식문서를 알려드리겠습니다.
https://github.com/kirill-konshin/next-redux-wrapper
기본적으로 next-redux-wrapper를 사용하였습니다.
코드
// pages/_app.tsx
// @ts-nocheck
import "../styles/globals.css";
import type { NextPage } from "next";
import type { AppProps } from "next/app";
import type { ReactElement, ReactNode } from "react";
import { wrapper } from "../modules";
import App from "next/app";
import { getUsers } from "../modules/user";
export type NextPageWithLayout = NextPage & {
getLayout?: (page: ReactElement) => ReactNode;
};
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
// const getLayout = Component.getLayout || ((page) => page);
// return getLayout(<Component {...pageProps} />);
console.log("pageProps >> ", pageProps);
return <Component {...pageProps} />;
}
MyApp.getInitialProps = wrapper.getInitialAppProps((store) => async (context) => {
const { req } = context.ctx;
await store.dispatch(getUsers());
return {
pageProps: {
...(await App.getInitialProps(context)).pageProps,
pathname: context.ctx.pathname,
userAgent: req ? req.headers["user-agent"] : "default user-agent",
},
};
});
export default wrapper.withRedux(MyApp);
// pages/test/index.tsx
// @ts-nocheck
import React from "react";
import { useSelector } from "react-redux";
import Link from "next/link";
import styles from "../../scss-styles/testPage.module.scss";
const Test = ({ pathname, userAgent, custom }) => {
const { users } = useSelector((state) => state.allUsersReducer);
return (
<div>
<pre>{JSON.stringify(users, null, 4)}</pre>
<hr />
<div className={styles.testOuterContainer}>
{pathname}
<br />
{userAgent}
<br />
{custom}
</div>
<hr />
<Link href="/clock">
<a>Clock</a>
</Link>
</div>
);
};
Test.getInitialProps = (context) => {
return {
custom: "custom",
};
};
export default Test;
공식문서에서는 이렇게 나와 있습니다.
You can dispatch actions from the pages/_app too. But this mode is not compatible with Next.js 9's Auto Partial Static Export feature, see the explantion below
제가 사용하는 방법은 Auto Partial Static Export라는 Next.js의 최적화 방식을 무시하는 방식으로 deprecated된 것 같습니다. 따라서 _app파일에서 dispatch하는 것이 아닌 getStaticProps, getServerSideProps를 이용하여 server-side나 build-time에서 pre-rendering해야 합니다.
우선 최적화를 생각하지 말고 코드를 짜보았습니다. 우선 _app에서 Next.js의 getInitialPorps를 사용하고 싶다면 MyApp의 정적 메서드에 wrapper.getInitialAppProps를 붙여주면 됩니다. 또한 여기서 중요한 점은
https://nextjs.org/docs/advanced-features/custom-app#caveats
Next.js공식문서에 나와 있다 싶이
- Adding a custom 'getInitialProps'in your 'App' will disable Automatic Static Optimization in pages without Static Generation
- When you add 'getInitialProps' in your custom app, you must 'import App from "next/app". call 'App.getInitialProps(appContext)' inside 'getInitialProps' and merge the returned object into the return value
- 'App' currently does not support Next.js Data Fetching methods like 'getStaticProps' or 'getServerSideProps'
즉 첫번째는 아까 말했다 싶이 최적화가 안된다는 점이고, 두번째는 next/app을 import해서 App.getInitialProps(context)를 return값으로 합쳐줘야 한다는 것이고, 세번째는 Next.js의 getStaticProps와 getServerSideProps를 사용하지 못한 다는 점입니다.
이 과정을 마쳤다면 _app컴포넌트에서 next-redux-wrapper를 이용하여 서버단에서 getUsers를 실행해서 비동기적으로 데이터 값을 받아 온 다음에 프리 렌더링 한 값을 HYDRATE해서 클라이언트의 redux로 getUsers의 반환값을 합칩니다.
또한 저는 이 외에도 context의 값을 이용해서 pageProps의 값으로 pathname과 userAgent의 값을 넘겨주었습니다.
이 pageProps로 넘겨준 값은 이제 pages에 해당하는 속성으로 받아올 수 있는데 Test컴포넌트의 props로 pathname과 userAgent를 받아와서 사용한 것을 볼 수 있습니다. 또한 useSelctor훅을 이용해서 server-side에서 hydrate된 값을 users에 할당했습니다. 그 외에도 pages마다 getInitialProps를 두어서 pre-rendering을 추가로 활용해 줄 수 있습니다.
이에 대한 결괏 값은 이러합니다.
페이지를 넘어다녀도 state가 원만히 유지되는 것을 확인해 볼 수 있습니다.
'Web > NextJs' 카테고리의 다른 글
[ Next.js - DeepDive ] -미들웨어 ( Middlewares ) (0) | 2022.08.04 |
---|---|
Next.js - _document와 _app에 대하여 (0) | 2022.03.30 |
Next.js - Dynamic Routes ( TODO ) (0) | 2022.03.03 |
Next.js - with Redux thunk (0) | 2022.03.01 |
Next.js - Static File Serving (0) | 2022.02.25 |