
๐ข Next.js ๊ณต์๋ฌธ์๋ฅผ ๋ณด๊ณ ์ ๋ฆฌํ ๋ด์ฉ์ ๋๋ค.
<Link>๋ HTML <a> ํ๊ทธ๋ฅผ ํ์ฅํ์ฌ ๊ฒฝ๋ก ๊ฐ ํ๋ฆฌํจ์นญ ๋ฐ ํด๋ผ์ด์ธํธ ์ธก ํ์์ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ์ปดํฌ๋ํธ์ด๋ค.import Link from 'next/link'
export default function Page() {
return <Link href="/dashboard">Dashboard</Link>
}
๋ค์๊ณผ ๊ฐ์ด next/link๋ฅผ importํ๊ณ ์ปดํฌ๋ํธ์์ href ํ๋กํผํฐ๋ฅผ ์ ๋ฌํด ์ฌ์ฉํ ์ ์๋ค.
| Props | Description | Type | Required |
|---|---|---|---|
| href | ์ด๋ํ ๊ฒฝ๋ก(URL) | string or Object | O |
| replace | ์ URL์ ํ์ฌ ๊ธฐ๋ก์ํ๋ก ๋์ฒด | Boolean | X |
| scroll | ์คํฌ๋กค ์์น ์ ์ง | Boolean | X |
| prefetch | ๊ฒฝ๋ก์ ํด๋น๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ ๋ก๋ | Boolean or null | X |
// string
<Link href="/dashboard">Dashboard</Link>
// object - Navigate to /about?name=test
<Link
href={{
pathname: '/about',
query: { name: 'test' },
}}
>
About
</Link>
import Link from 'next/link'
export default function Page() {
return (
<Link href="/dashboard" replace>
Dashboard
</Link>
)
}
import Link from 'next/link'
export default function Page() {
return (
<Link href="/dashboard" scroll={false}>
Dashboard
</Link>
)
}
๐ก ํ์ ์ ํ์ด์ง๊ฐ ๋ทฐํฌํธ(vh)์ ํ์๋์ง ์์ผ๋ฉด, Next.js๊ฐ ํ์ด์ง๋ก ์คํฌ๋กค๋๋ค.
ํ๋ฆฌํจ์นญ์ Link ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฉ์์ ๋ทฐํฌํธ์ ๋ค์ด์ฌ ๋(์ฒ์ ๋๋ ์คํฌ๋กค์ ํตํด) ๋ฐ์ํ๋ค.
Next.js๋ ํด๋ผ์ด์ธํธ ์ธก ํ์์ ์ฑ๋ฅ์ ํฅ์์ํค๊ธฐ ์ํด ๋งํฌ๋ ๊ฒฝ๋ก(href)์ ํด๋น ๋ฐ์ดํฐ๋ฅผ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ ๋ก๋ํฉ๋๋ค.
๐ก ์ฃผ์ํ ์ ์, ํ๋ฆฌํจ์นญ์ ํ๋ก๋์ ํ๊ฒฝ์์๋ง ํ์ฑํ๋๋ค.
๊ธฐ๋ณธ๊ฐ์ null์ด๋ค.
ํ๋ฆฌํจ์น ๋์์ ๊ฒฝ๋ก๊ฐ ์ ์ ์ธ์ง, ๋์ ์ธ์ง์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ค.
- ์ ์ ๊ฒฝ๋ก์ธ ๊ฒฝ์ฐ : ์ ์ฒด ๊ฒฝ๋ก๊ฐ ํ๋ฆฌํจ์น๋๋ค (๋ชจ๋ ๋ฐ์ดํฐ ํฌํจ)
- ๋์ ๊ฒฝ๋ก์ธ ๊ฒฝ์ฐ : loading.js ๊ฒฝ๊ณ๊ฐ ์๋ ๊ฐ์ฅ ๊ฐ๊น์ด ์ธ๊ทธ๋จผํธ(ํด๋)๊น์ง์ ๋ถ๋ถ ๊ฒฝ๋ก๊ฐ ํ๋ฆฌํจ์น๋๋ค.
import Link from 'next/link'
export default function Page() {
return (
<Link href="/dashboard" prefetch={false}>
Dashboard
</Link>
)
}
| ๊ฐ | Description |
|---|---|
| null | ์ ์ ๊ฒฝ๋ก์ผ ๊ฒฝ์ฐ, ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ํฌํจํด ์ ์ฒด ๊ฒฝ๋ก๊ฐ ํ๋ฆฌํจ์น๋๋ค. ๋์ ๊ฒฝ๋ก์ธ ๊ฒฝ์ฐ, loading.js ์ ๊ฐ์ฅ ๊ฐ๊น์ด ์ธ๊ทธ๋จผํธ๊น์ง ๋ถ๋ถ ๊ฒฝ๋ก๊ฐ ํ๋ฆฌํจ์น๋๋ค. |
| true | ์ ์ ๊ฒฝ๋ก์ ๋์ ๊ฒฝ๋ก ๋ชจ๋์ ๋ํด ์ ์ฒด ๊ฒฝ๋ก๊ฐ ํ๋ฆฌํจ์น๋๋ค. |
| false | ๋ทฐํฌํธ์ ๋ค์ด๊ฐ ๋์ ๋ง์ฐ์ค์ค๋ฒํ ๋ ๋ชจ๋ ํ๋ฆฌํจ์นญ์ด ์ํ๋์ง ์๋๋ค |
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>
)
}
๐ข Next.js
"์ฌ์ฉ ๋ผ์ฐํฐ ์ฌ์ฉ์ ๋ํ ํน๋ณํ ์๊ตฌ ์ฌํญ์ด ์๋ํ,useRouter๋ณด๋จLink์์๋ฅผ ์ฌ์ฉํด ๊ฒฝ๋ก ์ด๋์ ํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค."
โuseRouter์ ๊ฒฝ์ฐuse๊ฐ ๋ถ์ ๋งํผ, ์ฌ์ฉ์ด ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก ์ ํ๋๊ธฐ ๋๋ฌธ์ด๋ค.
window.history.pushState ๋ฐ window.history.replaceState ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ํ์ด์ง๋ฅผ ๋ค์ ๋ก๋ํ์ง ์๊ณ ๋ ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ก ์คํ์ ์
๋ฐ์ดํธํ ์ ์๋ค.pushState ๋ฐ replaceState ํธ์ถ์ Next.js ๋ผ์ฐํฐ์ ํตํฉ๋์ด ์ฌ์ฉ ๊ฒฝ๋ก์ ์ด๋ฆ ๋ฐ ์ฌ์ฉ ๊ฒ์ ๋งค๊ฐ ๋ณ์์ ๋๊ธฐํํ ์ ์๋ค.window.history.pushState๋ฅผ ์ฌ์ฉํ๋ฉด ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ก ์คํ์ ์ ํญ๋ชฉ์ ์ถ๊ฐํ ์ ์๋ค. ์ถ๊ฐ action์ด๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ์๊ฐ ๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ์ ๋๋ฅด๋ฉด ์ด์ ์ํ๋ก ๋์๊ฐ๋ค.ex) ์ํ ์ ๋ ฌํ๊ธฐ(์ค๋ฆ์ฐจ์/๋ด๋ฆผ์ฐจ์)
'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>
</>
)
}
๋ค๋ก๊ฐ๊ธฐ ๋ฒํผ์ ๋๋ฌ๋ ์ด์ ์ํ๋ก ๋์๊ฐ ์ ์๋ค.ex) app์ locale ์ ํํ๊ธฐ
'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>
</>
)
}
useRouter๋ฅผ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ redirect์ ์ด์ฉํด router.pushํน์ router.replace๋ฅผ ๋์ ํด ์ฌ์ฉํ ์ ์๋ค.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')
}
// ...
}
- redirect๋ ๋ด๋ถ์ ์ผ๋ก ์ค๋ฅ๋ฅผ ๋ฐ์์ํค๋ฏ๋ก, try-catch ๋ฌธ ์ธ๋ถ์์ ํธ์ถํด์ผ ํ๋ค.
- redirect๋ ๋ ๋๋ง ํ๋ก์ธ์ค ์ค ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ํธ์ถํ ์ ์์ง๋ง, ์ด๋ฒคํธ ํธ๋ค๋ฌ์์๋ ํธ์ถํ ์ ์๋ค. ์ด๋์
useRouterhook์ ์ฌ์ฉํ๋ค.- redirect๋ ์ ๋ URL๋ ํ์ฉํ๋ฉฐ ์ธ๋ถ ๋งํฌ๋ ๋ฆฌ๋๋ ์ ํ๋๋ฐ ์ฌ์ฉํ ์ ์๋ค.
- ๋ ๋๋ง ํ๋ก์ธ์ค ์ ์ ๋ฆฌ๋๋ ์ ์ ํ๊ณ ์ถ๋ค๋ฉด,
next.config.js๋๋ ๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก ๋ฆฌ๋๋ ์
์ ์๋ฒ ์์
์์๋ push(๋ธ๋ผ์ฐ์ ๊ธฐ๋ก ์คํ์ ์ ํญ๋ชฉ ์ถ๊ฐ)๋ฅผ ์ฌ์ฉํ๊ณ , ๊ทธ ์ธ์๋ replace(ํ์ฌ URL์ ๋ธ๋ผ์ฐ์ ๊ธฐ๋ก ์คํ์ผ๋ก ๋ฐ๊พธ๊ธฐ)๋ฅผ ์ฌ์ฉํ๋ค.
์ด๋ type ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ง์ ํด push/replace๋ก ์ ์ํ ์ ์๋ค.
| parameter | default value | description |
|---|---|---|
| push | server action์ ๊ธฐ๋ณธ๊ฐ | ๋ธ๋ผ์ฐ์ ๊ธฐ๋ก ์คํ์ ์ ํญ๋ชฉ ์ถ๊ฐ |
| replace | ๊ทธ ์ธ ๊ธฐ๋ณธ ๊ฐ | ํ์ฌ URL์ ๋ธ๋ผ์ฐ์ ๊ธฐ๋ก ์คํ์ผ๋ก ๋ฐ๊พธ๊ธฐ |
redirect() ํจ์๋ฅผ ํธ์ถํ๋ฉด NEXT_REDIREACT ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ณ , ํด๋น ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ธ๊ทธ๋จผํธ์ ๋ ๋๋ง์ด ์ข
๋ฃ๋๋ค.redirect() ํจ์๋ ํ์์คํฌ๋ฆฝํธ์ neverํ์
์ผ๋ก ๋ฐํ ํ์
์ ์ฌ์ฉํ์ง ์๋๋ค.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() ํจ์๋ Server action์ ํตํด ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์๋ ์ฌ์ฉํ ์ ์๋ค.useRouter hook์ ์ฌ์ฉํ ์ ์๋ค.
'use client'
import { navigate } from './actions'
export function ClientRedirect() {
return (
<form action={navigate}>
<input type="text" name="id" />
<button>Submit</button>
</form>
)
}
app/actions.ts
'use server'
import { redirect } from 'next/navigation'
export async function navigate(data: FormData) {
redirect(`/posts/${data.get('id')}`)
}