링크 및 경로 이동
Next.js에서는 네 가지 방법으로 경로 간 이동을 할 수 있습니다:
<Link>
컴포넌트 사용useRouter
훅 사용 (클라이언트 컴포넌트)redirect
함수 사용 (서버 컴포넌트)- 네이티브 History API 사용
이 페이지에서는 각 옵션을 사용하는 방법과 경로 이동의 작동 방식에 대해 자세히 알아보겠습니다.
<Link>
컴포넌트
<Link>
는 HTML <a>
태그를 확장하여 프리페칭과 경로 간 클라이언트 사이드 경로 이동을 제공하는 내장 컴포넌트입니다. 이는 Next.js에서 경로 간 이동을 위한 주요하고 권장되는 방법입니다.
next/link
에서 가져와서 컴포넌트에 href
prop을 전달하여 사용할 수 있습니다:
import Link from "next/link";
export default function Page() {
return <Link href="/dashboard">Dashboard</Link>;
}
import Link from "next/link";
export default function Page() {
return <Link href="/dashboard">Dashboard</Link>;
}
<Link>
에 전달할 수 있는 다른 선택적 prop들이 있습니다. 자세한 내용은 API 참조를 참조하세요.
예시
동적 세그먼트로 링크하기
동적 세그먼트로 링크할 때, 템플릿 리터럴과 보간을 사용하여 링크 목록을 생성할 수 있습니다. 예를 들어, 블로그 게시물 목록을 생성하려면:
import Link from "next/link";
export default function PostList({ posts }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</li>
))}
</ul>
);
}
활성 링크 확인하기
usePathname()
을 사용하여 링크가 활성 상태인지 확인할 수 있습니다. 예를 들어, 활성 링크에 클래스를 추가하려면 현재 pathname
이 링크의 href
와 일치하는지 확인할 수 있습니다:
"use client";
import { usePathname } from "next/navigation";
import Link from "next/link";
import { FC, ReactNode } from "react";
export const Links: FC<{ children?: ReactNode }> = () => {
const pathname = usePathname();
return (
<nav>
<Link className={`link ${pathname === "/" ? "active" : ""}`} href="/">
Home
</Link>
<Link
className={`link ${pathname === "/about" ? "active" : ""}`}
href="/about"
>
About
</Link>
</nav>
);
};
"use client";
import { usePathname } from "next/navigation";
import Link from "next/link";
export function Links() {
const pathname = usePathname();
return (
<nav>
<Link className={`link ${pathname === "/" ? "active" : ""}`} href="/">
Home
</Link>
<Link
className={`link ${pathname === "/about" ? "active" : ""}`}
href="/about"
>
About
</Link>
</nav>
);
}
id
로 스크롤하기
Next.js App Router의 기본 동작은 새 경로의 상단으로 스크롤하거나 뒤로 및 앞으로 경로 이동에 대해 스크롤 위치를 유지하는 것입니다.
경로 이동 시 특정 id
로 스크롤하려면 URL에 #
해시 링크를 추가하거나 href
prop에 해시 링크를 전달할 수 있습니다. 이는 <Link>
가 <a>
요소로 렌더링되기 때문에 가능합니다.
<Link href="/dashboard#settings">Settings</Link>
// 출력
<a href="/dashboard#settings">Settings</a>
알아두면 좋은 점:
- 경로 이동 시 페이지가 뷰포트에 보이지 않으면 Next.js가 해당 페이지로 스크롤합니다.
스크롤 복원 비활성화하기
Next.js App Router의 기본 동작은 새 경로의 상단으로 스크롤하거나 뒤로 및 앞으로 경로 이동에 대해 스크롤 위치를 유지하는 것입니다. 이 동작을 비활성화하려면 <Link>
컴포넌트에 scroll={false}
를 전달하거나 router.push()
또는 router.replace()
에 scroll: false
를 전달할 수 있습니다.
// next/link
<Link href="/dashboard" scroll={false}>
Dashboard
</Link>
// useRouter
import { useRouter } from "next/navigation";
const router = useRouter();
router.push("/dashboard", { scroll: false });
useRouter()
훅(Hook)
useRouter
훅(hook)을 사용하면 클라이언트 컴포넌트에서 프로그래밍 방식으로 경로를 변경할 수 있습니다.
"use client";
import { useRouter } from "next/navigation";
export default function Page() {
const router = useRouter();
return (
<button type="button" onClick={() => router.push("/dashboard")}>
Dashboard
</button>
);
}
useRouter
의 전체 메서드 목록은 API 참조를 참조하세요.
권장사항:
useRouter
를 사용해야 하는 특정 요구사항이 없다면 경로 간 경로 이동에<Link>
컴포넌트를 사용하세요.
redirect
함수
서버 컴포넌트의 경우 대신 redirect
함수를 사용하세요.
import { redirect } from "next/navigation";
async function fetchTeam(id: string) {
const res = await fetch("https://...");
if (!res.ok) return undefined;
return res.json();
}
export default async function Profile({ params }: { params: { id: string } }) {
const team = await fetchTeam(params.id);
if (!team) {
redirect("/login");
}
// ...
}
import { redirect } from "next/navigation";
async function fetchTeam(id) {
const res = await fetch("https://...");
if (!res.ok) return undefined;
return res.json();
}
export default async function Profile({ params }) {
const team = await fetchTeam(params.id);
if (!team) {
redirect("/login");
}
// ...
}
알아두면 좋은 점:
redirect
는 기본적으로 307(임시 리디렉션) 상태 코드를 반환합니다. 서버 액션에서 사용될 때는 303(See Other)을 반환하며, 이는 일반적으로 POST 요청의 결과로 성공 페이지로 리디렉션할 때 사용됩니다.redirect
는 내부적으로 오류를 던지므로try/catch
블록 외부에서 호출해야 합니다.redirect
는 렌더링 프로세스 동안 클라이언트 컴포넌트에서 호출할 수 있지만 이벤트 핸들러에서는 호출할 수 없습니다. 대신useRouter
훅(hook)을 사용할 수 있습니다.redirect
는 절대 URL도 허용하며 외부 링크로 리디렉션하는 데 사용할 수 있습니다.- 렌더링 프로세스 전에 리디렉션하려면
next.config.js
또는 미들웨어를 사용하세요.
자세한 정보는 redirect
API 참조를 참조하세요.
네이티브 History API 사용하기
Next.js를 사용하면 네이티브 window.history.pushState
와 window.history.replaceState
메서드를 사용하여 페이지를 다시 로드하지 않고 브라우저의 히스토리 스택을 업데이트할 수 있습니다.
pushState
와 replaceState
호출은 Next.js 라우터와 통합되어 usePathname
과 useSearchParams
와 동기화할 수 있습니다.
window.history.pushState
브라우저의 히스토리 스택에 새 항목을 추가하는 데 사용합니다. 사용자는 이전 상태로 돌아갈 수 있습니다. 예를 들어, 제품 목록을 정렬하는 경우:
"use client";
import { useSearchParams } from "next/navigation";
export default function SortProducts() {
const searchParams = useSearchParams();
function updateSorting(sortOrder: string) {
const params = new URLSearchParams(searchParams.toString());
params.set("sort", sortOrder);
window.history.pushState(null, "", `?${params.toString()}`);
}
return (
<>
<button onClick={() => updateSorting("asc")}>Sort Ascending</button>
<button onClick={() => updateSorting("desc")}>Sort Descending</button>
</>
);
}
window.history.replaceState
브라우저의 히스토리 스택의 현재 항목을 교체하는 데 사용합니다. 사용자는 이전 상태로 돌아갈 수 없습니다. 예를 들어, 애플리케이션의 로케일을 전환하는 경우:
"use client";
import { usePathname } from "next/navigation";
export function LocaleSwitcher() {
const pathname = usePathname();
function switchLocale(locale: string) {
// 예: '/en/about' 또는 '/fr/contact'
const newPath = `/${locale}${pathname}`;
window.history.replaceState(null, "", newPath);
}
return (
<>
<button onClick={() => switchLocale("en")}>English</button>
<button onClick={() => switchLocale("fr")}>French</button>
</>
);
}
라우팅 및 경로 이동의 작동 방식
App Router는 라우팅 및 경로 이동에 하이브리드 접근 방식을 사용합니다. 서버에서는 애플리케이션 코드가 자동으로 경로 세그먼트별로 코드 분할됩니다. 그리고 클라이언트에서는 Next.js가 경로 세그먼트를 프리페치하고 캐시합니다. 이는 사용자가 새 경로로 이동할 때 브라우저가 페이지를 다시 로드하지 않고 변경된 경로 세그먼트만 다시 렌더링된다는 것을 의미합니다 - 이는 경로 이동 경험과 성능을 향상시킵니다.
1. 코드 분할
코드 분할을 통해 애플리케이션 코드를 더 작은 번들로 분할하여 브라우저에서 다운로드하고 실행할 수 있습니다. 이는 각 요청에 대한 데이터 전송량과 실행 시간을 줄여 성능을 향상시킵니다.
서버 컴포넌트를 사용하면 애플리케이션 코드가 경로 세그먼트별로 자동으로 코드 분할됩니다. 이는 경로 이동 시 현재 경로에 필요한 코드만 로드된다는 것을 의미합니다.
2. 프리페칭 (Prefetching)
프리페칭은 사용자가 방문하기 전에 백그라운드에서 경로를 미리 로드하는 방법입니다.
Next.js에서 경로를 미리 불러오는 두 가지 방법이 있습니다:
<Link>
컴포넌트: 경로는 사용자의 뷰포트에 보이게 되면 자동으로 미리 불러와집니다. 프리페칭은 페이지가 처음 로드되거나 스크롤을 통해 뷰로 들어올 때 발생합니다.router.prefetch()
:useRouter
훅을 사용하여 프로그래밍 방식으로 경로를 불러올 수 있습니다.
<Link>
의 기본 프리페칭 동작(즉, prefetch
prop이 지정되지 않거나 null
로 설정된 경우)은 loading.js
사용 여부에 따라 다릅니다. 공유 레이아웃에서 첫 번째 loading.js
파일까지의 렌더링된 "트리"만 불러와지고 30초
동안 캐시됩니다. 이는 전체 동적 경로를 가져오는 비용을 줄이고, 사용자에게 더 나은 시각적 피드백을 제공하기 위해 즉각적인 로딩 상태를 표시할 수 있다는 것을 의미합니다.
prefetch
prop을 false
로 설정하여 프리페칭을 비활성화할 수 있습니다. 또는 prefetch
prop을 true
로 설정하여 로딩 경계를 넘어 전체 페이지 데이터를 프리페치(prefetch)할 수 있습니다.
자세한 내용은 <Link>
API 참조를 참조하세요.
알아두면 좋은 점:
- 프리페칭(prefetching)은 개발 환경에서는 활성화되지 않고 프로덕션 환경에서만 활성화됩니다.
3. 캐싱
Next.js에는 라우터 캐시라고 하는 인메모리 클라이언트 사이드 캐시가 있습니다. 사용자가 앱을 탐색할 때 프리페치(prefetch)된 경로 세그먼트와 방문한 경로의 React 서버 컴포넌트 페이로드가 캐시에 저장됩니다.
이는 경로 이동 시 서버에 새 요청을 하는 대신 가능한 한 캐시가 재사용된다는 것을 의미합니다 - 요청 수와 전송되는 데이터를 줄여 성능을 향상시킵니다.
라우터 캐시의 작동 방식과 구성 방법에 대해 자세히 알아보세요.
4. 부분 렌더링
부분 렌더링은 경로 이동 시 변경된 경로 세그먼트만 클라이언트에서 다시 렌더링되고 공유 세그먼트는 보존된다는 것을 의미합니다.
예를 들어, /dashboard/settings
와 /dashboard/analytics
두 개의 형제 경로 사이를 이동할 때 settings
와 analytics
페이지가 렌더링되고 공유된 dashboard
레이아웃은 보존됩니다.

