Linking and Navigating

Next.js에서 경로 간 이동하는 방법은 네 가지가 있습니다:

이 페이지에서는 각 옵션을 사용하는 방법과 내비게이션이 작동하는 방식을 자세히 설명합니다.

<Link> Component

<Link> 컴포넌트

<Link>는 HTML <a> 태그를 확장하여 경로 간 사전 로드와 클라이언트 측 내비게이션을 제공하는 내장 컴포넌트입니다. Next.js에서 경로 간 이동을 위한 기본적이고 권장되는 방법입니다.

다음과 같이 next/link에서 가져와 컴포넌트에 href prop을 전달하여 사용할 수 있습니다:

app/page.tsx
import Link from 'next/link'
 
export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>
}
app/page.js
import Link from 'next/link'
 
export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>
}

다른 선택적 prop들도 <Link>에 전달할 수 있습니다. 자세한 내용은 API 레퍼런스를 참조하세요.

Examples

예제

Linking to Dynamic Segments

동적 세그먼트에 링크하기

동적 세그먼트에 링크할 때 템플릿 리터럴과 인터폴레이션 (opens in a new tab)을 사용하여 링크 목록을 생성할 수 있습니다. 예를 들어, 블로그 게시물 목록을 생성하려면 다음과 같이 합니다:

app/blog/PostList.js
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>
  )
}

Checking Active Links

활성 링크 확인하기

usePathname()를 사용하여 링크가 활성화되었는지 확인할 수 있습니다. 예를 들어, 현재 pathname이 링크의 href와 일치하는지 확인하여 활성 링크에 클래스를 추가할 수 있습니다:

@/app/ui/nav-links.tsx
'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>
  )
}
@/app/ui/nav-links.tsx
'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>
  )
}

Scrolling to an id

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는 내비게이션 시 페이지가 뷰포트에 보이지 않으면 페이지로 스크롤합니다.

Disabling scroll restoration

스크롤 복원 비활성화

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()

useRouter 훅을 사용하여 클라이언트 컴포넌트에서 프로그래밍 방식으로 경로를 변경할 수 있습니다.

app/page.js
'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 레퍼런스를 참조하세요.

권장 사항: 특정 요구 사항이 없는 한 경로 간 내비게이션을 위해 <Link> 컴포넌트를 사용하는 것이 좋습니다.

redirect function

redirect 함수

서버 컴포넌트의 경우 redirect 함수를 사용하세요.

app/team/[id]/page.tsx
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')
  }
 
  // ...
}
app/team/[id]/page.js
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 (다른 페이지 보기)를 반환하며, 이는 POST 요청 결과로 성공 페이지로 리디렉션하는 데 자주 사용됩니다.
  • redirect는 내부적으로 오류를 발생시키므로 try/catch 블록 외부에서 호출해야 합니다.
  • redirect는 렌더링 프로세스 동안 클라이언트 컴포넌트에서 호출될 수 있지만, 이벤트 핸들러에서는 호출될 수 없습니다. 대신 useRouter을 사용할 수 있습니다.
  • redirect는 절대 URL도 허용하며 외부 링크로 리디렉션하는 데 사용할 수 있습니다.
  • 렌더링 프로세스 전에 리디렉션하려면 next.config.js 또는 미들웨어를 사용하세요.

자세한 내용은 redirect API 레퍼런스를 참조하세요.

Using the native History API

네이티브 History API 사용

Next.js는 네이티브 window.history.pushState (opens in a new tab)window.history.replaceState (opens in a new tab) 메서드를 사용하여 페이지를 다시 로드하지 않고 브라우저의 히스토리 스택을 업데이트할 수 있습니다.

pushStatereplaceState 호출은 Next.js 라우터와 통합되어 usePathnameuseSearchParams와 동기화할 수 있습니다.

window.history.pushState

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>
    </>
  )
}
'use client'
 
import { useSearchParams } from 'next/navigation'
 
export default function SortProducts() {
  const searchParams = useSearchParams()
 
  function updateSorting(sortOrder) {
    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

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>
    </>
  )
}
'use client'
 
import { usePathname } from 'next/navigation'
 
export function LocaleSwitcher() {
  const pathname = usePathname()
 
  function switchLocale(locale) {
    // 예: '/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>
    </>
  )
}

How Routing and Navigation Works

라우팅 및 내비게이션 작동 방식

App Router는 라우팅 및 내비게이션에 대해 하이브리드 접근 방식을 사용합니다. 서버에서 애플리케이션 코드는 경로 세그먼트에 의해 자동으로 코드 분할됩니다. 클라이언트에서 Next.js는 경로 세그먼트를 사전 로드하고 캐시합니다. 즉, 사용자가 새 경로로 내비게이션할 때 브라우저가 페이지를 다시 로드하지 않으며 변경된 경로 세그먼트만 다시 렌더링됩니다. 이는 내비게이션 경험과 성능을 향상시킵니다.

1. Code Splitting

  1. 코드 분할

코드 분할은 애플리케이션 코드를 더 작은 번들로 나누어 브라우저가 다운로드하고 실행할 수 있게 합니다. 이는 전송되는 데이터 양과 각 요청에 대한 실행 시간을 줄여 성능을 향상시킵니다.

