페이지 간 링크 생성
Link의 클라이언트 전환과 프리페치를 이해하고 정적·동적 href와 useRouter를 상황에 맞게 사용합니다.
웹 애플리케이션의 본질은 정보와 기능을 제공하고 사용자가 자유롭게 이동하도록 만드는 데 있습니다.
그래서 페이지를 연결하는 링크(Link)는 필수 요소입니다.
Next.js App Router에서는
기본 <a> 태그보다 Next.js의 <Link> 컴포넌트 사용을 권장합니다.
이 절에서는 <Link> 컴포넌트의 사용법과 그 이점, 그리고 실제 애플리케이션에서 링크를 효율적으로 관리하는 방법에 대해 자세히 알아보겠습니다.
<a> 태그 대신 <Link>를 사용하는 이유
일반적인 HTML의 <a> 태그를 사용하여 페이지를 이동할 경우, 브라우저는 해당 페이지를 처음부터 다시 로드합니다.
이는 전통적인 웹사이트에서는 일반적이었지만, React와 같은 SPA(Single Page Application) 프레임워크 기반의 애플리케이션에서는 전체 페이지가 깜빡이거나 새로고침되는 듯한 사용자 경험을 제공하게 됩니다.
반면, Next.js의 <Link> 컴포넌트를 사용하면 다음과 같은 중요한 이점을 얻을 수 있습니다.
-
클라이언트 사이드 탐색 (Client-side Navigation):
<Link>컴포넌트는 페이지 전체를 새로 로드하는 대신, JavaScript를 사용하여 필요한 부분만 업데이트합니다.이는 마치 데스크톱 애플리케이션처럼 부드럽고 빠른 페이지 전환을 가능하게 합니다.
브라우저의 새로고침 현상 없이 콘텐츠만 바뀌는 것을 경험할 수 있습니다.
-
코드 스플리팅 및 프리페칭 (Code Splitting and Pre-fetching): Next.js는 라우트별로 필요한 코드를 나눠 전달합니다.
<Link>를 추가하는 행위 자체가 코드 분할의 기준은 아닙니다.production의 자동 프리페치는 링크가 뷰포트에 들어오면 대상 라우트와 데이터를 정책에 따라 준비합니다. 네트워크와 동적 데이터 대기가 남을 수 있어 즉시 완료되는 전환을 보장하지 않습니다.
-
검색 엔진 최적화 (SEO) 이점:
<Link>컴포넌트는 여전히 내부적으로<a>태그로 렌더링되므로, 검색 엔진 크롤러가 웹사이트의 구조를 잘 파악하고 색인화하는 데 문제가 없습니다.
<Link> 컴포넌트 사용법
<Link> 컴포넌트를 사용하는 방법은 매우 간단합니다.
'next/link'에서 임포트:
링크를 사용하고자 하는 컴포넌트 파일 상단에 Link 컴포넌트를 임포트합니다.
import Link from 'next/link';href prop 지정:
<Link> 컴포넌트의 href prop에 이동하고자 하는 경로를 문자열로 지정합니다.
앱 내부 페이지로 이동할 때는 해당 App Router 경로와 일치하는 URL을 사용합니다. href에는 해시나 외부 URL도 쓸 수 있으며, 외부 사이트와 다운로드에는 일반 <a>도 적합합니다.
링크 텍스트나 요소 배치:
Next.js 13 이상에서는 <Link> 자체가 <a>로 렌더링됩니다.
따라서 <Link> 안에 다시 <a> 태그를 넣지 않고, 링크 텍스트나 스타일 속성을 <Link>에 직접 둡니다.
import Link from 'next/link';
export default function HomePage() {
return (
<div>
<h1>환영합니다!</h1>
<p>이곳은 저희 웹사이트의 홈 페이지입니다.</p>
<nav>
<ul>
<li>
<Link href="/about">회사 소개 페이지로 이동</Link>
</li>
<li>
<Link href="/dashboard" className="dashboard-link">
대시보드 보러 가기
</Link>
</li>
<li>
<Link href="/blog/my-first-post">
자세한 게시글 보기 (동적 라우트)
</Link>
</li>
</ul>
</nav>
</div>
);
}- 첫 번째
<li>에서는<Link>에 이동할 경로와 텍스트를 직접 넣었습니다. - 두 번째
<li>에서는<Link>에className을 적용해 버튼처럼 보이게 만들 수 있습니다. - 세 번째
<li>는 동적 라우트의 예시입니다. 나중에 자세히 다루겠지만,href에 동적인 경로를 지정할 수 있습니다.
동적 경로(Dynamic Paths)와 <Link>
[slug] 또는 [id]와 같이 대괄호로 정의된 동적 라우트 세그먼트를 가진 페이지로 이동할 때도 <Link> 컴포넌트를 사용합니다.
이때 href 속성에는 실제 경로를 문자열로 전달합니다.
예를 들어 src/app/products/[id]/page.tsx라는 동적 라우트가 있다면, 특정 상품 페이지로 이동하는 링크는 다음과 같이 작성할 수 있습니다.
// 특정 상품 목록 페이지에서
import Link from 'next/link';
function ProductList() {
const products = [
{ id: 'p001', name: '노트북' },
{ id: 'p002', name: '마우스' },
];
return (
<div>
<h1>상품 목록</h1>
<ul>
{products.map(product => (
<li key={product.id}>
<Link href={`/products/${product.id}`}>
{product.name} 상세 보기
</Link>
</li>
))}
</ul>
</div>
);
}보시는 것처럼, JavaScript의 템플릿 리터럴(Template Literal)을 사용하여 동적인 값을 href에 쉽게 삽입할 수 있습니다.
Link 컴포넌트의 추가적인 prop들 (선택)
<Link> 컴포넌트는 href 외에도 몇 가지 유용한 prop을 제공합니다.
-
replace:true로 설정하면 현재 히스토리 스택의 항목을 새 항목으로 교체합니다.현재 항목을 이력에 남기지 않을 때 사용합니다. 더 앞의 방문 기록까지 지우거나 뒤로 가기 자체를 막지는 않습니다. (기본값:
false)<Link href="/dashboard" replace> 대시보드로 이동 (뒤로 가기 방지) </Link> -
scroll:false는 Next.js의 이동 후 스크롤 관리를 끕니다. 기본값true도 무조건 문서 맨 위로 이동하는 것은 아니며, 새 페이지 영역이 현재 화면에 보이는지 등을 확인해 위치를 조정합니다.<Link href="/products#section-a" scroll={false}> 상품 페이지 특정 섹션으로 이동 (스크롤 유지) </Link> -
prefetch: 기본값은"auto"또는null이며, 정적·동적 라우트와 설정에 따라 준비하는 범위가 달라집니다.false는 뷰포트 진입과 hover 프리페치를 모두 끕니다. 자동 프리페치는 production에서만 실행됩니다.<Link href="/heavy-page" prefetch={false}> 무거운 페이지로 이동 (미리 로딩 안 함) </Link>
useRouter 훅 (클라이언트 컴포넌트에서)
<Link> 컴포넌트는 선언적으로 페이지 이동을 처리하기에 가장 좋은 방법입니다.
하지만 특정 이벤트(예: 폼 제출 후)에 따라 프로그래밍 방식으로 페이지를 이동해야 할 때는 Next.js가 제공하는 useRouter 훅을 사용할 수 있습니다.
useRouter는 클라이언트 컴포넌트에서만 사용할 수 있습니다.
"use client"; // 클라이언트 컴포넌트임을 명시
import { useRouter } from 'next/navigation'; // useRouter 임포트
export default function ClientButton() {
const router = useRouter(); // useRouter 훅 사용
const handleClick = () => {
// 버튼 클릭 시 프로그래밍 방식으로 /dashboard/settings 페이지로 이동
router.push('/dashboard/settings');
};
return (
<button onClick={handleClick}>
설정 페이지로 이동 (useRouter)
</button>
);
}useRouter 훅은 push, replace, refresh, back 등 다양한 메서드를 제공하여 라우팅을 세밀하게 제어할 수 있게 해줍니다.
이 훅에 대한 더 자세한 내용은 뒤에서 다룰 예정입니다.
탐색 옵션이 바꾸는 결과의 비교 기준입니다.
| 선택 | 구체적인 결과 | 남는 경계 |
|---|---|---|
| 기본 push | 이력이 홈 → 소개일 때 설정으로 가면 홈 → 소개 → 설정 | 뒤로 가면 소개로 돌아갈 수 있음 |
| replace | 같은 출발 이력에서 설정으로 가면 홈 → 설정 | 현재 소개 항목만 교체; 홈 등 더 앞선 이력은 남음 |
| scroll={false} | Next.js가 이동 후 스크롤 위치를 자동 조정하지 않음 | URL에 #id를 넣는 것과 그 요소로 스크롤하는 것은 구분 |
| prefetch={false} | 뷰포트 진입·hover에서 대상 준비를 하지 않음 | 클릭 탐색은 가능하며, 그때 필요한 데이터를 기다릴 수 있음 |
- 기본 push
- 구체적인 결과: 이력이 홈 → 소개일 때 설정으로 가면 홈 → 소개 → 설정남는 경계: 뒤로 가면 소개로 돌아갈 수 있음
- replace
- 구체적인 결과: 같은 출발 이력에서 설정으로 가면 홈 → 설정남는 경계: 현재 소개 항목만 교체; 홈 등 더 앞선 이력은 남음
- scroll={false}
- 구체적인 결과: Next.js가 이동 후 스크롤 위치를 자동 조정하지 않음남는 경계: URL에 #id를 넣는 것과 그 요소로 스크롤하는 것은 구분
- prefetch={false}
- 구체적인 결과: 뷰포트 진입·hover에서 대상 준비를 하지 않음남는 경계: 클릭 탐색은 가능하며, 그때 필요한 데이터를 기다릴 수 있음
이력은 동작을 설명하는 예시이며 실행 기록이 아닙니다. 자동 프리페치를 확인하려면 개발 서버와 production을 구분합니다.