Next.js에서 pages를 다룰 때 Pre-renders와 SEO개념을 알아야 한다.
Next.js는 기본적으로 모든 페이지를 Pre-render한다.
아래의 이미지 처럼 페이지를 로드할 때 js를 제외한 기초적인 U가 그려진 상태로 로드하게 된다. 이후 Js 번들이 로드되면 그제서야 Hydration이라는 과정을 거쳐서 사용자와 app이 인터렉션 하게 된다.
* 출처: Next.js 공식홈페이지
반면 Pre-render가 없는 경우 첫번 째로 로드할 때는 아무것도 없다(ex.react 컴포넌트 등 js로 만든 SPA). Js가 실행되야지만 컴포넌트들을 화면에 그린다.
*출처: Next.js 공식홈페이지
CSR만 제공한다면, Client(브라우저)처럼 동작하지 않는 검색엔진의 경우 아무런 데이터도 조회해갈 수 없다. 즉, no pre-render의 경우 js가 동작하지 않는 검색엔진에서는 아무것도 읽어갈 수 없다. 하지만 SSR를 하면 js를 실행하지 못하는 검색엔진에서도 타이틀, 대표 이미지등 미리 로드된 정보를 읽어갈 수 있다.
SSG(recommended) && SSR
SSG는 빌드 타임에 pre-render를 실행해 서버에 부하가 덜하다.
SSR은 요청 타임에 pre-render를 한다. 즉, 사용자 또는 검색엔진이 해당페이지를 방문했을 때(Url로 요청) node서버가 동작해 화면을 미리 그려서 전달하는 것. Next.js공식문서에 따르면 SSG를 적극적으로 쓰라고 권장하고 있다.
Page의 내용이 외부 데이터에 의존적인 상황
Page Paths까지 외부 데이터에 의존적인 상황
첫번 째는 getStaticProps만 가지고도 가능하다.
두번 째는 getStaticPaths도 함께 활용해야 가능하다.
Layouts는 여러 Page들을 공통적으로 처리할 때 쓰인다.
여래개의 Layout을 활용하고 싶은 경우는 Components폴더 안에 SubLayout.js파일을 생성하고 해당 페이지에 (Page.getLayout) getLayout함수를 제공한다.
next.js의 루트 디렉토리 아래 componens 폴더를 만들고 Layout.js 파일을 생성해 아래와 같이 pages안에 공통적으로 들어가는 부분을 담아둔다.
import Head from 'next/head';
import styles from '../styles/Home.module.css';
export default function Layout({ children }) {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
{children}
</main>
<footer>
...
이후 pages폴더 안에 _app.js파일을 생성한다. _app.js파일은 모든 페이지를 품을 수 있는 구조로 next.js에서 제공한다. 아래와 같이 작성해 준다.
_app.js
import Layout from "../components/Layout"
export default function App({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
localhost:3000
아래와 같이 footer가 두개 생성된 걸 확인할 수 있다.
index.js페이지가ㅏ _app.js로 감싸져 있는데 공통된 부분을 Index.js에서 제거하지 않았기 때문이다.
index.js에서 공통된 부분을 지우고 아래와 같이 작성한다.
import Head from 'next/head';
import styles from '../styles/Home.module.css';
import Link from 'next/link';
export async function getServerSideProps() {
console.log('server');
return {
props: { time: new Date().toISOString() }
}
}
export default function Home({ time }) {
return (
<>
<h1 className={styles.title}>
{time}
</h1>
<h1><Link href="/csr">CSR로</Link></h1>
<h1><Link href="/ssg">SSG로</Link></h1>
<h1><Link href="/isr">ISR로</Link></h1>
</>
)
}
localhost:3000
위와 같은 pages에서 csr, irs, ssg에 home으로 가는 공통적인 a 태그를 만들고 싶을 경우 layout을 활용해 보자.
componenet폴더 안에 SubLayout.js파일을 생성한다.
import Link from 'next/link';
export default function Layout({ children }) {
return (
<div>
<h1><Link href="/">Home으로 </Link></h1>
{children}
</div>
)
}
csr.js파일에 홈으로 가기 태그를 설정해 보자.
csr파일 안에 getLayout이라는 함수를 함께 설정해 준다.
import Head from 'next/head';
import styles from '../styles/Home.module.css';
import Link from 'next/link';
import { useEffect, useState } from 'react';
import Layout from '../components/Layout';
import SubLayout from '../components/SubLayout';
export default function CSR() {
const [time, setTime] = useState();
useEffect(() => {
console.log('Client')
setTime(new Date().toISOString());
}, [])
return (
<>
<h1 className={styles.title}>
{time}
</h1>
</>
)
}
CSR.getLayout = function getLayout(page) {
return (
<Layout>
<SubLayout>{page}</SubLayout>
</Layout>
)
}
_app.js에서 Component(page들)가 getLayout함수를 가지고 있다면 그것으로 실행하고 아니라면 해당 page를 리턴하는 코드를 작성해보자.
import Layout from "../components/Layout"
export default function App({ Component, pageProps }) {
// return (
// <Layout>
// <Component {...pageProps} />
// </Layout>
// )
const getLayout = Component.getLayout || ((page) => <Layout>{page}</Layout>)
return getLayout(<Component {...pageProps} />)
}
아래와 같이 CSR 페이지에 태그가 생성된 것을 확인할 수 있다.