Next.js 구동방식과 getInitialProps

0

Next.js

목록 보기
1/1

Next.js가 React Project의 SSR을 가능하게 한다.
어떤 방식으로 SSR을 가능하게 할까? 먼저 알아야 할 것은 Next js의 구동방식 이라고 생각한다.

Server Side
최초에 Next 서버로 요청이 들어왔을 때, Next 서버에서는 요청이 들어온 페이지에 들어갈 데이터를 Fetch하고 Html을 구성하여 Client로 보내준다. 그 과정을 알아보자.

_app.js_document.js
최초로 실행되는 녀석들이다. 위의 두 파일은 server only file이다. Next server logic에 사용되는 파일이라는 뜻으로 client에서 사용하는 로직(ex. eventlistener 등의 window / DOM 로직)을 사용하면 안된다. window is not defined라는 에러를 보았다면 해당 사항을 체크해보길 바란다.

최초로 실행되는 것은 _app.js 이다. _app.js는 client에서 띄우길 바라는 전체 컴포넌트의 레이아웃으로 이해하면 쉽다. 공통 레이아웃 이므로 최초에 실행되어 내부에 들어갈 컴포넌트들을 실행한다. 내부에 Content 들이 있다면 전부 실행하고 Html의 Body로 구성한다.

다음 샘플 코드로 설명하자면,

function MyApp({ Component, pageProps }) {
  return (
    <Layout>
    	<Component {...pageProps} />
	  </Layout>
  );
}

export default MyApp;

여기서 props로 받은 Component는 요청한 페이지이다. GET / 요청을 보냈다면, Component 에는 /pages/index.js 파일이 props로 내려오게 된다. pageProps는 페이지 getInitialProps를 통해 내려 받은 props들을 말하는데, 이는 getInitialProps 파트에서 자세히 설명하겠다.

그 다음 _documents.js가 실행된다. _document.js는 static html를 구성하기 위한 _app.js에서 구성한 Html body가 어떤 형태로 들어갈지 구성하는 곳이다. Content들을 브라우저가 html로 이해하도록 구조화 시켜주는 곳이라고 이해하면 쉽다.

다음 샘플 코드로 설명하자면,

import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {

render() {
return (

  <Html>
    <Head />
    <body>
      <Main />
      <NextScript />
    </body>
  </Html>
)

}
}

export default MyDocument
_app.js가 실행되면서 갖추어진 content들은 Main Component 아래에 생성된다.

기억하자!

_documents.js에 어플리케이션 로직을 넣지 말자. 브라우저는 Main을 제외한 다른 component들을 initialize하지 않는다. 공통된 어플리케이션 로직이 필요하다면, _app.js를 활용하자.

// next js docs 발췌
React components outside of

will not be initialized by the browser. Do not add application logic here. If you need shared components in all your pages (like a menu or a toolbar), take a look at the App component instead
getInitialProps
웹 페이지는 각 페이지마다 사전에 불러와야할 데이터들이 있다. Data Fectching이라고도 하는 로직은 CSR(Client Side Rendering)에서는 react 로직에 따라 componentDidMount or useEffect로 컴포넌트가 마운트 되고 나서 하는 경우가 많다. 이 과정을 서버에서 미리 처리하도록 도와주는 것이 바로 getInitialProps이다. (사실 Data Fetching에만 getInitialProps를 사용할 수 있는 것은 아니다.)

Next 9.3 버전에서는 getInitialProps 를 대신에 getStaticProps, getStaticPaths, getServerSideProps 를 사용하게 된다고 한다. 각각의 용법은 다르지만, 서버에서 페이지의 연산을 미리 한다는 점은 동일하므로 따로 언급하지는 않겠다. 각각의 로직이 어떤 역할을 하는지는 따로 포스팅할 기회가 있을지도...

Data Fetching을 서버에서 하게 되면 첫째, 속도가 빨라진다. 브라우저에서의 연산을 서버와 함께 하면서 미리 데이터를 받아오고 브라우저는 렌더링만 할 수 있기 때문이다. 둘째, 코드 상의 처리가 깔끔해진다. 데이터가 꼭 필요한 페이지의 경우 브라우저가 데이터를 가져올 때까지 화면 렌더링을 잠시 null 처리하는 경우가 있다. 이 과정이 없어지고, Initial한 데이터가 들어오는 과정을 전제로 코드를 작성할 수 있다.

사용법
목적에 따라서 사용법이 다르다. 해당 페이지에만 미리 데이터를 불러오는 로직을 넣을 것인지, 혹은 전체 페이지에 대해 동일한 Data Fetching을 할 것인지를 정해야 한다. 이는 기획에 따라 달라지는 부분이다. 공통된 Data Fetching이 필요하다면 _app.js에 getInitialProps를 붙이면 된다. 페이지마다 다른 Data가 필요하다면 페이지마다 getInitialProps를 붙이면 된다. 먼저 각 페이지마다 getInitialProps를 붙이는 방법은 다음과 같다.

import axios from 'axios';

const Page = ({ stars }) => {

return

Next stars: {stars}
;
};

Page.getInitialProps = async ctx => {
const { data } = await axios.get('...url');

return { stars: data };
}

export default Page;
사용시 주의할 점
getInitialProps 내부 로직은 서버에서 실행된다. 따라서 Client에서만 가능한 로직은 피해야 한다. (Window, document 등)
한 페이지를 로드할 때, 하나의 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>

	);
}

};
default Props
getInitialProps들은 기본적으로 받는 props가 있다. 이를 context(ctx)라고 한다.

ctx Object의 기본 구성은 다음과 같다.

pathname - 현재 pathname /user?type=normal page 접속 시에는 /user
query - 현재 query를 object형태로 출력 /user?type=normal page 접속 시에는 {type: 'normal'}
asPath - 전체 path /user?type=normal page 접속 시에는 /user?type=normal
req - HTTP request object (server only)
res - HTTP response object (server only)
err - Error object if any error is encountered during the rendering
ServerSide Cycle
Next Server가 GET 요청을 받는다.
요청에 맞는 Page를 찾는다.
_app.js의 getInitialProps가 있다면 실행한다.
Page Component의 getInitialProps가 있다면 실행한다. pageProps들을 받아온다.
_document.js의 getInitialProps가 있다면 실행한다. pageProps들을 받아온다.
모든 props들을 구성하고, _app.js > page Component 순서로 rendering.
모든 Content를 구성하고 _document.js를 실행하여 html 형태로 출력한다.
위의 과정으로 server logic이 실행이 된다. 이 순서가 가끔 헷갈려서 서버 상에 로직이 생각과 다르게 진행되는 경우가 많다. 브라우저 console에도 안찍히는 로직이므로, 디버깅이 어렵다는 단점도 있다.

By Cyrano on MAR 15, 2020.

profile
안녕하세요😄 비전공자의 웹개발자 도전기를 쓰는 중입니다! 수정/보완할 부분이 있다면 피드백 언제든 환영입니다!

0개의 댓글