๐ŸŽ‰ berenickt ๋ธ”๋กœ๊ทธ์— ์˜จ ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค. ๐ŸŽ‰
Front
NextJs
03-Linking๊ณผ Navigating

Linking and Navigating

Next.js์—์„œ ๊ฒฝ๋กœ๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ๋„ค ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

์ด ํŽ˜์ด์ง€์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๊ฐ ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๊ณ  ํƒ์ƒ‰์ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.


1. <Link> ์ปดํฌ๋„ŒํŠธ

<Link>๋Š” HTML <a> ํƒœ๊ทธ๋ฅผ ํ™•์žฅํ•˜์—ฌ ๊ฒฝ๋กœ ๊ฐ„ ํ”„๋ฆฌํŽ˜์นญ ๋ฐ ํด๋ผ์ด์–ธํŠธ ์ธก ํƒ์ƒ‰์„ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ์ œ๊ณต ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค. Next.js์—์„œ ๊ฒฝ๋กœ ๊ฐ„์„ ํƒ์ƒ‰ํ•˜๋Š” ๊ธฐ๋ณธ์ ์ด๊ณ  ๊ถŒ์žฅ๋˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

<Link>๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด next/link๋ฅผ importํ•˜๊ณ , href props๋ฅผ ์ปดํฌ๋„ŒํŠธ์— ์ „๋‹ฌํ•˜์„ธ์š”:

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

<Link>์— ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ์„ ํƒ์  props๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋” ๋งŽ์€ ์ •๋ณด๋Š” API ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.


1.1 ์˜ˆ์‹œ

1.1.1 ๋™์  ์„ธ๊ทธ๋จผํŠธ์— ๋งํฌ ๊ฑธ๊ธฐ

๋™์  ์„ธ๊ทธ๋จผํŠธ์— ๋งํฌ๋ฅผ ๊ฑธ ๋•Œ, ํ…œํ”Œ๋ฆฟ ๋ฆฌํ„ฐ๋Ÿด๊ณผ ์‚ฝ์ž…๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ๋งํฌ ๋ชฉ๋ก์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ ๋ชฉ๋ก์„ ์ƒ์„ฑํ•˜๋ ค๋ฉด:

app/blog/PostList.js
1
import Link from 'next/link'
2
3
export default function PostList({ posts }) {
4
return (
5
<ul>
6
{posts.map(post => (
7
<li key={post.id}>
8
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
9
</li>
10
))}
11
</ul>
12
)
13
}

1.1.2 ํ™œ์„ฑ ๋งํฌ ํ™•์ธํ•˜๊ธฐ

๋งํฌ๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด usePathname()์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํ˜„์žฌ pathname์ด ๋งํฌ์˜ href์™€ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•˜์—ฌ ํ™œ์„ฑ ๋งํฌ์— ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

app/components/links.tsx
1
'use client'
2
3
import { usePathname } from 'next/navigation'
4
import Link from 'next/link'
5
6
export function Links() {
7
const pathname = usePathname()
8
9
return (
10
<nav>
11
<ul>
12
<li>
13
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
14
Home
15
</Link>
16
</li>
17
<li>
18
<Link className={`link ${pathname === '/about' ? 'active' : ''}`} href="/about">
19
About
20
</Link>
21
</li>
22
</ul>
23
</nav>
24
)
25
}

1.1.3 id๋กœ ์Šคํฌ๋กคํ•˜๊ธฐ

Next.js App ๋ผ์šฐํ„ฐ์˜ ๊ธฐ๋ณธ ๋™์ž‘์€ ์ƒˆ ๊ฒฝ๋กœ์˜ ์ƒ๋‹จ์œผ๋กœ ์Šคํฌ๋กคํ•˜๊ฑฐ๋‚˜ ๋’ค๋กœ ๋ฐ ์•ž์œผ๋กœ ํƒ์ƒ‰์„ ์œ„ํ•ด ์Šคํฌ๋กค ์œ„์น˜๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฒฝ๋กœ ํƒ์ƒ‰์—์„œ ํŠน์ • ID๋กœ ์Šคํฌ๋กคํ•˜๋ ค๋ฉด URL์— # ํ•ด์‹œ ๋งํฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ href ํ”„๋กœํผํ‹ฐ์— ํ•ด์‹œ ๋งํฌ๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” <Link>๊ฐ€ <a> ์š”์†Œ๋กœ ๋ Œ๋”๋ง๋˜๋ฏ€๋กœ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

