일반적인 리액트 싱글 페이지는 초기 렌더링 때 모든 컴포넌트를 내려받지만 용량이나 규모가 커지면 로딩 속도가 지연될 수 있다. Next에서는 필요에 따라 파일을 불러올 수 있게 여러개의 파일을 분리하는 코드 스플리팅을 사용하였다.
폴더 구조를 보면 pages 폴더 안에 각 page(라우트) 들이 들어가며 컴포넌트 폴더에는 리액트 컴포넌트들이 들어가는데 브라우저가 실행되고 사용자가 접속하면 첫 페이지인 index 페이지만 불러오고 그 이후에 다른 페이지로 넘어갔ㅇ르 떄는 해당 페이지만 불러오게 된다.
Router와 Link를 모두 사용할 수 있는데 Link는 href를 이용하여 해당 페이지로 이동하게 할 수 있고 as는 href의 URL을 조금 더 직관적으로 만들어준다.
hre: pages 디렉토리 내부에 페이지 이름. 예 /blog/[slug]
as: 브라우저에서 보여질 주소. 예 /blog/hello-world.
Router는 링크와 동일하게 해당 페이지로 이동해주는 역할울 하지만 개발자에게 제어권을 더 넘겨줘 쉽게 redirect도 가능하다.
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/blog/[slug]" as="/blog/welcome">
<a>Blog</a>
</Link>
</li>
</ul>
)
}
export default Home
import Router from 'next/router'
function Link() {
return (
<div>
Click <span onClick={() => Router.push('/about')}>here</span>
</div>
)
}
export default Link
import { useEffect } from 'react'
import { useRouter } from 'next/router'
function Page() {
const router = useRouter()
useEffect(() => {
router.push('/?counter=10', null, { shallow: true })
}, [])
}
export default Page
SPA 에서 시적점이 되는 index.html로 custom document를 만들때만 작성이 팔요하며 생략된 경우 기본 값을 사용한다.
React lifecyle과 data fetching은 불가능하다.
import Document, { Head, Main, NextScript } from 'next/document';
export default class RootDocument extends Document {
render() {
return (
<html>
<Head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/styles/railscasts.min.css" />
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
import { AppProps } from 'next/app';
import Layout from '../components/Layout';
function MyApp({ Component, pageProps }: AppProps) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
export default MyApp;
import React, { Component } from 'react';
import ErrorPage from '../components/ErrorPage';
export default class RootError extends Component {
render() {
const { statusCode } = this.props;
return <ErrorPage statusCode={statusCode} />;
}
}
/pages/index.js -> /
/pages/apple/banana.tsx -> /apple/banana
/pages/apple/[id].tsx -> /apple/동적 path
import { GetStaticProps } from 'next'; // 타입스트립트 사용시 getStatcProps의 타입임포트
const ContentList = ({ contentList }) => {
return (
<ul>
{contentList.map((content) => (
<li>{content.title}</li>
))}
</ul>
);
};
export const getStaticProps: GetStaticProps = async (context) => {
const { params, preview, previewData } = context;
const res = await fetch('url');
const contentLists = await res.json();
return {
props: {
contentList,
},
revalidate:1, // 1초에 한번 데이터 fetch
};
};
export default ContentList;
export async function getStaticPaths() {
return {
//빌드 타임 때 아래 정의한 동적인값 경로만 pre렌더링한다
paths: [
{ params: { dynamic: 1 } },
{ params: { dynmic: 2 } }
......
{ params: { dynmic: 동적인값 } }
],
fallback: true,
}
}
fallback은 default가 false 이며 경로 지정 페이지만 사전 렌더링하고 없는 경로 접근시 404 페이지를 렌더링한다.
fallback 키가 true인경우, getStaticPaths 에 없는 경로는 설정된 FallbackPage 를 반환하고, getStaticProps 를 통해 해당 url 에 필요한 data 를 요청한다. 이후의 동일한 요청에 대해서는 방금 작업을 이용해서 미리 렌더링된 페이지가 제공된다.
import { useRouter } from 'next/router'
const Compo ({ data }) {
const router = useRouter()
if (router.isFallback) {
return <div>Loading...</div>
}
return (
)
}
export async function getStaticPaths() {
const response = await fetch(".../posts")
const data = await response.json()
const paths = data.map(({ id }) => ({
params: { id: String(id) }, // 값은 string으로 넣어야 한다.
}))
paths = [
{params : {id:'1'} },
{params : {id:'2'} },
{params : {id:'3'} },
{params : {id:'4'} },
]
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const res = await fetch(`...url/${params.id}`)
const data = await res.json()
return {
props: { data },
revalidate: 1,
}
}
export default Post
export async function getStaticProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}