부분 렌더링 없이는 각 경로 이동이 클라이언트에서 전체 페이지를 다시 렌더링하게 됩니다. 변경된 세그먼트만 렌더링하면 전송되는 데이터 양과 실행 시간이 줄어들어 성능이 향상됩니다.
5. 소프트 경로 이동
브라우저는 페이지 간 이동 시 "하드 경로 이동"을 수행합니다. Next.js App Router는 페이지 간 "소프트 경로 이동"을 가능하게 하여 변경된 경로 세그먼트만 다시 렌더링되도록 합니다(부분 렌더링). 이를 통해 경로 이동 중에 클라이언트 React 상태가 보존될 수 있습니다.
6. 뒤로 및 앞으로 경로 이동
기본적으로 Next.js는 뒤로 및 앞으로 경로 이동에 대해 스크롤 위치를 유지하고 라우터 캐시의 경로 세그먼트를 재사용합니다.
7. pages/
와 app/
간의 라우팅
pages/
에서 app/
으로 점진적으로 마이그레이션할 때 Next.js 라우터는 두 디렉토리 간의 하드 경로 이동을 자동으로 처리합니다. pages/
에서 app/
으로의 전환을 감지하기 위해 앱 경로를 확률적으로 검사하는 클라이언트 라우터 필터가 사용되는데, 이는 때때로 잘못된 감지를 초래할 수 있습니다. 기본적으로 이러한 경우는 매우 드물며, 잘못된 감지 가능성은 0.01%로 설정되어 있습니다. 이 가능성은 next.config.js
의 experimental.clientRouterFilterAllowedRate
옵션을 통해 조정할 수 있습니다. 다만, 잘못된 감지 비율을 낮추면 클라이언트 번들에서 생성되는 필터의 크기가 증가할 수 있다는 점에 유의해야 합니다.
또는, 이 처리를 완전히 비활성화하고 pages/
와 app/
간의 라우팅을 수동으로 관리하려면 next.config.js
에서 experimental.clientRouterFilter
를 false로 설정할 수 있습니다. 이 기능이 비활성화되면 앱 경로와 겹치는 페이지의 동적 경로는 기본적으로 정상적으로 이동되지 않을 수 있습니다.