1
<Link href="/dashboard#settings">Settings</Link>
2
3
// Output
4
<a href="/dashboard#settings">Settings</a>

1.1.4 ์Šคํฌ๋กค ๋ณต์› ๋น„ํ™œ์„ฑํ™”ํ•˜๊ธฐ

Next.js App ๋ผ์šฐํ„ฐ์˜ ๊ธฐ๋ณธ ๋™์ž‘์€ ์ƒˆ ๊ฒฝ๋กœ์˜ ์ƒ๋‹จ์œผ๋กœ ์Šคํฌ๋กคํ•˜๊ฑฐ๋‚˜ ๋’ค๋กœ ๋ฐ ์•ž์œผ๋กœ ํƒ์ƒ‰์„ ์œ„ํ•ด ์Šคํฌ๋กค ์œ„์น˜๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๋™์ž‘์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋ ค๋ฉด, <Link> ์ปดํฌ๋„ŒํŠธ์— scroll={false}๋ฅผ ์ „๋‹ฌํ•˜๊ฑฐ๋‚˜, router.push() ๋˜๋Š” router.replace()์— scroll: false๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

1
// next/link
2
<Link href="/dashboard" scroll={false}>
3
Dashboard
4
</Link>
1
// useRouter
2
import { useRouter } from 'next/navigation'
3
4
const router = useRouter()
5
6
router.push('/dashboard', { scroll: false })

2. useRouter() Hook

useRouter hook์„ ์‚ฌ์šฉํ•˜๋ฉด, ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ๊ฒฝ๋กœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

app/page.js
1
'use client'
2
3
import { useRouter } from 'next/navigation'
4
5
export default function Page() {
6
const router = useRouter()
7
8
return (
9
<button type="button" onClick={() => router.push('/dashboard')}>
10
Dashboard
11
</button>
12
)
13
}

useRouter์˜ ๋” ๋งŽ์€ ์ •๋ณด๋Š” API ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

์ถ”์ฒœ: useRouter๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๋Š” ํŠน๋ณ„ํ•œ ์š”๊ตฌ์‚ฌํ•ญ์ด ์—†๋Š” ํ•œ, ๋ผ์šฐํŠธ ๊ฐ„์— ์ด๋™ํ•  ๋•Œ <Link> ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.


3. redirect function

์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒฝ์šฐ, redirect ๊ธฐ๋Šฅ์„ ๋Œ€์‹  ์‚ฌ์šฉํ•˜์„ธ์š”.

app/team/[id]/page.tsx
1
import { redirect } from 'next/navigation'
2
3
async function fetchTeam(id: string) {
4
const res = await fetch('https://...')
5
if (!res.ok) return undefined
6
return res.json()
7
}
8
9
export default async function Profile({ params }: { params: { id: string } }) {
10
const team = await fetchTeam(params.id)
11
if (!team) {
12
redirect('/login')
13
}
14
15
// ...
16
}

