Next.js
는 react application에서Isomorphic Rendering
을 쉽게 해주는 React framwork입니다.- csr(Client-Side-Rendering)을 하게되면 구글을 제외한 검색봇들이 csr application을 크롤링할 수 없어서 seo 문제가 생기며, 메타 데이터나 오픈 그래프 태그도 JS가 생성하기 때문에 SNS 공유하기에도 문제가 생깁니다.
- ssr(Server-Side-rendering)을 하게되면 서버의 리스폰스에 의존해서 페이지를 이동해야하기 때문에 페이지 이동시 fouc가 발생하며 spa에 비해 퍼포먼스가 떨어집니다.
- Isomorphic(Universal) Rendering을 하게되면 최초 리퀘스트의 응답은 ssr하고 그이후에는 csr와 동일하게 동작해서 SEO와 UX 모두를 충족할 수 있습니다.
getInitialProps
함수는 pages
디렉토리 안에 있는 컴포넌트에서만 사용할 수 있는 스태틱 메소드입니다.
Next.js
는 pages
디렉토리안 파일에서 getInitialProps
나 getServerProps
함수를 사용하고 있으면 빌드 타임에 ssr를 적용할 수 있게 html
이 아니라 js
로 빌드합니다.
getInitialProps
함수는 해당 페이지가 초기 로딩 페이지면 서버에서 호출되며, 아닐경우 클라이언트에서 호출됩니다.
getInitialProps
함수의 인자로는 context
객체가 전달되는데 해당 객체는 pathname
, query
, req(ssr only)
, res(ssr only)
, err
등의 프로퍼티를 갖고 있습니다.
getInitialProps
함수의 리턴 값은 해당 컴포넌트의 첫 번째 인자로 전달됩니다.
import fetch from "isomorphic-unfetch";
function Page({ stars }) {
return <div>Next stars: {stars}</div>;
}
Page.getInitialProps = async (ctx) => {
const res = await fetch("https://api.github.com/repos/zeit/next.js");
const json = await res.json();
return { stars: json.stargazers_count };
};
export default Page;
getInitialProps
함수는 빌드 타임에 데이터를 가져오고 싶을 때 사용할 수 있습니다
getInitialProps
함수는 빌드 될 때 서버 사이드에서 한번만 호출됩니다.
getServerSideProps, getInitialProps
와 달리 해당 함수를 사용해도 Next.js
는 페이지를 ssr하지 않습니다.
import fetch from "node-fetch";
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
);
}
export async function getStaticProps() {
const res = await fetch("https://.../posts");
const posts = await res.json();
return {
props: {
posts,
},
};
}
export default Blog;
getStaticPaths
함수는 다이나믹 라우팅을 하면서 getStaticProps
를 사용하는 페이지가 빌드 타임에 어떤 params
에 해당하는 페이지를 프리 렌더링 할지 결정할려고 사용합니다.
getStaticPaths
함수를 사용하는 이유는 Next.js
입장에서는 해당 함수를 이용해 paths
를 전달 해주지 않으면 params
에 어떤 값이 들어올지 모르기 때문에 프리 렌더링을 하기위해 필수로 사용해야 합니다.
getStaticPaths
함수는 paths
와 fallback
프로퍼티를 갖고있는 객체를 리턴해야합니다.
fallback
프로퍼티가 true
면 paths
에 없는 경로에 방문 했을 때 fallback
페이지를 보여주고, 백그라운드로 getStaticProps
와 같이 params
에 해당 하는 페이지를 생성해서 사용자에게 보여줍니다.
해당 프로퍼티가 false
일 경우 paths
에 없는 경로에 방문 했을 때 그냥 404 페이지를 보여줍니다.
import fetch from 'node-fetch'
const Post = () => {
...
}
export async function getStaticPaths() {
const res = await fetch('https://.../posts')
const posts = await res.json()
const paths = posts.map(post => ({
params: { id: post.id },
}))
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
...
}
export default Post
getServerSideProps
함수는 ssr을 할려고 할 때 사용합니다.
getServerSideProps
함수는 getStaticProps
와 달리 해당 페이지 경로로 리퀘스트가 올 때 마다 호출됩니다.
getServerSideProps
함수는 항상 서버 사이드에서만 호출됩니다.
function Page({ data }) {
...
}
export async function getServerSideProps({ params }) {
const res = await fetch(`https://.../${params.id}`)
const data = await res.json()
return { props: { data } }
}
export default Page
pages
디렉토리 안에 파일을 생성하면 자동으로 파일명에 해당하는 주소로 접속할 수 있습니다.
pages/index.js
→/
pages/blog/first-post.js
→/blog/first-post
pages/blog/[id].js
→/blog/[id]
pages/blog/[...all].js
→/blog/*
만약 동적으로 라우팅을 해야할 경우 파일명을 [id]
와 같이 대괄호로 묶어서 생성하면 다이나믹 라우팅 기능을 이용할 수 있습니다.
해당 id
값은 router
객체나 context
객체의 query
파라미터로 조회할 수 있습니다.
Link
컴포넌트에서 다이나믹 라우팅을 사용할려면 href
prop에 파일명을 넣고 as
prop에 이동할 주소를 넣어야합니다. 굳이 href
를 넘겨주는 이유는 Next.js
가 사전에 컴포넌트를 preload
하기위해 넘겨줘야 합니다.
import Link from "next/link";
const index = () => (
<Link href="/blog/[id]" as="/blog/test-post">
test-post link
</Link>
);
위 예시의 pages/blog/first-post.js
와 pages/blog/[id].js
처럼 미리 정의된 주소가 동적 라우팅 주소에도 해당 될 수 있을 경우, 미리 정의된 주소(first-post.js
)가 우선 순위를 갖습니다.
또한 라우트 파라미터와 쿼리 파라미터가 동일하다면 라우트 파라미터가 우선 순위를 갖습니다.
import Router from "next/router";
// query 파라미터의 프로퍼티로는 2가 아니라 "second-post"가 들어감
// push메소드는 첫 번째 인자로 href를 받고 두 번째 인자로 as 마지막 세 번째 인자로 option를 받습니다
Router.push("/blog/[id]", "/blog/second-post?id=2");
동일한 pathname
에서 리렌더링 하지않고 쿼리 스트링만 바꾸고 싶을 때는 Router.push
메소드에 shallow
옵션을 true
로 전달하면 Shallow Routing
을 진행합니다.
import Router from "next/router";
Router.push("/blog/first-post?id=5", null, { shallow: true });
Shallow Routing
을 할 경우 getInitialProps
함수는 실행되지 않습니다.
Dynamic Import
를 할려면 next/dynamic
모듈에서 default export
된 dynamic
함수를 이용하면 됩니다.
import dynamic from "next/dynamic";
// default export된 컴포넌트를 다이나믹 임포트
const DynamicComponent = dynamic(() => import("../components/hello"), {
loading: <Loader />,
});
// Hello라는 이름으로 named default된 컴포넌트를 다이나믹 임포트
const DynamicNamedComponent = dynamic(() =>
import("../components/hello").then((module) => module.Hello)
);
function Home() {
return (
<>
<DynamicComponent />
<DynamicNamedComponent />
</>
);
}
export default Home;
기본적으로 Next.js
가 제공해주는 404 에러 페이지를 커스터마이징 할려면 /pages
디렉토리에 404.js
파일을 생성하면 됩니다.
// pages/404.js
export default function Custom404Page() {
return <h1>페이지를 찾을 수 없습니다.</h1>;
}
404 에러외에도 다양한 에러를 표시해주는 페이지를 커스터마이징 할려면 /pages
디렉토리에 _error.js
파일을 생성하면 됩니다.
만약 _error.js
와 404.js
파일이 둘다 존재하면 404.js
파일이 우선 순위를 갖습니다.
function Error({ statusCode }) {
return (
<p>
{statusCode
? `An error ${statusCode} occurred on server`
: "An error occurred on client"}
</p>
);
}
Error.getInitialProps = ({ res, err }) => {
const statusCode = res?.statusCode ?? err?.statusCode ?? 404;
return { statusCode };
};
export default Error;
일반적인 SPA React.js
앱과 다르게 Next.js
앱에서는 index.html
파일이 없기때문에
Custom Document는 Next.js
가 마운트(Server-Side) 되는 지점을 커스터마이징 할려고 사용합니다.
Custom Document는 주로 애플리케이션의 <html>
, <body>
, <head>
태그를 마크업하는 용도나 서버 사이드 렌더링 환경에서 css-in-js
동작을 구현하기 위해 사용합니다.
Custom Document는 pages/_document.js
경로에 파일을 생성해서 만들 수 있습니다.
주의할 점으로는 custom Document에서는 <title>
태그를 가질 수 없고, Data Fetching
과 Lifecycle
을 사용할 수 없습니다.
import Document, { Html, Head, Main, NextScript } from "next/document";
class MyDocument extends Document {
render() {
return (
<Html lang="ko">
<Head>
<meta
name="viewport"
content="initial-scale=1.0, width=device-width"
key="viewport"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
Next.js
는 페이지를 초기화할 때 App
컴포넌트를 사용합니다.
그래서 Document
와 달리 App
컴포넌트는 페이지가 이동될 때 마다 사용됩니다.
해당 App
컴포넌트를 커스터마이징 할려면 pages/_app.js
경로에 Custom App를 생성해서 커스터마이징할 수 있습니다.
Custom App는 주로 모든 페이지들의 공용 레이아웃을 정의하거나, Global CSS를 추가할 때 사용합니다.
만약 Custom App에 getInitialProps
함수를 사용하면 Automatic Static Optimization 기능이 비활성화 됩니다.
// component: 현재 페이지 컴포넌트
// pageProps: getInitialProps, getServerSideProps등으로 preloaded된 페이지 props
function MyApp({ Component, pageProps }) {
return (
<div>
hello world!
<Component {...pageProps} />
</div>
);
}
export default MyApp;