product page에서 pagination을 구현해 본다.
home page인 product page만 구현해 보고,
나머지 page에서는 시간 될때, 구현해 본다.
import withHandler, { ResponseType } from '@libs/server/withHandler'
import { NextApiRequest, NextApiResponse } from 'next'
import client from '../../../libs/server/client'
import { withApiSession } from '@libs/server/withSession'
async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseType>
) {
if (req.method === 'GET') {
const {
query: { page, limit }, ///req.query에서 page와
///limit를 받아옴.
///page는 현재page
///limit는 한페이지에 나타낼 갯수
} = req
const products = await client.product.findMany({
include: {
_count: {
select: {
favs: true,
},
},
},
take: Number(limit), ///한페이지에 나타낼 갯수
skip: (Number(page) - 1) * Number(limit),
///다음 페이지에 나타낼 item의 갯수
orderBy: { createdAt: 'asc' }, ///item 정렬순서
///desc 최신것부터, asc 옛날거부터~
})
res.json({
ok: true,
products,
})
}
if (req.method === 'POST') {
const { name, price, description } = req.body
const { user } = req.session
const product = await client.product.create({
data: {
name,
price: +price,
description,
image: 'aaa',
user: {
connect: {
id: user?.id,
},
},
},
})
res.json({ ok: true, product })
}
}
export default withApiSession(
withHandler({
methods: ['GET', 'POST'],
handler,
})
)
다음page 혹은 이전 page로 이동하는것을 나타내는 버튼을 component로
만들어 놓음
import { cls } from '@libs/client/utils'
interface PaginationButton {
children: React.ReactNode ///heroIcon에서 받아올 화살표
rOpt?: number
direction: 'left' | 'right' ///이전페이지 혹은 다음페이지
page: number
itemLength?: any ///item갯수
[key: string]: any
}
///home(index 페이지에서 받아올 props)
export default function PaginationButton({
children,
direction,
page,
itemLength,
onClick,
rest,
}: PaginationButton) {
return (
<button
{...rest}
onClick={onClick}
className={cls( ///prop의 조건에 따라 화살표 셋팅
direction === 'left' || (direction === 'right' && page <= 1)
? 'bottom-5'
: 'bottom-16',
direction === 'right' && itemLength < 5 ? 'hidden' : '',
///현재 한페이지에 5개 아이템이 보이게 셋팅해 놓음.
direction === 'left' && page <= 1 ? 'hidden' : '',
///page가 1이거나 0이면 left 화살표 안보이게 함.
///그 외 공통적으로 들어가는 부분
`fixed right-5 flex aspect-square w-14 cursor-pointer items-center justify-center rounded-full border-0 border-transparent bg-orange-400 text-white shadow-xl transition-all hover:bg-orange-500 sm:sticky sm-translate-x-[32rem]`
)}
>
{children} ///나중에 heroIcon에서 받아온 화살표 넣는 거
</button>
)
}
다른 부분은 생략하고 pagination부분만 알아보자
import PaginationButton from '@components/pagination-button'
import useUser from '@libs/client/useUser'
import { Product } from '@prisma/client'
import type { NextPage } from 'next'
import { useRouter } from 'next/router'
import { useState } from 'react'
import useSWR from 'swr'
import FloatingButton from '../components/floating-button'
import Item from '../components/item'
import Layout from '../components/layout'
export interface ProductWithCount extends Product {
_count: {
favs: number
}
}
interface ProductsResponse {
ok: boolean
products: ProductWithCount[]
}
const Home: NextPage = () => {
const { user, isLoading } = useUser()
const router = useRouter()
const [page, setPage] = useState(1) ///page기본값은 1로
const [limit, setLimit] = useState(5)
///한 page당 보여줄 item 개수는 5개로~
const { data } = useSWR<ProductsResponse>(
`/api/products?page=${page}&limit=${limit}`
)
///api요청시 page와 limit를 query에 넣어서 호출함.
const onPrevBtn = () => {
router.push(`${router.pathname}?page=${page - 1}&limit=${limit}`)
setPage((prev) => prev - 1)
}
///이전 page 버튼을 눌렀을때, 실행되는 함수
const onNextBtn = () => {
router.push(`${router.pathname}?page=${page + 1}&limit=${limit}`)
setPage((prev) => prev + 1)
}
///다음 page버턴을 눌렀을때, 실행되는 함수.
console.log(data)
return (
<Layout title="Home" hasTabBar>
<div className="flex flex-col space-y-5 py-10 mb-10">
{data?.products?.map((product) => (
<Item
key={product.id}
id={product.id}
title={product.name}
price={product.price}
hearts={product._count.favs}
comments={3}
></Item>
))}
<PaginationButton onClick={onPrevBtn} direction="left" page={page}>
///이전페이지 버튼눌렀을때, 보내질 props
///밑에 부분은 {...rest}로 받음
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M11 15l-3-3m0 0l3-3m-3 3h8M3 12a9 9 0 1118 0 9 9 0 01-18 0z"
/>
</svg>
</PaginationButton>
<PaginationButton
///다음Page 버튼을 눌렀을때, 실행될 버튼,
///itemLength가 들어가는 부분 확인할 것!!
onClick={onNextBtn}
direction="right"
page={page}
itemLength={data?.products.length}
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth="2"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M13 9l3 3m0 0l-3 3m3-3H8m13 0a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</PaginationButton>
<FloatingButton href="/products/upload">
<svg
className="h-12 w-15"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/>
</svg>
</FloatingButton>
</div>
</Layout>
)
}
export default Home
NOTICE!!! pagination은 열심히 배워서 까먹지 말고 잘 알아놓자!!!!