반응형
_app.js, _document.js
이 파일들은 pages폴더에 위치한 Next.JS에서 제공하는 로직을 override하기 위해 필요한 파일들이다. Next.JS에서 제공해 주는 기능만 사용한다면 생성하지 않아도 된다. 하지만 실제로 개발하다 보면 100% 커스텀해야 한다.
_app.js
- _app은 모든 페이지에 레이아웃 형태로 항상 적용되어 있다.
- 페이지 이동 시에 상태가 변하지 않는다.
- 필요한 초기 데이터를 getInitialProps에서 받아 컴포넌트들에 공유할 수 있다.
- global css를 추가하기 좋은 파일이다.
// _app.ts
const App = ({Component, pageProps}: AppProps) => {
return <Component {...pageProps} />}
}
export default App;
- 위 코드에서 Component는 활성 중인 page다. 그래서 route간 이동할 때마다 Component는 항상 새 페이지로 교체된다.
- pageProps는 `App.getInitialProps`를 이용하여 prefetching된 데이터를 받을 수 있다. 데이터가 없다면 빈 객체({})가 내려온다.
주의점
- _app 에서는 Next.JS의 getStaticProps, getServerSideProps와 같은 Data Fetching methods가 동작하지 않는다.
- server only file이며, Next.JS Server에서 사용함으로 Client에서 사용하는 로직 (eventListener의 window/ DOM로직)을 사용하면 안 된다. (window is not defined 에러 발생)
- 만약 getInitialProps를 _app에 사용하면 SSG의 효과가 없어 모든 페이지에 Automatic Static Optimization를 할 수 없다.
- 만약 _app에서 getInitialProps를 사용해야 한다면, `next/app`에서 App객체의 InitialProps를 통해 데이터를 가져와야 한다.
실제 사용 예제
getInitialProps를 이용하여 _app에서 특정 api를 호출 후, index.ts에 그 결과 값을 전달하는 로직을 만들어보자.
// _app.ts
interface DogProps {
message: string,
status: string
}
const CustomApp = ({Component, pageProps}: AppProps) => {
return <Component {...pageProps} />
}
CustomApp.getInitialProps = async (appContext: AppContext) => {
// 여기서의 App은 next/app 내의 App
const appProps = await App.getInitialProps(appContext);
let response = await fetch('https://dog.ceo/api/breeds/image/random');
let dog: DogProps = await response.json()
return {pageProps: dog}
}
export default CustomApp;
// index.ts
interface DogProps {
message: string,
status: string
}
const Home = (dog: DogProps) => { // _app.ts에서 내려받은 dog값
return (
<div>
{dog.status !== "error" && <img src={dog.message} alt="강아지 사진"/>}
{dog.status === "error" && <div>에러 발생!</div>}
</div>
)
}
export default Home;
결과
_document.js
- _document는 `html`과 `body` 태그를 커스텀하게 만들 수 있다.
- `,` ``, `
` 그리고 ``는 적절한 렌더링을 위해 꼭 필요한 태그들이다.
const Document = () => {
return (
<Html lang="en"> // html 태그 커스텀
<Head/>
<body className="bg-white"> // body 태그 커스텀
<Main/>
<NextScript/>
</body>
</Html>
)
}
export default Document;
주의점
- _app 에서는 Next.JS의 getStaticProps, getServerSideProps와 같은 Data Fetching methods가 동작하지 않는다.
- server only file이며, NextJS Server에서 사용함으로 Client에서 사용하는 로직 (eventListener의 window/ DOM로직)을 사용하면 안 된다. (window is not defined 에러 발생)
- _document의 `` 컴포넌트는 `next/head` 파일 내의 ``컴포넌트와 다르다. `next/document` 파일에서 import 한 것이며, 이는 `` 내의 태그들은 모든 페이지에서 적용될 태그들만 사용해야 한다. 만약 `태그와 같이 페이지마다 다른 값이 보이려면 `next/head`의 ``를 사용해야 한다.
- `
` 외에는 브라우저에서 실행되지 않으므로 비즈니스 로직이 있으면 안 된다.
커스텀 renderPage
이는 주로 CSS-in-JS (ex: emotion, styled-component)가 SSR에서 적용되도록 하기 위해서 사용한다.
class MyDocument extends Document {
static async getInitialProps(ctx) {
const originalRenderPage = ctx.renderPage
// 리액트 렌더링 로직
ctx.renderPage = () =>
originalRenderPage({
// 애플리케이션 전체에 적용하기 위한 로직 (_app)
enhanceApp: (App) => App,
// 각 페이지에 적용하기 위한 로직
enhanceComponent: (Component) => Component,
})
// 부모 getInitialProps 실행. 위의 커스텀된 renderPage가 포함되어있다.
const initialProps = await Document.getInitialProps(ctx)
return initialProps
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
References
Next.JS 공식 홈페이지 - https://nextjs.org/docs/advanced-features/custom-app
반응형