Next.js - Basic

2020년 02월 05일

Next.js 튜토리얼을 접하며 중구난방으로 적은 내용입니다.

Route

Next에서 기본적으로 라우팅을 하는 법은 pages/ 디렉토리에 파일을 생성하는 것이다. 그 파일 이름을 기준으로 고정 url을 갖는다. (e.g. pages/events -> url이 events, pages/index -> url이 /)

기본적으로 Link 컴포넌트를 사용해서 클라이언트 사이드 route transition을 한다. 이 때 동적 url을 생성할 수 있는데, pages/p/1, pages/p/2 처럼 하고 싶을 때에는, pages/p/[id].js 를 생성한다. 그 다음 라우트 컴포넌트에서 <Link href='/p/[id]'/> 이렇게 설정하고, 부모 페이지에서 props로 id를 넘겨주면 된다.

(조금 덜 깔끔한 방법도 있다. 브라켓을 사용하지 않고 그냥 링크 컴포넌트에서 <Link href={/p/${props.id} /> 라고 사용해도 된다.)

여기서 href는 pages 디렉토리 내의 path를 나타내고, as는 브라우저 상의 url을 나타낸다.

아래는 블로그라는 페이지(/blog)에서 포스트 링크 리스트를 나열하고, 각 포스트 링크를 클릭하면 post 페이지로 이동하는 코드이다.

import Layout from '../comp/Layout'
import Link from 'next/link'

// 포스트 링크 컴포넌트는
// 부모 페이지로부터 전달받은 props로 동적 라우팅을 한다.
// href에 [] 내부의 id는 blog 페이지로부터 전달 받은
// 쿼리 패러미터이다.
const PostLink = props => (
  <li>
    <Link href="/p/[id]" as={`/p/${props.id}`}>
      <a>{props.id}</a>
    </Link>
  </li>
)

export default function Blog() {
  return (
    <Layout>
      <h1>Blog</h1>
      <ul>
        <PostLink id="hello-next" />
        <PostLink id="learn-next" />
      </ul>
    </Layout>
  )
}
import { useRouter } from 'next/router'
import Layout from '../../comp/Layout'

// post 페이지
// 라우트 객체에서 쿼리를 가져와 보여준다.
export default function post() {
  const router = useRouter()
  return (
    <Layout>
      <h1>{router.query.id}</h1>
    </Layout>
  )
}

useRouter 훅(useRouter는 리액트 훅이다)은 Next.js의 라우트 객체를 리턴한다. 라우트 객체를 통해 쿼리 패러미터, pathname 등에 접근할 수 있으며 push 등 메서드를 사용할 수 있다.

useRouter 말고 withRouter HOC를 사용할 수도 있다. withRouter는 어떤 컴포넌트에서든 라우터 객체에 접근할 수 있도록 한다. 사용방법은 사용하고자 하는 컴포넌트를 감싸서 export하고, props로 router객체를 받아서 사용하면 된다.

import { withRouter } from 'next/router'

function Page({ router }) {
  return <p>{router.pathname}</p>
}

export default withRouter(Page)

Comp

components, comp 등 컴포넌트를 넣어놓는 디렉토리를 생성하여 거기에서 만든 컴포넌트는 page를 구성하는 요소로 여기저기에 갖다 쓸 수 있다. 즉 nextjs에서는 pages에 해당 url에 매핑되는 뷰를 만들고, 그 뷰를 만들 때 comp 에서 컴포넌트를 갖다가 구성한다.

Fetch Data

각 페이지에 필요한 data를 fetch해서 준비해야 하는 경우 getInitialProps API를 사용한다.

getInitialProps : Standard API to fetch data for pages(Async Function). Through this, can fetch data from remote data source and pass it as props to page.

getInitialProps 는 페이지에서 디폴트 export 하는 컴포넌트에만 적용 가능하다.

  • pages/index.js
import Layout from '../components/MyLayout.js'
import Link from 'next/link'
import fetch from 'isomorphic-unfetch'

const Index = props => (
  <Layout>
    <h1>Batman TV Shows</h1>
    <ul>
      {props.shows.map(show => (
        <li key={show.id}>
          <Link href="/p/[id]" as={`/p/${show.id}`}>
            <a>{show.name}</a>
          </Link>
        </li>
      ))}
    </ul>
  </Layout>
)

Index.getInitialProps = async function() {
  console.log('init prop function is called')
  const res = await fetch('https://api.tvmaze.com/search/shows?q=batman')
  const data = await res.json()

  console.log(`show data fetched. count: ${data.length}`)
  return {
    shows: data.map(entry => entry.show),
  }
}

export default Index
  • pages/p/[id].js
import { useRouter } from 'next/router'
import Layout from '../../components/MyLayout.js'

const Post = props => {
  return (
    <Layout>
      <h1>{props.show.name}</h1>
    </Layout>
  )
}

export default Post

Post.getInitialProps = async function(context) {
  const { id } = context.query
  const res = await fetch(`https://api.tvmaze.com/shows/${id}`)
  const show = await res.json()

  console.log(`Fetched show: ${show.name}`)

  return { show }
}

참고

next.js router