getStaticPaths, getStaticProps

김종민·2022년 8월 29일
0

apple-market

목록 보기
35/37


들어가기
/product/[id]/ 와 같이 path가 주어지는 page에서
정적 Page를 만드는 방법을 알아보자.
home page에서 product를 클릭 했을떄,
이동되는 DetailPage에 적용시켜 본다

1. pages/products/[id].tsx

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 &rarr;</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로 만들어 진다는것은 어메이징한 속도를
주는 것이다.

profile
코딩하는초딩쌤

0개의 댓글