들어가기
/product/[id]/ 와 같이 path가 주어지는 page에서
정적 Page를 만드는 방법을 알아보자.
home page에서 product를 클릭 했을떄,
이동되는 DetailPage에 적용시켜 본다
import useMutation from '@libs/client/useMutation'
import useUser from '@libs/client/useUser'
import { cls } from '@libs/client/utils'
import client from '@libs/server/client'
import { Product, User } from '@prisma/client'
import type { GetStaticPaths, GetStaticProps, NextPage } from 'next'
import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { userAgent } from 'next/server'
import useSWR, { useSWRConfig } from 'swr'
import Button from '../../components/button'
import Layout from '../../components/layout'
interface ProductWithUser extends Product {
user: User
}
interface ItemDetailResponse {
ok: boolean
product: ProductWithUser
relatedProducts: Product[]
isLiked: boolean
}
const ItemDetail: NextPage<ItemDetailResponse> = ({
product,
relatedProducts,
isLiked,
}) => {
///3. 밑에서 getStaticProps에서 만든 Props들을 담아줌.
///NextPage 옆에 반드시 Type적어줄 것.
const { user, isLoading } = useUser()
const router = useRouter()
// console.log(router.query) ///product id를 따냄.
const { mutate } = useSWRConfig()
const { data, mutate: boundMutate } = useSWR<ItemDetailResponse>(
router.query.id ? `/api/products/${router.query.id}` : null
)
const [toggleFav] = useMutation(`/api/products/${router.query.id}/fav`)
const onFavClick = () => {
if (!data) return
boundMutate({ ...data, isLiked: !data.isLiked }, false)
toggleFav({})
//mutate('/api/users/me', { ok: false }, false)
//mutate('/api/users/me', (prev:any)=>({ok:!prev.ok}), false)
}
if (router.isFallback) {
return (
<Layout title="Loading for you!!!!!!!!">
<span>I love you</span>
</Layout>
)
}
///4. fallback: true에서 첫번쨰 방문자의 Loading시 첫 HTML 페이지를
///만들떄, Loading하는 동안 보여지는 Page
return (
<Layout canGoBack>
<div className="px-4 py-4">
<div className="mb-8">
<div className="relative ">
<Image
src={`https://imagedelivery.net/9VhLr461mPKMhcmTPOPfGg/${product.image}/public`}
className=" bg-slate-400 rounded-md"
width={600}
height={500}
alt="Picture of the author"
/>
<div className="flex cursor-pointer py-3 border-b items-center space-x-3">
<Image
width={48}
height={48}
src={`https://imagedelivery.net/9VhLr461mPKMhcmTPOPfGg/${product?.user?.avatar}/appleavatar`}
className="w-12 h-12 rounded-full bg-fuchsia-300"
alt="Picture of the author"
/>
<div>
<p className="text-sm font-medium text-gray-700">
{product?.user?.name}
</p>
<Link href={`/users/profiles/${product?.user?.id}`}>
<a className="rext-xs font-medium">View profile →</a>
</Link>
</div>
</div>
<div className="mt-10 ">
<h1 className="text-3xl font-bold text-gray-900 ">
{product?.name}
</h1>
<p className="text-3xl mt-3 pb-5 text-slate-400 font-black border-b-2">
{product?.price}원
</p>
<p className="text-base my-6 text-gray-800">
{product?.description}
</p>
<div className="flex items-center justify-between">
<Button large text="Talk to seller" />
<button
onClick={onFavClick}
className={cls(
'p-3 mx-3 rounde flex items-center justify-center',
isLiked
? 'text-red-500 hover:text-red-300'
: 'text-gray-400 hover:text-gray-200'
)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-9 w-9"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z"
clipRule="evenodd"
/>
</svg>
</button>
</div>
</div>
</div>
</div>
<div className="mt-6">
<h2 className="text-2xl font-bold text-gray-800">Similar items</h2>
<div className="mt-5 grid grid-cols-3 gap-4">
{relatedProducts.map((product) => (
<div key={product.id}>
<div className="h-36 w-full bg-slate-400" />
<h3>{product.name}</h3>
<p className="text-sm font-medium text-gray-900">
{product.price}원
</p>
</div>
))}
</div>
</div>
</div>
</Layout>
)
}
///2. getStaticPaths를 설정해 주면, Detail Page가 생성됨.
///fallback: 는 3가지 option이 있음, 'blocking' , false ,true
///'blocking'은 유저가 datailPage(product/[id])에 들어가게 되면,
///그 detail page를 HTML page로 만들어줌. 그 다음에 접속한 사람은
///바로 HTML페이지로 보게됨(속도 겁나 빠르긋죠)
/// true는 blocking과 같은데, 첫번쨰 방문자가 방문했을떄,
///HTML페이지로 만들어 주는 동안, Loading 페이지를 만들어 보여줄 수 있음.
///위에 example가 있음.
export const getStaticPaths: GetStaticPaths = () => {
return {
paths: [],
fallback: true,
}
}
///1.getStaticProps로 /api/product/[id].ts에 있는
///product, terms, relatedProducts, isLiked를
///불러오는 코드를 그대로 여기서 사용해서
///위 3개의 props를 불러온다.
///return부분에서 JSON.parse해 주는 부분을 집중해서 본다.!
export const getStaticProps: GetStaticProps = async (ctx) => {
if (!ctx?.params?.id) {
return {
props: {},
}
}
const product = await client.product.findUnique({
where: {
id: Number(ctx.params.id), ///id를 Int로 만들어줌.
},
include: {
user: {
select: {
id: true,
name: true,
avatar: true,
},
},
},
})
const terms = product?.name.split(' ').map((word) => ({
name: {
contains: word,
},
}))
const relatedProducts = await client.product.findMany({
where: {
OR: terms,
AND: {
id: {
not: Number(ctx.params.id),
},
},
},
})
const isLiked = false
return {
props: {
product: JSON.parse(JSON.stringify(product)),
relatedProducts: JSON.parse(JSON.stringify(relatedProducts)),
isLiked,
},
}
}
export default ItemDetail
NOTICE!!!!
정적페이지를 만드는 이유는 그 페이지를 이동했을때,
Loading을 하게 되면, 페이지가 열리는 시간이 길어진다.
그래서 페이지가 미리 HTML로 만들어 진다는것은 어메이징한 속도를
주는 것이다.