노마드 코더 Next.js 시작하기 를 보고 정리한 글입니다.
리액트 공식 사이트에서는 Next.js 를 소개하는 이유를 이렇게 설명하고 있다.
리액트의 CSR 방법이 가진 단점을 극복하기 위해 SSR 을 도입한다. 그러나 리액트에서 직접 SSR을 구축하기 보다는 Next js 라이브러리를 이용하면 편리하다.
리액트의 경우 유저가 보는 html 에는 <div class="root"></div>
만 가지고 있다. client-side-rendering 방식으로 인터넷 브라우저가 리액트, 자바스크립트를 모두 가져와서 화면을 그리는 방식이다.
만약 network에 있는 slow 3G 방식을 이용한다면 어떻게 될까 ? div 태그만 가진 html 파일만 나오게 된다. 노마드 코더 테스트 를 클릭해서 직접 해보면 이해가 갈 것 이다. 그 외에도 검색 크롤러가 빈 html 뼈대만 있을 때 어떤 페이지인지 인식을 할 수 없기 때문에 SEO 관점에서 좋지 않다.
Next js 는 pre-rendering 을 지원하는 리액트의 framework 다. (참고로 리액트는 라이브러리다.) Next js 는 각 페이지에 대에서 pre-rendering 양식을 선택할 수 있다. 아래의 두 가지 방식 형식이 존재한다.
Next js 는 각 페이지들을 사전에 미리 HTML 문서로 생성하여 가지고 있는 것이다. Pure한 React 에서 CSR 방식은 번들링 된 js가 클라이언트 단의 추가 랜더링을 관여했다면, Next js 방식은 빌드 타임 때 해당하는 페이지 별로 각각 HTML 문서를 미리 생성해서 가지고 있다가 서버에 요청이 들어오면 페이지를 반환해준다.
: 요청이 올 때 마다 해당하는 Html 문서를 그 때 그 때 생성하여 반환한다.
: HTML 을 빌드 타임 때 각 페이지 별로 생성하고, 해당 페이지로 요청이 올 경우 이미 생성된 HTML 문서를 반환한다.
SSR 혹은 SSG 와 함께 CSR을 사용할 수 있다. 그 까닭은 "CSR이 아니면 SSR 아니야?" 이런 것이 아니기 때문에 가능한 것이다.
function Profile() {
const [data, setData] = useState(null)
const [isLoading, setLoading] = useState(false)
useEffect(() => {
setLoading(true)
fetch('api/profile-data')
.then((res) => res.json())
.then((data) => {
setData(data)
setLoading(false)
})
}, [])
if (isLoading) return <p>Loading...</p>
if (!data) return <p>No profile data</p>
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}
1 ) getStaticProps
pre-rendering 시 데이터를 가져오기 위해 getStaticProps라는 비동기 함수를 사용할 수 있다. 이 함수는 빌드 타임에서 pre-render 시 가져온 데이터를 넘겨줄 수 있다.
// 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,
},
}
}
/:id
가 되는 경우이 함수는 build 타임에 호출되고, 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
페이지에 SSR 방식 랜더링을 사용하려면 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
Reference |
export default function Potato()
<a href="/about">About 페이지</a>
페이지는 위와 같이 a태그
를 사용할 수 있지만, 페이지 이동시마다 새로고침이 되면서 전체 코드를 다시 가져와야하는 일이 발생한다.
<Link href="/about">
<a>About 페이지</a>
</Link>
따라서 next/link 에 있는 것을 끌어와서 쓴다. 리액트와 같이
<Link href="/about">About 페이지</Link>
React-router-dom의 Link 를 이용하여 a 태그 없이 할 수 있다. 작동은 하지만 Link 자체에 인라이 스타일, className 등을 적용하기 어렵다. 따라서 a태그를 안에 넣어주는 것을 권장한다.