Linking and Navigating
May 31, 2024
NEXT JS
App Route
Routing
NEXT JS 공식문서를 참고하여 번역함.
경로 이동을 하는 데 4가지 방법이 있습니다:
<Link> Component
사용useRouter hook (Client Components)
사용redirect function (Server Components)
사용native History API
사용
이 페이지에서는 각 옵션을 사용하는 방법과 탐색 기능의 작동 방식을 자세히 설명합니다.
# <Link>
Component
<Link>
는 HTML의 <a>
태그를 확장하여 라우트 간에 미리 가져오기(prefetching)
와 클라이언트 측 탐색 기능을 제공하는 내장된 구성 요소입니다. 이는 Next.js에서 라우트 간 탐색을 위한 주요하고 권장되는 방법입니다.
이를 사용하려면 next/link에서 가져와서 해당 컴포넌트에 href 속성을 전달하면 됩니다:
app/page.tsx
import Link from 'next/link'export default function Page() {return <Link href="/dashboard">Dashboard</Link>}
<Link>
에 전달할 수 있는 다른 선택적 속성들도 있습니다. 자세한 내용은 API reference
를 확인하세요.
Examples
Linking to Dynamic Segments
동적 세그먼트
에 링크할 때는 템플릿 리터럴과 인터폴레이션
을 사용하여 링크 목록을 생성할 수 있습니다. 예를 들어, 블로그 게시물 목록을 생성하려면 다음과 같이 할 수 있습니다:
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/components/links.tsx
'use client'import { usePathname } from 'next/navigation'import Link from 'next/link'export function Links() {const pathname = usePathname()return (<nav><ul><li><Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">Home</Link></li><li><LinkclassName={`link ${pathname === '/about' ? 'active' : ''}`}href="/about">About</Link></li></ul></nav>)}
id
로 스크롤 하기
Next.js 앱 라우터의 기본 동작은 새로운 라우트로 이동할 때 맨 위로 스크롤하거나, 뒤로 및 앞으로 탐색할 때 스크롤 위치를 유지하는 것입니다.
탐색할 때 특정 id
로 스크롤하려면 URL 끝에 #
해시 링크를 추가하거나 href
속성에 해시 링크를 전달하면 됩니다. 이는 <Link>
가 <a>
요소로 렌더링되기 때문에 가능합니다.
<Link href="/dashboard#settings">Settings</Link>// Output<a href="/dashboard#settings">Settings</a>
알아두면 좋은 정보:
- 탐색 시 페이지가 뷰포트에 보이지 않으면 Next.js가 해당페이지
로 스크롤합니다.
스크롤 복구 비활성화
Next.js 앱 라우터의 기본 동작은 새로운 라우트로 이동할 때 맨 위로 스크롤하거나, 뒤로 및 앞으로 탐색할 때 스크롤 위치를 유지하는 것입니다. 이 동작을 비활성화하려면 <Link>
컴포넌트에 scroll={false}
를 전달하거나, router.push()
또는 router.replace()
에 scroll: false
를 전달하면 됩니다.
// next/link<Link href="/dashboard" scroll={false}>Dashboard</Link>
// useRouterimport { useRouter } from 'next/navigation'const router = useRouter()router.push('/dashboard', { scroll: false })
# useRouter()
hook
useRouter hook
은 클라이언트 컴포넌트
에서 프로그래밍 방식으로 라우트를 변경할 수 있게 해줍니다.
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 reference
를 확인하세요.
권장 사항:
useRouter
를 사용해야 하는 특정 요구 사항이 없는 한, 라우트 간 탐색에는<Link>
컴포넌트를 사용하세요.
# redirect
function
서버 컴포넌트
의 경우, 대신 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 undefinedreturn res.json()}export default async function Profile({ params }: { params: { id: string } }) {const team = await fetchTeam(params.id)if (!team) {redirect('/login')}// ...}
알아두면 좋은 정보:
-redirect
함수는 기본적으로 307 (임시 리디렉션) 상태 코드를 반환합니다. 서버 액션에서 사용될 때는 303 (다른 위치 보기) 상태 코드를 반환하며, 이는 POST 요청의 결과로 성공 페이지로 리디렉션할 때 흔히 사용됩니다.
-redirect
함수는 내부적으로 오류를 발생시키므로try/catch
블록 외부에서 호출해야 합니다.
-redirect
함수는 클라이언트 컴포넌트의 렌더링 과정에서 호출할 수 있지만, 이벤트 핸들러에서는 사용할 수 없습니다. 대신useRouter hook
을 사용할 수 있습니다.
-redirect
는 absolute URL도 허용하며 외부 링크로 리디렉션하는 데 사용할 수 있습니다.
- 렌더링 과정 전에 리디렉션을 수행하려면,next.config.js
또는Middleware
를 사용하세요.
# Using the native History API
Next.js는 페이지를 새로 고치지 않고도 브라우저의 히스토리 스택을 업데이트하기 위해 native 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) {// e.g. '/en/about' or '/fr/contact'const newPath = `/${locale}${pathname}`window.history.replaceState(null, '', newPath)}return (<><button onClick={() => switchLocale('en')}>English</button><button onClick={() => switchLocale('fr')}>French</button></>)}
# 라우팅 및 탐색 작동 방식
앱 라우터는 라우팅과 내비게이션에 하이브리드 접근 방식을 사용합니다. 서버에서는 애플리케이션 코드가 경로 세그먼트에 따라 자동으로 코드 분할
됩니다. 클라이언트에서는 Next.js가 경로 세그먼트를 prefetches
하고 caches
합니다. 이는 사용자가 새로운 경로로 이동할 때 브라우저가 페이지를 새로 고치지 않으며, 변경되는 경로 세그먼트만 다시 렌더링되어 내비게이션 경험과 성능이 향상됨을 의미합니다.
1. Code Splitting
코드 분할은 애플리케이션 코드를 더 작은 번들로 나누어 브라우저가 다운로드하고 실행할 수 있게 합니다. 이렇게 하면 각 요청에 대한 전송 데이터 양과 실행 시간이 줄어들어 성능이 향상됩니다.
서버 컴포넌트
를 사용하면 애플리케이션 코드가 경로 세그먼트에 따라 자동으로 코드 분할됩니다. 이는 현재 경로에 필요한 코드만 내비게이션 시 로드됨을 의미합니다.
2. Prefetching
프리패칭은 사용자가 방문하기 전에 경로를 백그라운드에서 미리 로드하는 방법입니다.
Next.js에서 경로를 프리패칭하는 두 가지 방법이 있습니다:
<Link>
컴포넌트: 경로가 사용자의 뷰포트에 보이게 되면 자동으로 프리패칭됩니다. 프리패칭은 페이지가 처음 로드될 때 또는 스크롤을 통해 보이게 될 때 발생합니다.router.prefetch()
:useRouter
훅을 사용하여 프로그래매틱하게 경로를 프리패칭할 수 있습니다.
<Link>
의 기본 프리패칭 동작(즉, prefetch
속성을 지정하지 않거나 null
로 설정했을 때)은 loading.js
의 사용 여부에 따라 다릅니다. 공유한 레이아웃만 첫 번째 loading.js
파일까지 렌더된 컴포넌트의 "tree"를 따라 프리패칭되고 30초
동안 캐시됩니다. 이는 전체 동적 경로를 가져오는 비용을 줄여주며, 사용자에게 더 나은 시각적 피드백을 제공하기 위해 즉각적인 로딩 상태
를 표시할 수 있음을 의미합니다.
prefetch
속성을 false
로 설정하여 프리패칭을 비활성화할 수 있습니다. 또는 prefetch
속성을 true
로 설정하여 loading boundaries를 넘어 전체 페이지 데이터를 프리패칭할 수 있습니다.
자세한 내용은 <Link> API reference
를 확인하세요.
알아두면 좋은 정보:
- 프리패칭은 개발 환경에서는 활성화되지 않고, 프로덕션 환경에서만 활성화됩니다.
3. Caching
Next.js에는 Router Cache
라고 불리는 메모리 내 클라이언트 측 캐시가 있습니다. 사용자가 앱 내를 탐색할 때, 프리패칭된
경로 세그먼트와 방문한 경로의 React 서버 컴포넌트 페이로드가 캐시에 저장됩니다.
이는 내비게이션 시 캐시를 최대한 재사용하여 서버에 새로운 요청을 보내는 대신 요청 수와 전송되는 데이터 양을 줄여 성능을 향상시킨다는 것을 의미합니다.
Router Cache
가 어떻게 작동하는지 및 설정 방법에 대해 자세히 알아보세요.
4. Partial Rendering
부분 렌더링은 내비게이션 시 변경되는 경로 세그먼트만 클라이언트에서 다시 렌더링되고, 공유되는 세그먼트는 유지된다는 것을 의미합니다.
예를 들어, 두 형제 경로인 /dashboard/settings
와 /dashboard/analytics
사이를 내비게이션할 때, settings
페이지와 analytics
페이지는 렌더링되고, 공유되는 dashboard
레이아웃은 유지됩니다.
부분 렌더링이 없으면 각 내비게이션마다 전체 페이지가 클라이언트에서 다시 렌더링됩니다. 변경된 세그먼트만 렌더링하면 전송되는 데이터 양과 실행 시간이 줄어들어 성능이 향상됩니다.
브라우저는 페이지 간 내비게이션 시 "hard navigation"을 수행합니다. Next.js 앱 라우터는 페이지 간 "soft navigation"을 가능하게 하여 변경된 경로 세그먼트만 다시 렌더링되도록 합니다(부분 렌더링). 이를 통해 내비게이션 동안 클라이언트의 React 상태가 유지됩니다.
기본적으로 Next.js는 뒤로 및 앞으로 내비게이션할 때 스크롤 위치를 유지하고, Router Cache
에서 경로 세그먼트를 재사용합니다.
7. Routing between pages/
and app/
pages/
에서 app/
으로 점진적으로 마이그레이션할 때, Next.js 라우터는 두 경로 간의 hard navigation을 자동으로 처리합니다. pages/
에서 app/
으로의 전환을 감지하기 위해 앱 경로의 확률적 검사를 활용하는 클라이언트 라우터 필터가 있으며, 이로 인해 가끔씩 잘못된 양성이 발생할 수 있습니다. 기본적으로 이러한 발생은 매우 드물어야 하며, *잘못된 양성의 가능성을 0.01%로 설정합니다. 이 가능성은 next.config.js
의 experimental.clientRouterFilterAllowedRate
옵션을 통해 사용자 정의할 수 있습니다. *잘못된 양성 비율을 낮추면 클라이언트 번들에서 생성된 필터의 크기가 증가한다는 점을 유의하는 것이 중요합니다.
잘못된 양성(false positives): "거짓 양성" 또는 "잘못된 양성"은 테스트나 검사 결과에서 실제로는 존재하지 않는 것을 존재한다고 잘못 판정하는 경우를 의미합니다. 예를 들어, 어떤 검사에서 실제로는 질병이 없는 사람을 질병이 있다고 진단하는 경우가 거짓 양성입니다.
또는, 이러한 처리를 완전히 비활성화하고 pages/
와 app/
간의 라우팅을 수동으로 관리하려면 next.config.js
에서 experimental.clientRouterFilter
를 false로 설정할 수 있습니다. 이 기능을 비활성화하면 기본적으로 pages의 동적 경로가 app 경로와 겹치는 경우 제대로 내비게이션되지 않습니다.