Ch.1에서 <Link>를 처음 사용했습니다. HTML의 <a> 태그와 비슷해 보이지만, 동작 방식이 근본적으로 다릅니다.
<a href="..."> | <Link href="..."> | |
|---|---|---|
| 동작 | 브라우저가 페이지 전체를 새로 요청 | 필요한 부분만 교체 (클라이언트 사이드 내비게이션) |
| 새로고침 | 발생 | 발생하지 않음 |
| 레이아웃 상태 | 초기화됨 (사이드바 열림/닫힘 등 유실) | 유지됨 |
| 속도 | 느림 (HTML, CSS, JS 전부 다시 로드) | 빠름 (변경된 컴포넌트만 교체) |
직접 확인해 보세요. 루트 레이아웃에서 <Link>를 <a>로 바꿔보면, 페이지를 이동할 때마다 화면이 깜빡이며 전체가 다시 로드됩니다.
<Link>는 클릭 시 브라우저의 기본 동작(전체 페이지 요청)을 막고, JavaScript로 URL을 변경한 뒤 필요한 컴포넌트의 렌더링 결과를 서버에서 가져와 교체합니다. 이것이 클라이언트 사이드 내비게이션입니다.
<Link>에는 숨겨진 기능이 하나 더 있습니다. 프로덕션에서 링크가 뷰포트(화면)에 보이면, Next.js가 백그라운드에서 해당 경로를 prefetch합니다. 사용자가 클릭하기 전에 이미 준비가 되어 있으므로, 이동이 거의 즉시 일어날 수 있습니다.
// 기본 동작(auto): 정적 라우트는 전체, 동적 라우트는 loading.tsx 경계까지 부분 prefetch
<Link href='/blog'>블로그</Link>
// prefetch 끄기
<Link href='/blog' prefetch={false}>블로그</Link>
기본값인 prefetch="auto"에서는 정적 라우트는 전체 경로를, 동적 라우트는 가장 가까운 loading.tsx 경계까지 부분적으로 prefetch합니다. 따라서 어떤 요청이 발생하는지는 라우트 성격에 따라 조금씩 달라질 수 있습니다.
npm run build && npm start로 프로덕션 서버를 실행한 뒤, 개발자 도구의 네트워크 탭에서 확인해 보세요. 정적 라우트와 동적 라우트는 prefetch 요청 형태가 다를 수 있으므로, loading.tsx 유무와 함께 보면 더 잘 보입니다.
usePathname()은 현재 URL의 경로 부분을 반환하는 Hook입니다. 현재 페이지를 내비게이션에서 강조하거나, 특정 경로에서만 UI를 다르게 보여주는 등 경로에 따라 동작을 분기할 때 사용합니다.
'use client';
import { usePathname } from 'next/navigation';
export default function Example() {
const pathname = usePathname();
// /blog/nextjs-routing 접속 시 → "/blog/nextjs-routing"
// /about 접속 시 → "/about"
return <p>현재 경로: {pathname}</p>;
}
'use client'가 필요합니다. 서버 컴포넌트에서는 사용할 수 없습니다.
usePathname과 Link를 조합하면 현재 페이지를 강조하는 내비게이션 링크를 만들 수 있습니다:
// app/components/NavLink.tsx
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
export function NavLink({
href,
children,
}: {
href: string;
children: React.ReactNode;
}) {
const pathname = usePathname();
const isActive = pathname === href || pathname.startsWith(href + '/');
return (
<Link
href={href}
className={isActive ? 'font-bold text-blue-600' : 'text-inherit'}
>
{children}
</Link>
);
}
pathname.startsWith(href + '/')는 하위 경로도 활성 상태로 표시하기 위한 패턴입니다. 예를 들어 /blog/nextjs-routing에 접속해도 /blog 링크가 활성화됩니다.
참고로, usePathname()은 URL의 path 부분만 반환하며 쿼리 스트링은 포함하지 않습니다. 쿼리 스트링은 useSearchParams()를 사용합니다.