Next/link, route, <a> 뭐가 어떻게 다른데?
Next.js를 사용해보다가, <Link>와 Router 의 차이점에 대해서 궁금해져서 알아보았습니다.
<Link href="/about"><a>About me</a></Link>
<a href="/about">About me</a>
<button onClick={() => router.push("/about")}>About me</button>
위 코드들은 겉으로 보기엔 경로이동을 한다는 점에서는 비슷해보이지만, 각자 다른 점이 있습니다. 가장 큰 차이점은 "검색엔진최적화(SEO)"였습니다.
1. Route
router.push()를 이용하는 경우는 window.location과 비슷하게 동작합니다. <a>태그를 만들지 않기때문에 검색엔진최적화(SEO)을 신경쓰고 있다면(아마 Next.js를 사용하는 이상 대부분 신경을 쓰지 않을까요) 해당 링크는 크롤링되지 않아서 SEO에 불리합니다. 대부분 onClick과 같은 이벤트 핸들러와 같이 사용됩니다.
2. <Link>
하지만 <Link>태그는 <a>태그를 생성하기 때문에 웹사이트가 크롤링되어 SEO에 유리합니다. 페이지를 다시 로드하지않고 SPA동작처럼 "보이게" 만듭니다.
3. <a>
순수 HTML 요소로, 사용자를 새 페이지의 URL로 이동시키는 하이퍼링크를 생성합니다. 이때 페이지는 완전히 새로고침 됩니다. 만약 Next.js에서 <a>를 사용할 일이 있으면 <Link>태그로 바꾸는 것이 좋습니다. (하지만 <Link>가 언제나 <a>태그를 포함하는 것은 아닙니다)
결론 : 웬만하면 <Link>태그를 사용하는 것이 좋으며, SEO에 상관없이 SPA처럼 동작하게 하고싶을 때엔 router.push를 사용합니다.
- 아래는 라우팅 사용에 대한 공식문서를 번역한 내용입니다. (사용법에 관한 내용입니다)
https://nextjs.org/docs/routing/introduction
Next.js는 페이지를 기반으로 연결(라우팅)을 하는 파일시스템입니다. 파일이 '/pages'라는 폴더 안에 추가되면, 자동으로 연결(라우팅)이 되는것이죠. 'pages'폴더 안의 파일들은 가장 일반적인 패턴을 만들 때 사용됩니다.
Index
router는 자동으로 'index'라는 파일을 자동으로 루트 디렉토리에 연결시킵니다.
- pages/index.js → /
- pages/blog/index.js → /blog
중첩 연결
router는 nested 파일(폴더안의 폴더안의 폴더안의 폴더안의.... 파일)에도 사용가능합니다. 만약 중첩된 폴더 구조를 가지고 있따면 파일들은 자동으로 다음과 같이 연결됩니다.
- pages/blog/first-post.js → /blog/first-post
- pages/dashboard/settings/username.js → /dashboard/settings/username
동적 세그먼트 연결
동적으로 연결시키기 위해서는 [대괄호] 문법을 사용해야 합니다. 이렇게 하면 파라미터의 이름으로 연결됩니다.
- pages/blog/[slug].js → /blog/:slug (/blog/hello-world)
- pages/[username]/settings.js → /:username/settings (/foo/settings)
- pages/post/[...all].js → /post/* (/post/2020/id/title)
페이지간 연결하기
Next.js의 라우터는 SPA처럼 페이지간 이동시에 클라이언트 사이드(CSR)에서 렌더링을 하게 해주는데요. Link라고 불리는 리액트의 컴포넌트가 클라이언트 사이드(CSR)렌더링을 해줍니다.
import Link from 'next/link'
function Home() {
return (
<ul>
<li>
<Link href="/">
<a>Home</a>
</Link>
</li>
<li>
<Link href="/about">
<a>About Us</a>
</Link>
</li>
<li>
<Link href="/blog/hello-world">
<a>Blog Post</a>
</Link>
</li>
</ul>
)
}
export default Home
위와 같이 다중으로도 사용합니다. 각각은 href에 연결된 경로를 다음과 같이 연결해줍니다.
- / → pages/index.js
- /about → pages/about.js
- /blog/hello-world → pages/blog/[slug].js
뷰포트 안의 모든 <Link /> 는 디폴트로 static generation을 사용하는 페이지로 prefetch됩니다. (CSR이 아닌) SSR로 라우팅된 데이터는 prefetched 되지 않습니다.
동적 경로 연결하기
또한 동적 라우팅 경로로 interpolation(객체 안에 들어있는 프로퍼티....쯤으로 해석....) 를 사용할 수도 있습니다. 예를들어서 컴포넌트로 라우팅할 수 있는 글 목록을 보여주기 위해서 다음과 같이 할 수 있습니다 :
import Link from 'next/link'
function Posts({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${encodeURIComponent(post.slug)}`}>
<a>{post.title}</a>
</Link>
</li>
))}
</ul>
)
}
export default Posts
(encodeURIComponent는 경로의 utf-8호환성을 위해 사용되었습니다)
다른방법으론, URL 객체를 사용할 수도 있습니다 : interpolation을 사용하는 대신 href안에서 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 },
}}
>
<a>{post.title}</a>
</Link>
</li>
))}
</ul>
)
}
export default Posts
- pathname은 'pages' 폴더 안에 있는 파일 이름입니다. 여기서는 '/blog/[slug]' 가됩니다.
- query는 동적 객체입니다. 여기서는 'slug'가됩니다.