dashboard 초기 목록 로딩방식을 csr로 변경

This commit is contained in:
dohyeons 2025-11-20 16:37:52 +09:00
parent 818fd5ac0d
commit eb6fe57839
2 changed files with 18 additions and 84 deletions

View File

@ -18,32 +18,26 @@ import { Pagination, PaginationInfo } from "@/components/common/Pagination";
import { DeleteConfirmModal } from "@/components/common/DeleteConfirmModal";
import { Plus, Search, Edit, Trash2, Copy, MoreVertical, AlertCircle, RefreshCw } from "lucide-react";
interface DashboardListClientProps {
initialDashboards: Dashboard[];
initialPagination: {
total: number;
page: number;
limit: number;
};
}
/**
*
* - CSR
* -
* - ///
*/
export default function DashboardListClient({ initialDashboards, initialPagination }: DashboardListClientProps) {
export default function DashboardListClient() {
const router = useRouter();
const { toast } = useToast();
const [dashboards, setDashboards] = useState<Dashboard[]>(initialDashboards);
const [loading, setLoading] = useState(false); // 초기 로딩은 서버에서 완료
// 상태 관리
const [dashboards, setDashboards] = useState<Dashboard[]>([]);
const [loading, setLoading] = useState(true); // CSR이므로 초기 로딩 true
const [error, setError] = useState<string | null>(null);
const [searchTerm, setSearchTerm] = useState("");
// 페이지네이션 상태
const [currentPage, setCurrentPage] = useState(initialPagination.page);
const [pageSize, setPageSize] = useState(initialPagination.limit);
const [totalCount, setTotalCount] = useState(initialPagination.total);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [totalCount, setTotalCount] = useState(0);
// 모달 상태
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
@ -73,17 +67,8 @@ export default function DashboardListClient({ initialDashboards, initialPaginati
}
};
// 초기 로드 여부 추적
const [isInitialLoad, setIsInitialLoad] = useState(true);
// 검색어/페이지 변경 시 fetch (초기 로딩 포함)
useEffect(() => {
// 초기 로드는 건너뛰기 (서버에서 이미 데이터를 가져왔음)
if (isInitialLoad) {
setIsInitialLoad(false);
return;
}
// 이후 검색어/페이지 변경 시에만 fetch
loadDashboards();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchTerm, currentPage, pageSize]);
@ -91,7 +76,7 @@ export default function DashboardListClient({ initialDashboards, initialPaginati
// 페이지네이션 정보 계산
const paginationInfo: PaginationInfo = {
currentPage,
totalPages: Math.ceil(totalCount / pageSize),
totalPages: Math.ceil(totalCount / pageSize) || 1,
totalItems: totalCount,
itemsPerPage: pageSize,
startItem: totalCount === 0 ? 0 : (currentPage - 1) * pageSize + 1,

View File

@ -1,73 +1,22 @@
import DashboardListClient from "@/app/(main)/admin/dashboard/DashboardListClient";
import { cookies } from "next/headers";
/**
* fetch
*
* -
* - CSR로
*/
async function getInitialDashboards() {
try {
// 서버 사이드 전용: 백엔드 API 직접 호출
// 도커 네트워크 내부에서는 서비스 이름 사용, 로컬에서는 127.0.0.1
const backendUrl = process.env.SERVER_API_URL || "http://backend:8080";
// 쿠키에서 authToken 추출
const cookieStore = await cookies();
const authToken = cookieStore.get("authToken")?.value;
if (!authToken) {
// 토큰이 없으면 빈 데이터 반환 (클라이언트에서 로드)
return {
dashboards: [],
pagination: { total: 0, page: 1, limit: 10 },
};
}
const response = await fetch(`${backendUrl}/api/dashboards/my?page=1&limit=10`, {
cache: "no-store", // 항상 최신 데이터
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${authToken}`, // Authorization 헤더로 전달
},
});
if (!response.ok) {
throw new Error(`Failed to fetch dashboards: ${response.status}`);
}
const data = await response.json();
return {
dashboards: data.data || [],
pagination: data.pagination || { total: 0, page: 1, limit: 10 },
};
} catch (error) {
console.error("Server-side fetch error:", error);
// 에러 발생 시 빈 데이터 반환 (클라이언트에서 재시도 가능)
return {
dashboards: [],
pagination: { total: 0, page: 1, limit: 10 },
};
}
}
/**
* ( )
* - +
* -
*/
export default async function DashboardListPage() {
const initialData = await getInitialDashboards();
export default function DashboardListPage() {
return (
<div className="bg-background flex min-h-screen flex-col">
<div className="space-y-6 p-6">
{/* 페이지 헤더 (서버에서 렌더링) */}
{/* 페이지 헤더 */}
<div className="space-y-2 border-b pb-4">
<h1 className="text-3xl font-bold tracking-tight"> </h1>
<p className="text-muted-foreground text-sm"> </p>
</div>
{/* 나머지 컨텐츠 (클라이언트 컴포넌트 + 서버 데이터) */}
<DashboardListClient initialDashboards={initialData.dashboards} initialPagination={initialData.pagination} />
{/* 클라이언트 컴포넌트 */}
<DashboardListClient />
</div>
</div>
);