Docs
Routing
Linking and Navigating

Linking and Navigating

Next.js 라우터를 사용하면 싱글 페이지 애플리케이션과 유사하게 페이지 간에 클라이언트 측 라우트 전환을 할 수 있습니다.

이 클라이언트 측 라우트 전환을 위해서 Link라는 React 컴포넌트가 제공됩니다.

import Link from 'next/link'
 
function Home() {
  return (
    <ul>
      <li>
        <Link href="/">Home</Link>
      </li>
      <li>
        <Link href="/about">About Us</Link>
      </li>
      <li>
        <Link href="/blog/hello-world">Blog Post</Link>
      </li>
    </ul>
  )
}
 
export default Home

위의 예에서는 여러 개의 링크를 사용합니다. 각 링크는 알려진 페이지에 대한 경로(href)를 매핑합니다:

  • /pages/index.js
  • /aboutpages/about.js
  • /blog/hello-worldpages/blog/[slug].js

Static Generation을 사용하는 페이지에서 (초기 화면 또는 스크롤을 통해 나타나는) 뷰포트의 모든 <Link />는 기본적으로 (해당 데이터를 포함하여) 미리 가져와집니다. 서버에서 렌더링된 라우트에 해당하는 데이터는 <Link />가 클릭 되었을 때 가져와집니다.

Linking to dynamic paths

보간을 이용해서 경로를 생성할 수 있는데, 이는 동적 라우트 세그먼트에 유용합니다. 예를 들어, 컴포넌트에 prop으로 전달된 게시글 목록을 표시하려면:

import Link from 'next/link'
 
function Posts({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href={`/blog/${encodeURIComponent(post.slug)}`}>
            {post.title}
          </Link>
        </li>
      ))}
    </ul>
  )
}
 
export default Posts

이 예제에서는 경로를 utf-8과 호환되도록 만들기 위해서 encodeURIComponent (opens in a new tab) 가 사용되었습니다.

또는, URL 객체를 사용할 수도 있습니다:

import Link from 'next/link'
 
function Posts({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link
            href={{
              pathname: '/blog/[slug]',
              query: { slug: post.slug },
            }}
          >
            {post.title}
          </Link>
        </li>
      ))}
    </ul>
  )
}
 
export default Posts

이제 보간을 사용하여 경로를 만드는 대신 href에서 URL 객체를 사용합니다:

  • pathnamepages 디렉터리에 있는 페이지의 이름입니다. 이 예제에서는 /blog/[slug]입니다.
  • query는 동적 세그먼트가 있는 객체입니다. 이 예제에서는 slug입니다.

Injecting the router

예제

React 컴포넌트에서 router 객체에 접근하기 위해서 useRouter 또는 withRouter를 사용할 수 있습니다.

일반적으로는 useRouter를 사용하는 것을 권장합니다.

Imperative Routing

next/link 가 대부분의 라우팅 요구사항을 충족할 수 있지만, next/router 문서를 참고해서 이것 없이도 클라이언트 측 탐색을 할 수 있습니다.

아래의 예제는 useRouter를 이용해서 기본적인 페이지 탐색 방법을 보여줍니다:

import { useRouter } from 'next/router'
 
export default function ReadMore() {
  const router = useRouter()
 
  return (
    <button onClick={() => router.push('/about')}>
      Click here to read more
    </button>
  )
}

Shallow Routing

예제

얕은 라우팅을 사용하면 데이터 가져오기 메서드인 getServerSideProps, getStaticProps, 그리고 getInitialProps 을 다시 실행하지 않고 URL을 변경할 수 있습니다.

(useRouter 또는 withRouter 에서 추가된) router 객체를 통해서 state를 잃지 않고 업데이트된 pathname을 받을 수 있습니다.

얕은 라우팅을 사용하기 위해서 shallow 옵션을 true로 설정합니다. 다음 예를 확인하세요:

import { useEffect } from 'react'
import { useRouter } from 'next/router'
 
// 현재 URL은 '/'입니다.
function Page() {
  const router = useRouter()
 
  useEffect(() => {
    // 항상 첫번째 랜더링 이후에 탐색을 수행합니다.
    router.push('/?counter=10', undefined, { shallow: true })
  }, [])
 
  useEffect(() => {
    // 카운터가 바뀌었습니다!
  }, [router.query.counter])
}
 
export default Page

URL은 /?counter=10 으로 업데이트되며 페이지는 교체되지 않고 라우트의 상태만 변경됩니다.

아래와 같이 componentDidUpdate (opens in a new tab)를 통해 URL 변경 사항을 확인할 수 있습니다:

componentDidUpdate(prevProps) {
  const { pathname, query } = this.props.router
  // verify props have changed to avoid an infinite loop
  // 무한 반복을 피하기 위해 props가 변경되었는지 확인합니다.
  if (query.counter !== prevProps.router.query.counter) {
    // 새 쿼리를 기반으로 데이터를 가져옵니다.
  }
}

Caveats

얕은 라우팅은 현재 페이지의 URL 변경에 대해서 동작합니다. 예를 들어, pages/about.js라는 다른 페이지가 있고 이것을 실행한다고 가정해 보겠습니다:

router.push('/?counter=10', '/about?counter=10', { shallow: true })

새 페이지이므로 얕은 라우팅을 요청했음에도 불구하고 현재 페이지를 언로드하고 새 페이지를 로드한 후 데이터 가져오기를 기다립니다.

미들웨어와 함께 얕은 라우팅을 사용하면 이전에 미들웨어 없이 사용했던 것처럼 새로운 페이지가 현재 페이지와 일치하는지 보장할 수 없습니다. 이는 미들웨어가 동적으로 다시 작성할 수 있기 때문이며, 이를 클라이언트 측에서 얕은 라우팅으로 건너뛴 데이터 가져오기 없이는 확인할 수 없으므로 얕은 라우트 변경은 항상 얕은 것으로 처리되어야 합니다.