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
/about
→pages/about.js
/blog/hello-world
→pages/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 객체를 사용합니다:
pathname
은pages
디렉터리에 있는 페이지의 이름입니다. 이 예제에서는/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 })
새 페이지이므로 얕은 라우팅을 요청했음에도 불구하고 현재 페이지를 언로드하고 새 페이지를 로드한 후 데이터 가져오기를 기다립니다.
미들웨어와 함께 얕은 라우팅을 사용하면 이전에 미들웨어 없이 사용했던 것처럼 새로운 페이지가 현재 페이지와 일치하는지 보장할 수 없습니다. 이는 미들웨어가 동적으로 다시 작성할 수 있기 때문이며, 이를 클라이언트 측에서 얕은 라우팅으로 건너뛴 데이터 가져오기 없이는 확인할 수 없으므로 얕은 라우트 변경은 항상 얕은 것으로 처리되어야 합니다.