Next.js에는 두 가지 중요한 기본 페이지가 있습니다. 뱌로 _document와 _app입니다.
_document와 _app은 페이지에 공통적으로 적용될 내용을 작성하곤 하는데, 둘이 정확히 어떻게 다른지와 어떤 내용을 작성해야 하는지에 대해 정리해 보겠습니다.
App페이지
_app은 서버로 요청이 들어왔을 때 가장 먼저 실행되는 컴포넌트로, 페이지에 적용할 공통 레이아웃의 역할을 합니다.
규칙
1. Component 속성값은 서버에 요청한 페이지가 됩니다.
(Ex http://localhost:3000/home에 접속하면, Component는 home컴포넌트를 가리킵니다.)
2. pageProps는 getInitialProps, getStaticProps, getServerSideProps중 하나를 통해 페칭한 초기 속성값이 됩니다.
pages/users2/index.js
import axios from "axios";
import React from "react";
const Users = ({ users }) => {
return (
<div>
<ul>
{users.map((user) => {
return <li key={user.id}>{user.name}</li>;
})}
</ul>
</div>
);
};
Users.getInitialProps = async (ctx) => {
console.log("getInitialProps ctx >> ", ctx);
const { data } = await axios.get("https://jsonplaceholder.typicode.com/users");
return { users: data };
};
export default Users;
만약 이렇게 user페이지를 작성했다면
pageProps >> {
users: [
{
id: 1,
name: 'Leanne Graham',
username: 'Bret',
email: 'Sincere@april.biz',
address: [Object],
phone: '1-770-736-8031 x56442',
website: 'hildegard.org',
company: [Object]
},
{
id: 2,
name: 'Ervin Howell',
username: 'Antonette',
email: 'Shanna@melissa.tv',
address: [Object],
phone: '010-692-6593 x09125',
website: 'anastasia.net',
company: [Object]
},
...
{
id: 9,
name: 'Glenna Reichert',
username: 'Delphine',
email: 'Chaim_McDermott@dana.io',
address: [Object],
phone: '(775)976-6794 x41206',
website: 'conrad.com',
company: [Object]
},
{
id: 10,
name: 'Clementina DuBuque',
username: 'Moriah.Stanton',
email: 'Rey.Padberg@karina.biz',
address: [Object],
phone: '024-648-3804',
website: 'ambrose.net',
company: [Object]
}
]
}
와 같이 prefetching된 데이터가 들어있을 것입니다.
또한 당연히 되짚고 넘어가자면 getInitialProps는 서버단에서 실행되는 것이므로 ctx에는 pathname, query, asPath, req, res, err등 다양한 서버단의 중요한 값들이 넘어오게 됩니다.
3. _app에서도 getInitialProps를 사용해 모든 페이지에서 사용할 공통 속성값을 지정할 수 있으나, 이럴 경우 자동 정적 최적화가 비활성화되어 모든 페에지가 서버 사이드 렌더링을 통해 제공됩니다.
import App from 'next/app'
function app({ Component, pageProps }) {
return <Component {...pageProps} />
}
app.getInitialProps = async (appContext) => {
const appProps = await App.getInitialProps(appContext);
return { ...appProps }
}
다음과 같이 _app에서 getInitialProps를 사용하고자 한다면, 꼭 위처럼 App객체를 불러온 다음에 getInitialProps를 통해 데이터를 불러와야 합니다.
또한 한 페이지를 로드할 때, 하나의 getInitialProps로직만 실행됩니다. 예를 들어 _app.js에 getInitialProps를 달아서 사용한다면 그 하부 페이지의 getInitialProps는 실행되지 않습니다. 다만 아래와 같이 커스터마이징을 하면 최종 결과를 pageProps에 담을 수 있습니다.
export default class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
let pageProps = {};
// 실행하고자 하는 component에 getInitialprops가 있으면 실행하여 props를 받아올 수 있다.
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return {
pageProps
};
}
render() {
const { Component, pageProps, router } = this.props;
return (
<div>
<Component {...pageProps} />
</div>
);
}
};
하지만 Next 9.3버전에서는 getInitialProps를 대신에 getStaticProps, getStaticPaths, getServerSideProps를 사용하게 됩니다. 서버에서 페이지 연산을 미리 한다는 점은 동일하므로 제 블로그의 다른 글들을 보시는 것을 추천합니다.
document페이지
_document는 _app다음에 실행되며, 공통적으로 활용할 <head>(Ex. 메타 태그)나 <body>태그 안에 들어갈 내용들을 커스텀할 때 활용합니다.
주요 사용 목적
- 폰트 임포트
- charset, 웹 접근성 관련 태그 설정
// 기본적인 _document의 형태
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
규칙
1. _document를 작성할 때는 Document클래스를 상속받는 클래스형 컴포넌트로 작성해야만 하며, 렌더 함수는 꼭 <Html>, <Head>, <Main>, <NextScript>요소를 리턴해 주어야 합니다.
2. _document에서 사용하는 <Head>태그는 next/head가 아닌 next/document모듈에서 불러와야 하는데, _document의 <Head>태그에는 모든 문서에 공통적으로 적용될 내용이 들어가야 합니다.
3. _document는 언제나 서버에서 실행되므로 브라우저 api또는 이벤트 핸들러가 포함된 코드는 실행되지 않습니다. 또한 <Main/>부분을 제외한 부분은 브라우저에서 실행되지 않으므로 이곳에 비지니스 로직을 추가해서는 안되며, _app과 마찬가지로 getStaticProps와 getServerSideProps를 통해 데이터를 불러올 수 없습니다.
'Web > NextJs' 카테고리의 다른 글
[ Next.js - DeepDive ] - Responses and Redirections (0) | 2022.08.04 |
---|---|
[ Next.js - DeepDive ] -미들웨어 ( Middlewares ) (0) | 2022.08.04 |
Next.js - Advanced Custom app + next-redux-wrapper를 활용한 data pre-rendering (0) | 2022.03.03 |
Next.js - Dynamic Routes ( TODO ) (0) | 2022.03.03 |
Next.js - with Redux thunk (0) | 2022.03.01 |