Next.js에서에서 각 페이지는 pages 디렉토리의 .js, .jsx, .ts or .tsx 파일들로부터 추출된 React Component이다.
예: 만약 pages/about.js
페이지를 생성한다면 그건 아래와 같은 모습일거고, /about
에 접근할 수 있다.
function About() {
return <div>About</div>
}
export default About
Next.js는 동적라우트 할 수 있는 페이지를 제공한다. 만약 pages/posts/[id].js
파일을 생성한다면 posts/1
, posts/2
등에 접근할 수 있다.
Next.js는 디폴트로 매 페이지를 pre-rendering 한다. 즉 Next.js는 client-side javascript에 의해 모든것이 완료되는 대신 미리 각 페이지 HTML을 생성한다. 이런 Pre-rendering은 app의 퍼포먼스와 SEO(Search engine optimization)에 더 좋은 결과를 만든다.
생성된 HTML은 해당 각 페이지의 최소한의 필수적인 js code와 연관된다. 그래서 브라우저가 로드될 때 그 필수적인 js code가 실행이 되고 해당 페이지는 완전한 상호작용을 할 수 있게 된다.(이런 과정을 hydration이라 한다.)
Next.js는 두가지 종류의 pre-rendering이 있다: Static Generation 과 Server-side Rendering. 두 개의 차이는 언제 HTML을 생성하는데 있다.
중요한건, 개발자 어떤 pre-rendering을 할지 선택할 수 있다. 그래서 개발자는 대부분의 페이지를 Static Generation으로 그리고 그 외는 Server-side Rendering으로 하는 "hybrid" Next.js app을 개발 할 수 있다.
Static Generation는 Server-side Rendering 보다 퍼포먼스적으로 더 좋다. 정적으로 생성된 페이지는 성능개선을 위한 특별한 설정없이 CDN으로 캐시되기 때문이다. 하지만 Server-side Rendering만 사용할 수 밖에 없는 경우도 있다.
Client-side Rendering 또한 Static-Generation 이나 SSR과 함께 사용가능하다. 즉 어떤 페이지는 전체적으로 Cient-side js에 의해 렌더될 수 있다는 뜻이다. 좀 더 자세한 정보는 Data Fetching 문서를 살펴봐라.
만약 Static Generation을 사용한다면, 그 페이지는 빌드시에 HTML이 생성된다. 이말은 운영환경에서 HTML페이지가 next build
가 실행될때 생성되다는 뜻이다. 이 HTML은 매 요청시에 재사용될 것이고 CDN의 의해 캐시된다.
Next.js에서는 정적페이지를 데이터가 있게 혹은 없게 생성할 수 있다. 한번 각 경우를 살펴보자.
Next.js는 데이터 fetching없이 Static Generation하는 것이 디폴트다. 아래 예가 있다.
function About() {
return <div>About</div>
}
export default About
이 페이지는 pre-render를 위해 어떤 외부 data도 가져올 필요가 없다. 이런경우 Next.js는 빌드시에 해당 HTML파일을 생성한다.
어떤 페이지들은 pre-rendering 시에 외부 data가 필요하다. 이런경우 Next.js가 제공하는 특별한 함수를 사용할 수 있는데, 아래 함수중 한개 혹은 두개 모두 사용이 필요할 수 있다.
getStaticProps
를 사용해라getStaticPaths
를 사용해라. (이런 경우 보통 getStaticProps
도 같이 사용해야한다.) 예: 만약 블로그 페이지가 CMS(content management system)로부터 블로그 포스트 리스트를 가져와야할 때
// 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
pre-render시 이 데이터를 가져오기위해 Next.js는 해당 파일에 getStaticProps
라는 async
함수를 export
하게 해준다. 이 함수는 빌드시에 호출된다. 그리고 pre-render때 가져온 데이터를 해당 페이지의 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
getStaticProps
가 어떻게 동작하는지 알고 싶다면 Data Fetching 문서를 참고해라.
Next.js는 dynamic routes로 페이지를 생성할 수 있게 할 수 있다. 예를 들면 pages/posts/[id].js
는 id
를 기준으로 한 블로그 포스트를 보여준다. 따라서 post/1
에 접근하면 id:1
로 된 블로그 포스트를 보여줄 수 있다.
하지만 빌드타임에 어떤 id
인가를 판단하는건 외부정보에 종속된다.
예: 만약 db에 id:1
로 된 블로그포스트를 하나 올렸다고 하자. 그럼 빌드시 posts/1
만 pre-render 되길 원할 것이다.
나중에 만약 id:2
의 블로그 포스트가 추가된다면, posts/2
도 역시 pre-render되길 원할 것이다.
그럼 pre-render되는 페이지들의 paths는 외부 data에 종속되는 셈이다. Next.js는 이런 경우 해당페이지(pages/posts/[id].js
)에 getStaticPaths
라 불리는 async
함수를 export
할 수 있게 제공해준다. 이 함수는 빌드시에 호출되고 pre-render 되는 paths를 특정할 수 있게 해준다.
// This function gets called at build time
export async function getStaticPaths() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false }
}
또한 pages/posts/[id].js
에 getStaticProps
를 export할 필요가 있다. 그래야만 해당 id
포스트 페이지의 대한 data를 가져올 수 있고 pre-render 시에 사용할 수 있다.
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
getStaticPaths
의 동작을 좀 더 알고싶다면 Data Fetching 문서를 참조해라.
보통 Static Generation을 가능한 권장한다. 왜냐면 이경우 매 요청시마다 페이지를 render하는 SSR 보다 훨씬 빠르기 때문이다.
Static Generation 은 아래 경우들에 사용 할 수 있다.
스스로 물어보기: "이 페이지는 사용자 요청 전에 pre-render될 수 있을까?" 만약 그러면 Static Generation을 택해라.
반면 Static Generation이 좋지 않은 경우가 있다. 만약 사용자 요청전에 미리 페이지를 pre-render할 수 없는 경우가 그렇다. 만약 해당 페이지의 정보가 자주 업데이트 되고 그 페이지 content가 매 요청 시마다 변경되는 경우가 그런 경우다.
이런 경우 다음 중 하나를 선택할 수 있다:
Static Generation 과 Client-side Rendering: 페이지의 특정 정보는 pre-render를 스킵할 수있다. 그리고 client-side js를 통해 그 특정 정보를 채운다. 만약 이런 접근방식을 더 알고 싶다면 Data Fetching 문서를 참고해라.
Server-side Rendering: Next.js는 매 요청 시마다 페이지를 pre-render한다. 이건 쫌 느릴거다. 왜냐면 CDN으로 캐시할 수 없으니까. 그렇지만 pre-rendered 페이지는 항상 최신정보를 가지고 있을 것이다.
SSR 혹은 Dynamic Rendering으로 불리기도 한다.
만약 Server-side Rendering을 사용한다면 HTML페이지는 매 요청시마다 생성된다.
SSR을 사용하기 위해서는 getServerSideProps
라는 async
함수를 export
할 필요가 있다. 이 함수는 서버에서 매 요청시에 호출된다.
예를 들어 빈번히 data가 업데이트 되는 페이지를 pre-render한다 가정하자. 이때 getServerSideProps
를 쓸 수 있고 이 함수는 data를 가지고 와서 아래처럼 Page
에 전달한다.
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
와 비슷하다. 차이점은 getServerSideProps
는 빌드타임이 아닌 매 요청시에 실행되다는 점이다.
getServerSideProps
의 동작이 어떻게 되는지 알고 싶다면 Data Fetching문서를 참조해라.
Next.js의 두가지 방식의 pre-render에 대해 이야기해 보았다.
Static Generation(권장): 빌드시에 HTML을 생성하고 그건 매 요청시에 재사용된다. Static Generation을 사용하는 페이지를 만드려면 가져올 데이터가 없는경우는 그냥 컴포넌트를 export하면 되고, 외부 데이터에 종속된 페이지의 경우 getStaticProps
를 쓰면된다. 그리고 필요에 따라 getStaticPaths
도 필요할 수 있다. 사용자 요청전에 pre-render 할 수 있는건 굉장히 좋은 거고 Client-side 렌더링과 추가정보를 가져오는데 함께 사용할 수 있다.
Server-side Rendering: 매요청마다 HTML이 생성된다. SSR을 하기 위해서는 getServerSideProps
를 export해야한다. SSR은 Static Generation보다 성능면에서 더 느리다. 그러니까 반드시 필요한 경우만 사용해라.
다음 정보를 읽는 것을 추천한다.