diff --git a/src/app/(after-login)/dashboard/[dashboardid]/_components/Column.tsx b/src/app/(after-login)/dashboard/[dashboardid]/_components/Column.tsx index 2311a51..8d63a45 100644 --- a/src/app/(after-login)/dashboard/[dashboardid]/_components/Column.tsx +++ b/src/app/(after-login)/dashboard/[dashboardid]/_components/Column.tsx @@ -1,6 +1,7 @@ "use client"; import { useEffect, useState, useRef } from "react"; +import { useIntersection } from "@/lib/hooks/useIntersection"; import { DashboardColumn, TaskCardList } from "@/lib/types"; import { fetchTaskCardList } from "@/lib/apis/cardsApi"; import { TOKEN_1 } from "@/lib/constants/tokens"; @@ -50,26 +51,11 @@ export default function Column({ id, title }: DashboardColumn) { handleLoad(); }, []); - useEffect(() => { - if (isLast) return; - - const observer = new IntersectionObserver( - (entries) => { - if (entries[0].isIntersecting) { - handleLoad(); - } - }, - { threshold: 0.5 } - ); - - const current = observerRef.current; - if (current) observer.observe(current); - - return () => { - if (current) observer.unobserve(current); - observer.disconnect(); - }; - }, [cursorId, isLoading, isLast]); + useIntersection({ + target: observerRef, + onIntersect: handleLoad, + disabled: isLast, + }); return (
diff --git a/src/app/(after-login)/mydashboard/layout.tsx b/src/app/(after-login)/mydashboard/layout.tsx index 1235300..4f4437f 100644 --- a/src/app/(after-login)/mydashboard/layout.tsx +++ b/src/app/(after-login)/mydashboard/layout.tsx @@ -4,13 +4,18 @@ import { TOKEN_1 } from "@/lib/constants/tokens"; import DashboardMenu from "@/components/layout/navbar/DashboardMenu"; import UserMenu from "@/components/layout/navbar/UserMenu"; +const PAGE_SIZE = 15; + export default async function Layout({ children, }: { children: React.ReactNode; }) { - // 가장 첫 번째 페이지 리스트 불러오도록 나중에 수정 - const { dashboards } = await fetchDashboardList(TOKEN_1); + const { dashboards } = await fetchDashboardList({ + token: TOKEN_1, + page: 1, + size: PAGE_SIZE, + }); const firstDashboardId = dashboards[0]?.id; diff --git a/src/app/(after-login)/mypage/layout.tsx b/src/app/(after-login)/mypage/layout.tsx index d990c3b..b8e4b91 100644 --- a/src/app/(after-login)/mypage/layout.tsx +++ b/src/app/(after-login)/mypage/layout.tsx @@ -4,13 +4,18 @@ import { TOKEN_1 } from "@/lib/constants/tokens"; import DashboardMenu from "@/components/layout/navbar/DashboardMenu"; import UserMenu from "@/components/layout/navbar/UserMenu"; +const PAGE_SIZE = 15; + export default async function Layout({ children, }: { children: React.ReactNode; }) { - // 가장 첫 번째 페이지 리스트 불러오도록 나중에 수정 - const { dashboards } = await fetchDashboardList(TOKEN_1); + const { dashboards } = await fetchDashboardList({ + token: TOKEN_1, + page: 1, + size: PAGE_SIZE, + }); const firstDashboardId = dashboards[0]?.id; diff --git a/src/components/layout/sidebar/SideMenuItem.tsx b/src/components/layout/sidebar/SideMenuItem.tsx index dfed3c5..f1580fe 100644 --- a/src/components/layout/sidebar/SideMenuItem.tsx +++ b/src/components/layout/sidebar/SideMenuItem.tsx @@ -1,5 +1,3 @@ -"use client"; - import { useIsMobile } from "@/lib/hooks/useCheckViewport"; import { useDashboardStore } from "@/lib/store/useDashboardStore"; import { useRouter } from "next/navigation"; @@ -26,7 +24,7 @@ export default function SideMenuItem({ onClick={() => { router.push(`/dashboard/${id}`); }} - className="flex items-center p-4 rounded hover:bg-violet-8 tablet:px-[10px] tablet:py-2 pc:p-3" + className="flex items-center w-full p-4 rounded hover:bg-violet-8 tablet:px-[10px] tablet:py-2 pc:p-3" style={isCurrent ? { background: "#f1effd" } : {}} disabled={isCurrent} > diff --git a/src/components/layout/sidebar/SideMenuList.tsx b/src/components/layout/sidebar/SideMenuList.tsx index 4105824..3ed3f90 100644 --- a/src/components/layout/sidebar/SideMenuList.tsx +++ b/src/components/layout/sidebar/SideMenuList.tsx @@ -1,21 +1,62 @@ +"use client"; + +import { useEffect, useState, useRef } from "react"; +import { useIntersection } from "@/lib/hooks/useIntersection"; import { DashboardList } from "@/lib/types"; import { fetchDashboardList } from "@/lib/apis/dashboardsApi"; import { TOKEN_1 } from "@/lib/constants/tokens"; import SideMenuItem from "./SideMenuItem"; -export default async function SideMenuList() { - const { dashboards, totalCount, cursorId } = - await fetchDashboardList(TOKEN_1); - const items: DashboardList[] = dashboards; +const PAGE_SIZE = 15; + +export default function SideMenuList() { + const [items, setItems] = useState([]); + const [page, setPage] = useState(1); + const [isLoading, setIsLoading] = useState(false); + const [isLast, setIsLast] = useState(false); + const observerRef = useRef(null); + + const handleLoad = async () => { + if (isLoading || isLast) return; + setIsLoading(true); + + try { + const { dashboards: newDashboards } = await fetchDashboardList({ + token: TOKEN_1, + size: PAGE_SIZE, + page, + }); + + if (newDashboards.length === 0) { + setIsLast(true); + } else { + setItems((prev) => [...prev, ...newDashboards]); + setPage((prev) => prev + 1); + } + } finally { + setIsLoading(false); + } + }; + + useEffect(() => { + handleLoad(); + }, []); - // 해당 값들 사용하게 되면 지울 테스트 코드들 - console.log(totalCount); - console.log(cursorId); + useIntersection({ + target: observerRef, + onIntersect: handleLoad, + disabled: isLast, + }); return ( -
- {items.map((item) => ( - +
+ {items.map((item, index) => ( +
+ +
))}
); diff --git a/src/components/layout/sidebar/index.tsx b/src/components/layout/sidebar/index.tsx index 22dd3cc..6666d95 100644 --- a/src/components/layout/sidebar/index.tsx +++ b/src/components/layout/sidebar/index.tsx @@ -5,11 +5,11 @@ import SideMenuList from "./SideMenuList"; export default function Sidebar() { return ( -
+
-
+
diff --git a/src/lib/apis/dashboardsApi.ts b/src/lib/apis/dashboardsApi.ts index d32aa35..df6c3f1 100644 --- a/src/lib/apis/dashboardsApi.ts +++ b/src/lib/apis/dashboardsApi.ts @@ -1,8 +1,16 @@ import { BASE_URL } from "@/lib/constants/urls"; -export async function fetchDashboardList(token: string) { +export async function fetchDashboardList({ + token, + page, + size, +}: { + token: string; + page: number; + size: number; +}) { const res = await fetch( - `${BASE_URL}/dashboards?navigationMethod=pagination&page=1&size=10`, + `${BASE_URL}/dashboards?navigationMethod=pagination&page=${page}&size=${size}`, { headers: { Accept: "application/json", diff --git a/src/lib/hooks/useIntersection.ts b/src/lib/hooks/useIntersection.ts new file mode 100644 index 0000000..467d3b8 --- /dev/null +++ b/src/lib/hooks/useIntersection.ts @@ -0,0 +1,38 @@ +"use client"; + +import { useEffect } from "react"; + +interface UseIntersectionProps { + target: React.RefObject; + onIntersect: () => void; + disabled?: boolean; + threshold?: number; +} + +export function useIntersection({ + target, + onIntersect, + disabled, + threshold = 0.5, +}: UseIntersectionProps) { + useEffect(() => { + if (disabled) return; + + const observer = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting) { + onIntersect(); + } + }, + { threshold: threshold } + ); + + const current = target.current; + if (current) observer.observe(current); + + return () => { + if (current) observer.unobserve(current); + observer.disconnect(); + }; + }, [target, onIntersect, disabled]); +}