NextJS Essential 3 - Navigating
Linking and Navigating
ref : https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating
Link Component
import Link from 'next/link'
export default function Page() {
return <Link href="/dashboard">Dashboard</Link>
}
Server
서버측에서 Route 변경할때는 아래 함수를 사용한다.
Server Components, Route Handlers, Server Actions 사용 가능
- redirect
- permanentRedirect
redirect
https://nextjs.org/docs/app/api-reference/functions/redirect
// eg) 서버 컴포넌트에서 데이터가 없는 경우 redirect
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')
}
}
permanentRedirect
https://nextjs.org/docs/app/api-reference/functions/permanentRedirect
// eg) 사용자 정보 업데이트 > 캐시 revalidate > redirect
'use server'
import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
export async function updateUsername(username: string, formData: FormData) {
try {
// Call database
} catch (error) {
// Handle errors
}
revalidateTag('username') // Update all references to the username
permanentRedirect(`/profile/${username}`) // Navigate to the new user profile
}
----
// eg) DB, API 조회 후 데이터가 없으면 /으로 리다이렉트 ( NOT FOUND 대신 홈으로 이동 시키고 싶은 경우 )
export default async function Playlist(props) {
const { params, searchParams } = props;
const playList = await getPlayListFromServer(searchParams?.list);
if (!playList) permanentRedirect(`/`); // Navigate to the new user profile
...
}
Client
클라이언트 컴포넌트에서 사용할 수 있는 훅들이다.
- useRouter
- useParams
- usePathname
- useSearchParams
useRouter
https://nextjs.org/docs/app/api-reference/functions/use-router
- 'use client' 을 써야 된다. > useRouter
- Server Components, you would redirect() instead.
- cf) next/router 은 사용하지 않는다.
// eg) navigate another page
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
// router.push
// router.replace
// router.back
// router.
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
}
// eg) navigate external page
<Link
className="font-medium underline underline-offset-4"
href="https://github.com/vercel/ai-chatbot"
target="_blank"
>
open source
</Link>{" "}
useParams
'use client'
import { useParams } from 'next/navigation'
export default function ExampleClientComponent() {
const params = useParams<{ tag: string; item: string }>()
// Route -> /shop/[tag]/[item]
// URL -> /shop/shoes/nike-air-max-97
// `params` -> { tag: 'shoes', item: 'nike-air-max-97' }
console.log(params)
return <></>
}
usePathname, useSearchParams
'use client'
import { usePathname, useSearchParams } from 'next/navigation'
function ExampleClientComponent() {
const pathname = usePathname()
// URL >>. Returned value
// /dashboard?v=2 >> '/dashboard'
// /blog/hello-world >> '/blog/hello-world'
function switchLocale(locale: string) {
// e.g. '/en/about' or '/fr/contact'
const newPath = `/${locale}${pathname}`
window.history.replaceState(null, '', newPath)
}
// URL -> `/dashboard?search=my-project`
// `search` -> 'my-project'
const searchParams = useSearchParams()
const search = searchParams.get('search')
}
notfound
- 적절한 리소스가 없는 경우, 404 not found 페이지로 이동한다.
// [product-id]/not-found.tsx
import { Button } from "@/components/ui/button";
import Link from "next/link";
export default function NotFound() {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-background text-foreground">
<h1 className="text-4xl font-bold mb-4">페이지를 찾을 수 없어요</h1>
<p className="text-xl mb-8">찾고 계신 페이지가 존재하지 않아요.</p>
<Button asChild>
<Link href="/">홈으로 돌아가기</Link>
</Button>
</div>
);
}
---
// [product-id]/page.tsx
import { notFound } from "next/navigation";
export default async function Page({ params }: PageProps) {
const productId = params?.["product-id"];
const product = await getProductById(Number(productId));
if (!product) notFound();
return (
<div>
<ProductDetail product={product} />
</div>
);
}
Window
replaceState
window.history.replaceState({}, "", `/chat/${chatId}`);
Server Routes 타입별 랜더링
Dynamic Routes (SSR)
https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes#example
interface LibraryProps {
params: {
id: string;
};
}
const sleep = async (ms) => new Promise((res) => setTimeout(res, ms));
export default async function Library({ params }: LibraryProps) {
console.log("-->params.id", params.id);
await sleep(2500);
console.log("--> get some data from server");
return (<div />)
}
generateStaticParams (SSG)
https://nextjs.org/docs/app/api-reference/functions/generate-static-params
// app/blog/[slug]/page.js
// Return a list of `params` to populate the [slug] dynamic segment
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({ slug: post.slug, }))
}
// Multiple versions of this page will be statically generated
// using the `params` returned by `generateStaticParams`
export default function Page({ params }) {
const { slug } = params
// ...
}
Parallel Routes
https://nextjs.org/docs/app/building-your-application/routing/parallel-routes
Intercepting Routes
https://nextjs.org/docs/app/building-your-application/routing/intercepting-routes