์•Œ์•„๋‘๋ฉด ์œ ์šฉ:

  • redirect๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ 307(์ž„์‹œ ๋ฆฌ๋””๋ ‰์…˜) ์ƒํƒœ ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
    • ์„œ๋ฒ„ ์•ก์…˜์—์„œ ์‚ฌ์šฉํ•˜๋ฉด 303(๊ธฐํƒ€ ์ฐธ์กฐ)์„ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ,
    • ์ด๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ POST ์š”์ฒญ์˜ ๊ฒฐ๊ณผ๋กœ ์„ฑ๊ณต ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋””๋ ‰์…˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • redirect๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋ฏ€๋กœ try/catch ๋ธ”๋ก ์™ธ๋ถ€์—์„œ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • redirect๋Š” ๋ Œ๋”๋ง ํ”„๋กœ์„ธ์Šค ์ค‘ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์—์„œ๋Š” ํ˜ธ์ถœํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
    • ๋Œ€์‹  useRouter ํ›…์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • redirect๋Š” ์ ˆ๋Œ€ URL๋„ ํ—ˆ์šฉํ•˜๋ฉฐ, ์™ธ๋ถ€ ๋งํฌ๋กœ ๋ฆฌ๋””๋ ‰์…˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ Œ๋”๋ง ํ”„๋กœ์„ธ์Šค ์ „์— ๋ฆฌ๋””๋ ‰์…˜ํ•˜๋ ค๋ฉด, next.config.js ๋˜๋Š” Middleware๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”.

์ž์„ธํ•œ ๋‚ด์šฉ์€ redirect API ์ฐธ์กฐ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.


4. Using the native History API

Next.js๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด window.history.pushState ๋ฐ window.history.replaceState ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŽ˜์ด์ง€๋ฅผ ๋‹ค์‹œ ๋กœ๋“œํ•˜์ง€ ์•Š๊ณ ๋„ ๋ธŒ๋ผ์šฐ์ €์˜ ๊ธฐ๋ก ์Šคํƒ์„ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

pushState ๋ฐ replaceState ํ˜ธ์ถœ์€ Next.js ๋ผ์šฐํ„ฐ์— ํ†ตํ•ฉ๋˜์–ด, usePathname ๋ฐ useSearchParams์™€ ๋™๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


4.1 window.history.pushState

์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์˜ history ์Šคํƒ์— ์ƒˆ ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ์ด์ „ state๋กœ ๋Œ์•„๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ œํ’ˆ ๋ชฉ๋ก์„ ์ •๋ ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

1
'use client'
2
3
import { useSearchParams } from 'next/navigation'
4
5
export default function SortProducts() {
6
const searchParams = useSearchParams()
7
8
function updateSorting(sortOrder: string) {
9
const params = new URLSearchParams(searchParams.toString())
10
params.set('sort', sortOrder)
11
window.history.pushState(null, '', `?${params.toString()}`)
12
}
13
14
return (
15
<>
16
<button onClick={() => updateSorting('asc')}>Sort Ascending</button>
17
<button onClick={() => updateSorting('desc')}>Sort Descending</button>
18
</>
19
)
20
}

4.2 window.history.replaceState

๋ธŒ๋ผ์šฐ์ €์˜ history ์Šคํƒ์—์„œ ํ˜„์žฌ ํ•ญ๋ชฉ์„ ๋ฐ”๊พธ๋ ค๋ฉด ์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ์ด์ „ state๋กœ ๋Œ์•„๊ฐˆ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ locale์„ ์ „ํ™˜ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค:

1
'use client'
2
3
import { usePathname } from 'next/navigation'
4
5
export function LocaleSwitcher() {
6
const pathname = usePathname()
7
8
function switchLocale(locale: string) {
9
// e.g. '/en/about' or '/fr/contact'
10
const newPath = `/${locale}${pathname}`
11
window.history.replaceState(null, '', newPath)
12
}
13
14
return (
15
<>
16
<button onClick={() => switchLocale('en')}>English</button>
17
<button onClick={() => switchLocale('fr')}>French</button>
18
</>
19
)
20
}

5. ๋‚ด๋น„๊ฒŒ์ด์…˜์˜ ์ž‘๋™ ๋ฐฉ์‹

App ๋ผ์šฐํ„ฐ๋Š” ๋ผ์šฐํŒ…๊ณผ navigation์— ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„์—์„œ๋Š”, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๊ฐ€ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋ณ„๋กœ ์ž๋™์œผ๋กœ code-split๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” Next.js๊ฐ€ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋ฅผ prefetchesํ•˜๊ณ  cachesํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•  ๋•Œ, ๋ธŒ๋ผ์šฐ์ €๋Š” ํŽ˜์ด์ง€๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•˜์ง€ ์•Š๊ณ  ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋งŒ ๋‹ค์‹œ ๋ Œ๋”๋งํ•˜์—ฌ ํƒ์ƒ‰ ํ™˜๊ฒฝ๊ณผ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.