서버 컴포넌트를 사용하면 애플리케이션 코드가 경로 세그먼트에 의해 자동으로 코드 분할됩니다. 이는 현재 경로에 필요한 코드만 내비게이션 시 로드된다는 것을 의미합니다.

2. Prefetching

  1. 사전 로드

사전 로드는 사용자가 방문하기 전에 백그라운드에서 경로를 미리 로드하는 방법입니다.

Next.js에서 경로를 사전 로드하는 두 가지 방법이 있습니다:

  • <Link> 컴포넌트: 경로가 사용자의 뷰포트에 보이게 되면 자동으로 사전 로드됩니다. 사전 로드는 페이지가 처음 로드될 때 또는 스크롤을 통해 뷰에 들어올 때 발생합니다.
  • router.prefetch(): useRouter 훅을 사용하여 프로그래밍 방식으로 경로를 사전 로드할 수 있습니다.

<Link>의 기본 사전 로드 동작(즉, prefetch prop이 지정되지 않았거나 null로 설정된 경우)은 loading.js의 사용 방식에 따라 다릅니다. 첫 번째 loading.js 파일까지 렌더링된 "트리"의 공유 레이아웃만 사전 로드되어 30초 동안 캐시됩니다. 이는 전체 동적 경로를 가져오는 비용을 줄여주며, 사용자에게 더 나은 시각적 피드백을 제공하기 위해 즉각적인 로딩 상태를 표시할 수 있습니다.

prefetch prop을 false로 설정하여 사전 로드를 비활성화할 수 있습니다. 또는 prefetch prop을 true로 설정하여 로딩 경계를 넘어 전체 페이지 데이터를 사전 로드할 수 있습니다.

자세한 내용은 <Link> API 레퍼런스를 참조하세요.

참고:

  • 사전 로드는 개발 환경에서는 활성화되지 않으며, 프로덕션 환경에서만 활성화됩니다.

3. Caching

  1. 캐싱

Next.js에는 라우터 캐시라고 불리는 메모리 내 클라이언트 측 캐시가 있습니다. 사용자가 애플리케이션을 탐색할 때 사전 로드된 경로 세그먼트와 방문한 경로의 React 서버 컴포넌트 페이로드가 캐시에 저장됩니다.

이는 내비게이션 시 새로운 서버 요청을 만드는 대신 가능한 한 캐시를 재사용하여 요청 수와 전송되는 데이터 양을 줄여 성능을 향상시킵니다.

라우터 캐시가 어떻게 작동하고 설정하는 방법에 대해 자세히 알아보세요.

4. Partial Rendering

  1. 부분 렌더링

부분 렌더링은 내비게이션 시 변경된 경로 세그먼트만 클라이언트에서 다시 렌더링되고, 공유된 세그먼트는 유지되는 것을 의미합니다.

예를 들어, 두 형제 경로인 /dashboard/settings/dashboard/analytics 간 내비게이션할 때 settingsanalytics 페이지가 렌더링되고, 공유된 dashboard 레이아웃은 유지됩니다.

How partial rendering works

부분 렌더링이 없다면, 각 내비게이션은 전체 페이지를 클라이언트에서 다시 렌더링하게 됩니다. 변경된 세그먼트만 렌더링하면 전송되는 데이터 양과 실행 시간을 줄여 성능을 향상시킬 수 있습니다.

5. Soft Navigation

  1. 소프트 내비게이션

브라우저는 페이지 간 내비게이션 시 "하드 내비게이션"을 수행합니다. Next.js App Router는 페이지 간 "소프트 내비게이션"을 가능하게 하여 변경된 경로 세그먼트만 다시 렌더링되도록 합니다(부분 렌더링). 이를 통해 내비게이션 중 클라이언트 React 상태가 유지됩니다.

6. Back and Forward Navigation

  1. 뒤로 및 앞으로 내비게이션

기본적으로 Next.js는 뒤로 및 앞으로 내비게이션을 위해 스크롤 위치를 유지하고 라우터 캐시의 경로 세그먼트를 재사용합니다.

7. Routing between pages/ and app/

  1. pages/app/ 간의 라우팅

pages/에서 app/로 점진적으로 마이그레이션할 때 Next.js 라우터는 두 경로 간의 하드 내비게이션을 자동으로 처리합니다. pages/에서 app/로의 전환을 감지하기 위해 앱 경로의 확률적 검사를 활용하는 클라이언트 라우터 필터가 있으며, 때때로 false positive를 초래할 수 있습니다. 기본적으로 이러한 발생은 매우 드물어야 하며, false positive 가능성을 0.01%로 구성합니다. 이 가능성은 next.config.jsexperimental.clientRouterFilterAllowedRate 옵션을 통해 사용자 지정할 수 있습니다. false positive 비율을 낮추면 클라이언트 번들에서 생성된 필터의 크기가 증가한다는 점을 유의해야 합니다.

또는 이 처리를 완전히 비활성화하고 pages/app/ 간의 라우팅을 수동으로 관리하려는 경우, next.config.js에서 experimental.clientRouterFilter를 false로 설정할 수 있습니다. 이 기능이 비활성화되면 기본적으로 앱 경로와 겹치는 페이지의 동적 경로는 제대로 내비게이션되지 않습니다.