원본: https://nextjs.org/docs/basic-features/pages
Next.js에서 page란 .js
, .jsx
, .ts
, .tsx
파일에서 export 된 리액트 컴포넌트이다. 각 페이지는 파일 이름으로 라우팅된다.
pages/about.js
라는 파일이 있으면 이 파일은 /about
이라는 주소로 접근 가능하다.Next.js는 동적 라우팅을 제공한다.
pages/posts/[id].js
라는 파일이 있다면 이 파일은 posts/1
, posts/2
등으로 매칭된다.pages/posts/[pid].js
는 posts/1
, posts/abc
, 기타 등등으로 매칭된다.기본적으로 Next.js는 모든 페이지를 pre-rendering한다. pre-rendering은 즉 클라이언트에서 javascript로 HTML을 생성하는 것이 아니라 Next.js가 HTML을 미리 생성해두는 방식을 의미한다. 이 방식을 사용하면 더 좋은 성능을 낼 수 있고 SEO에도 적합하다.
각각 생성된 HTML은 페이지에 필요한 최소한의 javascript 코드와 연결되어있다. 페이지가 브라우저에서 로드되면 HTML이 먼저 로드된 후, 이 javascript 코드가 실행되면서 동작 가능한 페이지로 완성된다. 이 과정을 hydration이라고 한다.
pre-rendering에는 Static Generation과 Server-side Rendering이라는 두 가지 방식이 있다. 이 둘의 차이점은 언제 HTML을 생성하느냐이다.
Next.js에서는 이 두 가지 방식을 페이지별로 선택해서 사용할 수 있다.
Static Generation이 권장되는 이유는 정적으로 생성된 페이지가 CDN에 캐시되는 것이 가능해서 성능을 향상시킬 수 있기 때문이다. 하지만 일부 경우에는 Server-side Rendering이 더 좋은 선택지일 수도 있다.
Static generation을 사용하면 페이지의 HTML이 빌드 타임에 생성된다. 즉 next build
를 실행했을 때 생성된다. 이 HTML은 매 요청마다 재사용될 것이며 CDN에 캐시 가능하다.
또한 페이지를 생성할 때 데이터를 포함할 수도 있고 그렇지 않을 수도 있다.
기본적으로 Next.js에서는 data fetching(데이터 가져오기) 없이 pre-rendering이 진행된다.
function About() {
return <div>About</div>
}
export default About
어떤 페이지들은 외부 데이터 로드를 필요로 한다. 이때 두 가지 경우가 있는데, 하나 또는 두 가지 전부 적용될 수 있다. 각각의 경우에 따라 Next.js에서 제공하는 getStaticProps
나 getStaticPaths
함수를 사용하면 된다.
getStaticProps
getStaticPaths
// TODO: Need to fetch `posts` (by calling some API endpoint)
// before this page can be pre-rendered.
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
export default Blog
위 코드에서는 posts
라는 데이터를 가져와야 한다. 그러기 위해서는 같은 파일 내부에서 export async
키워드를 붙인 getStaticProps
함수를 작성하면 된다. 이 함수는 빌드 타임에 실행되며 pre-render 시 fetch된 데이터를 페이지의 props
로 넘겨준다.
function Blog({ posts }) {
// Render posts...
}
// This function gets called at build time
export async function getStaticProps() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// By returning { props: { posts } }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
}
}
export default Blog
동적 라우팅을 사용하면 pages/posts/[id].js
라는 파일을 생성했을 때 id: 1
을 가진 페이지를 posts/1
에서 접속할 수 있다. 하지만 이 id
는 외부 데이터에 따라 결정될 수 있다.
그러기 위해서는 같은 파일 내부에서 export async
키워드를 붙인 getStaticPaths
함수를 작성하면 된다. 이 함수는 빌드 타임에 실행되며 pre-rendering을 진행할 경로를 지정할 수 있다.
function Post({ post }) {
// Render post...
}
export async function getStaticPaths() {
// ...
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return { props: { post } }
}
export default Post
user request 전에 pre-rendering을 진행할 수 있다면 Static generation을 사용하면 된다.
그렇지 않을 경우 Client-side rendering과 Static generation을 함께 사용하거나 Server-side rendering을 사용하면 된다.
Server-side rendering을 사용하면 각 페이지의 HTML은 매 요청 때마다 생성된다.
SSR을 사용하기 위해서는 같은 파일 내부에서 export async
키워드를 붙인 getServerSideProps
함수를 작성하면 된다. 이 함수는 모든 요청 때마다 서버에서 실행된다.
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page
보다시피 getServerSideProps
는 getStaticProps
와 비슷하지만 빌드 타임에 실행되는 대신 매 요청 때마다 실행된다는 점만 다르다.