5.1 ์ฝ”๋“œ ๋ถ„ํ• (code splitting)

์ฝ”๋“œ ๋ถ„ํ• ์„ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๋ฅผ ๋” ์ž‘์€ ๋ฒˆ๋“ค๋กœ ๋ถ„ํ• ํ•˜์—ฌ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ „์†ก๋˜๋Š” ๋ฐ์ดํ„ฐ์˜ ์–‘๊ณผ ๊ฐ ์š”์ฒญ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์ด ์ค„์–ด๋“ค์–ด ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.

์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๋ฅผ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋ณ„๋กœ ์ž๋™์œผ๋กœ ์ฝ”๋“œ ๋ถ„ํ• ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ํ˜„์žฌ ๊ฒฝ๋กœ์— ํ•„์š”ํ•œ ์ฝ”๋“œ๋งŒ ๋‚ด๋น„๊ฒŒ์ด์…˜์— ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค.


5.2 Prefetching

Prefetching์€ ์‚ฌ์šฉ์ž๊ฐ€ ๊ฒฝ๋กœ๋ฅผ ๋ฐฉ๋ฌธํ•˜๊ธฐ ์ „์— ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๊ฒฝ๋กœ๋ฅผ ๋ฏธ๋ฆฌ ๋กœ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

Next.js์—์„œ ๊ฒฝ๋กœ๋ฅผ ํ”„๋ฆฌํŽ˜์นญํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ๋‘ ๊ฐ€์ง€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

  • <Link> component: Routes๋Š” ์‚ฌ์šฉ์ž์˜ ๋ทฐํฌํŠธ์— ํ‘œ์‹œ๋  ๋•Œ ์ž๋™์œผ๋กœ Prefetching๋ฉ๋‹ˆ๋‹ค.

    • Prefetching์€ ํŽ˜์ด์ง€๊ฐ€ ์ฒ˜์Œ ๋กœ๋“œ๋  ๋•Œ ๋˜๋Š” ์Šคํฌ๋กค์„ ํ†ตํ•ด ๋ทฐํฌํŠธ์— ํ‘œ์‹œ๋  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • router.prefetch(): useRouter ํ›…์€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ฐฉ์‹์œผ๋กœ ๊ฒฝ๋กœ๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<Link>์˜ ํ”„๋ฆฌํŽ˜์นญ ๋™์ž‘์€ ์ •์  ๊ฒฝ๋กœ์™€ ๋™์  ๊ฒฝ๋กœ์— ๋”ฐ๋ผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค:

  • ์ •์  ๊ฒฝ๋กœ(Static Routes): prefetch ๊ธฐ๋ณธ๊ฐ’์€ true์ž…๋‹ˆ๋‹ค. ์ „์ฒด ๊ฒฝ๋กœ๊ฐ€ prefetched๋˜๊ณ  cached๋ฉ๋‹ˆ๋‹ค.
  • ๋™์  ๊ฒฝ๋กœ(Dynamic Routes): prefetch ๊ธฐ๋ณธ๊ฐ’์„ ์ž๋™์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ Œ๋”๋ง๋œ ์ปดํฌ๋„ŒํŠธ์˜ โ€˜ํŠธ๋ฆฌโ€™์—์„œ ์ฒซ ๋ฒˆ์งธ loading.js ํŒŒ์ผ๊นŒ์ง€ ๊ณต์œ  ๋ ˆ์ด์•„์›ƒ๋งŒ ํ”„๋ฆฌํŽ˜์น˜๋˜๊ณ  30์ดˆ ๋™์•ˆ ์บ์‹œ๋ฉ๋‹ˆ๋‹ค.
    • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ „์ฒด ๋™์  ๊ฒฝ๋กœ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋ฐ ๋“œ๋Š” ๋น„์šฉ์ด ์ค„์–ด๋“ค๊ณ ,
    • ์‚ฌ์šฉ์ž์—๊ฒŒ ์ฆ‰๊ฐ์ ์ธ ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ํ‘œ์‹œํ•˜์—ฌ ๋” ๋‚˜์€ ์‹œ๊ฐ์  ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”„๋ฆฌํŽ˜์นญ ํ”„๋กœํผํ‹ฐ๋ฅผ false๋กœ ์„ค์ •ํ•˜์—ฌ prefetch์„ ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ <Link> API ์ฐธ์กฐ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

