728x90
반응형

Next.js는 React를 CRA(Create React App)으로 설치해보았으면 다음 명령어 그대로 사용하면 된다. 

npx create-next-app

 

 

다음과 같이 설치하여 npm run dev 또는 yarn dev를 통해 실행하면 된다.

폴더 구조

폴더 구조는 _app.js 와 index.js가 만들어져 있을 것이고, api폴더는 api를 사용할 때, public은 보통 image를 넣어 사용한다. 

React를 설치하였을 때와 다른 _app.js와 _document.js 같은 것들에 대해서 알아보겠다.

 

먼저 _app은 서버로 요청이 들어왔을 때 가장 먼저 실행되는 컴포넌트로, 페이지에 적용할 공통 레이아웃의 역할을 한다.

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

여기서 pageProps는 초기 속성값이 되는데, 초기 속성값은 getInitialProps, getStaticProps, getServerSideProps 중 하나를 통해 패칭한 값이다. 

getInitialProps를 사용해 모든 페이지에서 사용할 공통 속성 값을 지정하는데 이럴 경우 자동 정적 최적화(Automatic Static Optimization)이 비활성화되어 모든 페이지가 서버 사이드 렌더링을 통해 제공된다. 

_app에서 getInitialProps를 사용하고자 한다면 App 객체를 불러온 후 getInitialProps를 통해 데이터를 불러와야 한다.

app.getInitialProps = async(appContext) => {
	const appProps = await App.getInitialProps(appContext);

	return {...appProps}
}

 

그 다음은 document 페이지이다.

create-next-app으로 설치하면 document는 없을 것이다. 하지만 보통 <head>태그나 <body>태그 안에 들어갈 내용을 커스텀할 때 활용하기 위하여 만들어준다. 

_document.js 로 만들어준다. 이건 _app 다음에 실행된다.폰트 import나 charset, 웹 접근성 관련 태그를 여기서 설정한다. 

_document를 작성할 때는 Document 클래스를 상속받는 클래스 컴포넌트로 작성해야만 하며, 렌더 함수는 꼭 <Html>, <Head>, <Main>, <NextScript> 요소를 리턴해 주어야 한다. 

_document 에서 사용하는 <Head> 태그는 next/head가 아닌 next/document 모듈에서 불러와야 한다.

_document의 <Head>태그에는 모든 문서에 공통적으로 적용될 내용(charset, 메타태그 등)이 들어가야 한다.

_document는 언제나 서버에서 실행되므로 브라우저 api 또는 이벤트 핸들러가 포함된 코드는 실행되지 않는다.

<Main /> 부분을 제외한 부분은 브라우저에서 실행되지 않는다.

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

즉, Next.js 의 실행되는 순서는 다음과 같다.

  1. Next Server가 GET 요청을 받는다.
  2. 요청에 맞는 Page를 찾는다.
  3. _app.js의 getInitialProps가 있다면 실행한다.
  4. Page Component의 getInitialProps가 있다면 실행한다. pageProps들을 받아온다.
  5. _document.js의 getInitialProps가 있다면 실행한다. pageProps들을 받아온다.
  6. 모든 props들을 구성하고, _app.js > page Component 순서로 rendering.
  7. 모든 Content를 구성하고 _document.js를 실행하여 html 형태로 출력한다.

Server Side

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

_app.js 와 _document.js

최초로 실행된다. 없어도 된다 사실.

server only file이다. 즉, Next Server logic에 사용되는 파일이라는 뜻으로 client에서 사용되는 로직(eventListener 등의 window / DOM 로직)을 사용하면 안된다. 서버사이드 렌더링이기 때문! (앞 글에서 언급하였다)

window is not defined //이런 에러일 때 확인해보자.

최초 실행은 _app.js

_app.js는 client에서 띄우길 바라는 전체 컴포넌트의 레이아웃으로 이해하면 쉽다.

공통 레이아웃이므로 최초에 실행되어 내부에 들어갈 컴포넌트들을 실행한다.

내부에 Content들이 있다면 전부 실행하고 Html의 Body로 구성한다.

props로 받은 Component는 요청한 페이지이다. GET / 요청을 보냈다면, Component에서는 /pages/index.js 파일이 props로 내려오게 된다.

pageProps는 페이지 getInitialProps를 통해 내려 받은 props 들을 말한다.

그 다음 _document.js가 실행된다. _document.js는 static html를 구성하기 위한 _app.js에서 구성한 Html body가 어떤 형태로 들어갈지 구성하는 곳이다.

Content들을 브라우저가 html로 이해하도록 구조화 시켜주는 곳이라고 이해하면 쉽다.

_document.js에 어플리케이션 로직을 넣지말자!!

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

getInitialProps

웹 페이지는 각 페이지마다 사전에 불러와야할 데이터들이 있다.

Data Fetching이라고도 하는 로직은 CSR에서는 componentDidMount나 useEffect로 컴포넌트가 마운트 되고 나서 하는 경우가 많다.

이 과정을 서버에서 미리 처리하도록 도와주는 것이 getInitialProps이다.

데이터 패칭을 서버에서 하게 되면, 속도가 빨라진다.

Initial한 데이터가 들어오는 과정을 전제로 코드를 작성할 수 있다.

전체 페이지에 동일한 Data Fetching을 할 것인지를 정해야 한다.

공통된 Data Fetching이 필요하다면 → _app.js에 getInitialProps를 붙이면 된다.

페이지마다 다른 Data가 필요하다면 페이지마다 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;

사용 시 주의할 점

  • getInitialProps 내부 로직은 서버에서 실행된다→ 따라서 Client에서만 가능한 로직은 피해야 한다.
  • 한 페이지를 로드할 때, 하나의 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>

		);
	}
};

getInitialProps는 기본적으로 받는 props가 있다. 이를 content(ctx)라고 한다.

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

  • pathname : 현재 pathname (/user?type=normal page 접속 시에는 /user)
  • query : 현재 query를 object 형태로 출력
  • asPath : 전체 path
  • req : HTTP request object
  • res : HTTP response object
  • err : Error object if any error is encountered during the rendering
728x90
반응형

+ Recent posts