Dynamic Routes
Dynamic Routes는 관행적으로 [slug]라는 이름으로 많은 상품명에 따라 동일한 레이아웃을 가져갈 때 사용됩니다.
반드시 slug라는 이름을 사용해야하는 것은 아니며, 가독성을 위해 아래는 categoryname이라는 이름으로 예시를 작성하였습니다.
다음 폴더 구조에서의 예시
└── category
└── [categoryname]
└── page.tsx
page.tsx
import React from "react";
const Page = ({ params }: { params: { categoryname: string } }) => {
return (
<div>
<h1>{params.categoryname}</h1>
</div>
);
};
export default Page;
브라우저에 http://localhost:3000/category/dress 를 입력하면
dress가 렌더링됩니다.
React에서 react-router-dom을 사용한다면 수많은 Nested Routing을 처리하는 것만으로 코드가 길어지지만 Next.js에서는 위와 같은 params를 직접 받을 수 있습니다.
searchParams
searchParams는 상품 검색시 uri로 검색 정보를 넘길 때 사용됩니다.
page.tsx
const Page = ({
searchParams,
}: {
searchParams: { id: string | undefined };
}) => {
return (
<div>
<h1>{searchParams.id}</h1>
</div>
);
};
export default Page;
client component에서도 동일한 코드를 구현할 수 있다.
"use client";
import { useSearchParams } from "next/navigation";
const Page = () => {
const searchParams = useSearchParams();
const id = searchParams.get("id");
return (
<div>
<h1>{id}</h1>
</div>
);
};
export default Page;
loading, error boundary, zod, dynamic routes, searchParams 사용 예시
폴더 구조
├── events
├── [id]
│ ├── loading.tsx
│ └── page.tsx
└── error.tsx
zod 설치
pnpm add zod
설명은 주석으로 대체하였습니다.
import { Suspense } from 'react';
import Loading from './loading';
import { z } from 'zod';
interface Props {
params: { id: string };
searchParams: { [key: string]: string | string[] | undefined };
}
// http://localhost:8077/events/seoul?sp1=test&pageno=2 접속시
// params = { id: 'seoul' }
// searchParams = { sp1: 'test', pageno: '2' }
const pageNumberSchema = z.coerce.number().int().positive();
// searchParam value가 string이므로 숫자로 바꿔주고, 정수이면서 양수인지를 체크한다.
const Page = ({ params, searchParams }: Props) => {
console.log('search', searchParams);
const parsedPageNo = pageNumberSchema.safeParse(searchParams.pageno);
console.log('parsedPageNo', parsedPageNo);
// pageno값이 '2'이면 결과 : parsedPageNo { success: true, data: 2 }
// pageno값이 '0'이면 결과 : parsedPageNo { success: false, error: [Getter] }
if (!parsedPageNo.success) {
// 즉, 양수가 아니면 에러를 던진다.
throw new Error('Invalid page number');
}
// error.tsx는 같은 폴더에 있어도 되지만 없다면 상위 폴더의 error.tsx가 실행된다.
return (
<main className="py-24 text-center">
<Suspense key={parsedPageNo.data} fallback={<Loading />}>
<p>page number is {parsedPageNo.data}</p>
</Suspense>
</main>
);
};
export default Page;
loading.tsx에는 spinner 애니메이션이나 아이콘을 등록할 수도 있고,
UI library를 이용하여 <Skeleton />을 등록할 수도 있습니다.
error.tsx는 반드시 client component에서만 동작합니다.
error.tsx
'use client'; // Error components must be Client Components
import { useEffect } from 'react';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
// Log the error to an error reporting service
console.error(error);
}, [error]);
return (
<main className="py-24 text-center">
<p>{error.message}</p>
<button
className="mt-4 border bg-blue-500 px-4 py-2 text-white"
onClick={
// Attempt to recover by trying to re-render the segment
reset
}
>
Try again
</button>
</main>
);
}
만약 error.tsx가 없다면 다음과 같이 web app 전체가 crash되는 현상이 발생합니다.
위와 같이 error boundary를 통해
error를 처리하여 전체 app crash를 방지하고, reset 코드를 부여해줄 수 있습니다.
위의 경우 searchParams로 유효성 검사를 하였지만 react-query 혹은 fetch를 이용하여서도 동일한 방식으로 적용될 수 있습니다.
'Next.js 개발 가이드 > 02. 코딩 가이드 및 필수 패키지' 카테고리의 다른 글
6. Middleware (0) | 2024.01.26 |
---|---|
5. Internationalization (0) | 2024.01.24 |
3. tailwind-merge + clsx (0) | 2023.12.16 |
2. Json schema validator: Zod (0) | 2023.12.15 |
1. 코딩 스타일 가이드 (0) | 2023.12.15 |