์•Œ์•„๋‘๋ฉด ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค:

Prefetching์€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋ฉฐ, ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


5.3 Caching

Next.js์—๋Š” ๋ผ์šฐํ„ฐ ์บ์‹œ๋ผ๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ ํด๋ผ์ด์–ธํŠธ ์ธก ์บ์‹œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ์„ ํƒ์ƒ‰ํ•  ๋•Œ, ๋ฏธ๋ฆฌ ๊ฐ€์ ธ์˜จ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ์™€ ๋ฐฉ๋ฌธํ•œ ๊ฒฝ๋กœ์˜ React ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ํŽ˜์ด๋กœ๋“œ๊ฐ€ ์บ์‹œ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

์ฆ‰, ํƒ์ƒ‰ ์‹œ ์„œ๋ฒ„์— ์ƒˆ๋กœ ์š”์ฒญํ•˜๋Š” ๋Œ€์‹  ์บ์‹œ๋ฅผ ์ตœ๋Œ€ํ•œ ์žฌ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

  • ์š”์ฒญ ๋ฐ ๋ฐ์ดํ„ฐ ์ „์†ก ํšŸ์ˆ˜๋ฅผ ์ค„์—ฌ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, ํƒ์ƒ‰ ์‹œ ์„œ๋ฒ„์— ์ƒˆ๋กœ์šด ์š”์ฒญ์„ ํ•˜๋Š” ๋Œ€์‹  ์บ์‹œ๋ฅผ ์ตœ๋Œ€ํ•œ ์žฌ์‚ฌ์šฉํ•˜์—ฌ ์š”์ฒญ๊ณผ ๋ฐ์ดํ„ฐ ์ „์†ก ํšŸ์ˆ˜๋ฅผ ์ค„์—ฌ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.

๋ผ์šฐํ„ฐ ์บ์‹œ์˜ ์ž‘๋™ ๋ฐฉ์‹๊ณผ ๊ตฌ์„ฑ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด์„ธ์š”.


5.4 Partial Rendering

๋ถ€๋ถ„ ๋ Œ๋”๋ง์€ ๋‚ด๋น„๊ฒŒ์ด์…˜์—์„œ ๋ณ€๊ฒฝ๋œ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋งŒ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋‹ค์‹œ ๋ Œ๋”๋ง๋˜๊ณ  ๊ณต์œ  ์„ธ๊ทธ๋จผํŠธ๋Š” ์œ ์ง€๋œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, 2๊ฐœ์˜ ํ˜•์ œ ๊ฒฝ๋กœ์ธ /dashboard/settings์™€ /dashboard/analytics ์‚ฌ์ด๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” ๊ฒฝ์šฐ, settings ๋ฐ analytics ํŽ˜์ด์ง€๊ฐ€ ๋ Œ๋”๋ง๋˜๊ณ , ๊ณต์œ  ๋Œ€์‹œ๋ณด๋“œ ๋ ˆ์ด์•„์›ƒ์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

Partial Rendering

๋ถ€๋ถ„ ๋ Œ๋”๋ง์ด ์—†์œผ๋ฉด, ํƒ์ƒ‰ํ•  ๋•Œ๋งˆ๋‹ค ์ „์ฒด ํŽ˜์ด์ง€๊ฐ€ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋‹ค์‹œ ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ๋˜๋Š” ์„ธ๊ทธ๋จผํŠธ๋งŒ ๋ Œ๋”๋งํ•˜๋ฉด ์ „์†ก๋˜๋Š” ๋ฐ์ดํ„ฐ์˜ ์–‘๊ณผ ์‹คํ–‰ ์‹œ๊ฐ„์ด ์ค„์–ด๋“ค์–ด ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.


