최초에 Next 서버로 요청이 들어왔을 때 Next 서버에서는 요청이 들어온 페이지에 들어갈 데이터를 Fetch하고 Html을 구성하여 Client로 보내준다.
기본으로는 Next 자체에서 제공하는 로직으로 실행되지만 커스터마이징을 위해서는 pages 폴더에 각각 _app.js, _document.js 파일을 생성하고 코드를 작성하면 된다.
위 두 파일은 server only file이다. Next server logic에 사용되는 파일이라는 뜻으로 client에서 사용하는 로직(eventlistener 등의 window / DOM 로직)을 사용하면 안된다.
최초 실행은 _app.js이다. _app.js는 client에서 띄우길 바라는 전체 컴포넌트의 레이아웃으로 이해하면 쉽다. 공통 레이아웃이므로 최초에 실행되어 내부에 들어갈 컴포넌트들을 실행한다. 내부에 Content 들이 있다면 전부 실행하고 Html의 Body로 구성한다.
주로 header, footer 영역 부분이 주로 들어간다.
import '../styles/globals.css';
import 'semantic-ui-css/semantic.min.css';
import Footer from "../src/component/Footer";
import Top from "../src/component/Top";
function MyApp({ Component, pageProps }) {
return (
<div style={{width: 1000, margin: "0 auto"}}>
<Top />
<Component {...pageProps} />;
<Footer />
</div>
);
}
export default MyApp
/*
* 페이지 전환시 레이아웃을 유지할 수 있습니다
* 페이지 전환시 상태값을 유지할 수 있습니다.
* componentDidCatch를 이용해서 커스텀 에러 핸들링을 할 수 있습니다.
* 추가적인 데이터를 페이지로 주입시켜주는게 가능합니다.
* 글로벌 CSS를 이곳에 선언합니다.
* */
여기서 props로 받은 Component는 요청한 페이지이다.(위 코드에서는 <Component {...pageProps} />
부분) GET /
요청을 보냈다면 Component에는 /pages/index.js
파일이 props로 내려오게 된다. pageProps는 페이지 getInitialProps를 통해 내려 받은 props들을 말한다.
그 다음 _document.js가 실행된다
_document.js는 static html를 구성하기 위한 _app.js에서 구성한 Html body가 어떤 형태로 들어갈지 구성하는 곳이다. Content들을 브라우저가 html로 이해하도록 구조화 시켜주는 곳이라고 이해하면 쉽다.
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>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
<Document>
는 Class 형 컴포넌트만 지원하며 다음 4가지 컴포넌트는 필수로 존재해야 한다.
<Html>
<Head>
<Main>
<NextScript>
import Document, {Html, Head, Main, NextScript} from 'next/document'
그리고 4가지 필수 컴포넌트는 위와 같이 next/document에서 import 해야한다.
_app.js가 실행되면 갖추어진 content들은 Main Component 아래에 생성된다.
_document.js에 어플리케이션 로직을 넣지 말자. 브라우저는 Main을 제외한 다른 component들을 initialize하지 않는다. 공통된 어플리케이션 로직이 필요하다면 _app.js를 활용하라
import Head from "next/head"; next/head의 Head 컴포넌트는 특정 페이지에서 바뀌는 Head 태그에서 사용해야한다. 예를 들어 페이지마다 바뀌는
태그를 적용할 경우 이는 pages 디렉토리 내에 있는 컴포넌트에서 next/head의` 컴포넌트를 이용해야한다.
import Head from "next/document";
next/document의 Head 컴포넌트는 모든 페이지에서 사용할 Head 속성들을 조작할 때 사용한다.
예를 들어 페이지의 언어 설정인 <lang>
과 같은 속성일 경우 _document.js에서 next/document의 <Head>
를 import 하여 사용한다.
웹 페이지는 각 페이지마다 사전에 불러와야할 데이터들이 있다.
Data Fectching이라고도 하는 로직은 CSR(Client Side Rendering)에서는 react 로직에 따라 componentDidMount or useEffect
로 컴포넌트가 마운트 되고 나서 하는 경우가 많다.
이 과정을 서버에서 미리 처리하도록 도와주는 것이 바로 getInitialProps이다.
Next 9.3 버전에서는 getInitialProps 대신 getStaticProps, getStaticPaths, getServerSideProps를 사용하게 된다고 한다.
Data Fetching을 서버에서 하게 되면 첫째 속도가 빨라진다. 브라우저에서 연산을 서버와 함께 하면서 미리 데이터를 받아오고 브라우저는 렌더링만 할 수 있기 때문이다.
둘째, 코드상의 처리가 깔끔해진다. 데이터가 꼭 필요한 페이지의 경우 브라우저가 데이터를 가져올때까지 화면 렌더링을 잠시 null 처리하는 경우가 있다. 이 과정이 없어지고 Initial한 데이터가 들어오는 과정을 전제로 코드를 작성할 수 있다.
공통된 Data Fetching이 필요하다면 _app.js에 getInitialProps를 붙이면 된다.
페이지마다 다른 Data가 필요하다면 페이지마다 getInitialProps를 붙이면 된다.
ex) 각 페이지마다 getInitialProps를 붙이는 방법
import axios from 'axios';
const Page = ({ stars }) => {
return <div>Next stars: {stars} </div>;
};
Page.getInitialProps = async ctx => {
const { data } = await axios.get('url');
return { stars: data };
}
export default Page;
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>
);
}
}
getInitialProps들은 기본적으로 받는 props가 있다. 이를 context(ctx)라고 한다.