5.5 Soft Navigation

๋ธŒ๋ผ์šฐ์ €๋Š” ํŽ˜์ด์ง€ ์‚ฌ์ด๋ฅผ ์ด๋™ํ•  ๋•Œ โ€œHard Navigationโ€์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. Next.js ์•ฑ ๋ผ์šฐํ„ฐ๋Š” ํŽ˜์ด์ง€ ๊ฐ„ โ€œSoft Navigationโ€์„ ํ™œ์„ฑํ™”ํ•˜์—ฌ ๋ณ€๊ฒฝ๋œ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋งŒ ๋‹ค์‹œ ๋ Œ๋”๋ง๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค(๋ถ€๋ถ„ ๋ Œ๋”๋ง). ์ด๋ฅผ ํ†ตํ•ด ํƒ์ƒ‰ ์ค‘์— ํด๋ผ์ด์–ธํŠธ React ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


5.6 Back and Forward Navigation

๊ธฐ๋ณธ์ ์œผ๋กœ Next.js๋Š” ๋’ค๋กœ ๋ฐ ์•ž์œผ๋กœ ํƒ์ƒ‰์„ ์œ„ํ•œ ์Šคํฌ๋กค ์œ„์น˜๋ฅผ ์œ ์ง€ํ•˜๊ณ , ๋ผ์šฐํ„ฐ ์บ์‹œ์—์„œ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์žฌ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.


5.7 page์™€ app ์‚ฌ์ด์˜ ๋ผ์šฐํŒ…

page/์—์„œ app/์œผ๋กœ ์ ์ง„์ ์œผ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•  ๋•Œ Next.js ๋ผ์šฐํ„ฐ๋Š” ๋‘ ํŽ˜์ด์ง€ ์‚ฌ์ด์˜ ํ•˜๋“œ ๋„ค๋น„๊ฒŒ์ด์…˜์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. pages/์—์„œ app/๋กœ์˜ ์ „ํ™˜์„ ๊ฐ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์•ฑ ๊ฒฝ๋กœ์˜ ํ™•๋ฅ ์  ๊ฒ€์‚ฌ๋ฅผ ํ™œ์šฉํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ๋ผ์šฐํ„ฐ ํ•„ํ„ฐ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด ํ•„ํ„ฐ๋Š” ๋•Œ๋•Œ๋กœ ์˜คํƒ์„ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ ์˜คํƒ ๊ฐ€๋Šฅ์„ฑ์„ 0.01%๋กœ ์„ค์ •ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋Š” ๋งค์šฐ ๋“œ๋ฌผ๊ฒŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ€๋Šฅ์„ฑ์€ next.config.js์˜ experimental.clientRouterFilterAllowedRate ์˜ต์…˜์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜คํƒ๋ฅ ์„ ๋‚ฎ์ถ”๋ฉด ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค์—์„œ ์ƒ์„ฑ๋˜๋Š” ํ•„ํ„ฐ์˜ ํฌ๊ธฐ๊ฐ€ ์ปค์ง„๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜์„ธ์š”.

๋˜๋Š” ์ด ์ฒ˜๋ฆฌ๋ฅผ ์™„์ „ํžˆ ๋น„ํ™œ์„ฑํ™”ํ•˜๊ณ  page/์™€ app/ ๊ฐ„์˜ ๋ผ์šฐํŒ…์„ ์ˆ˜๋™์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ ค๋ฉด, next.config.js์—์„œ experimental.clientRouterFilter๋ฅผ false๋กœ ์„ค์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋ฉด, ์•ฑ ๊ฒฝ๋กœ์™€ ๊ฒน์น˜๋Š” ํŽ˜์ด์ง€์˜ ๋ชจ๋“  ๋™์  ๊ฒฝ๋กœ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๋Œ€๋กœ ํƒ์ƒ‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.