dashboard 초기 목록 로딩방식을 csr로 변경
This commit is contained in:
@@ -18,32 +18,26 @@ import { Pagination, PaginationInfo } from "@/components/common/Pagination";
|
|||||||
import { DeleteConfirmModal } from "@/components/common/DeleteConfirmModal";
|
import { DeleteConfirmModal } from "@/components/common/DeleteConfirmModal";
|
||||||
import { Plus, Search, Edit, Trash2, Copy, MoreVertical, AlertCircle, RefreshCw } from "lucide-react";
|
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 router = useRouter();
|
||||||
const { toast } = useToast();
|
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 [error, setError] = useState<string | null>(null);
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
|
||||||
// 페이지네이션 상태
|
// 페이지네이션 상태
|
||||||
const [currentPage, setCurrentPage] = useState(initialPagination.page);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(initialPagination.limit);
|
const [pageSize, setPageSize] = useState(10);
|
||||||
const [totalCount, setTotalCount] = useState(initialPagination.total);
|
const [totalCount, setTotalCount] = useState(0);
|
||||||
|
|
||||||
// 모달 상태
|
// 모달 상태
|
||||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
@@ -73,17 +67,8 @@ export default function DashboardListClient({ initialDashboards, initialPaginati
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 초기 로드 여부 추적
|
// 검색어/페이지 변경 시 fetch (초기 로딩 포함)
|
||||||
const [isInitialLoad, setIsInitialLoad] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// 초기 로드는 건너뛰기 (서버에서 이미 데이터를 가져왔음)
|
|
||||||
if (isInitialLoad) {
|
|
||||||
setIsInitialLoad(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이후 검색어/페이지 변경 시에만 fetch
|
|
||||||
loadDashboards();
|
loadDashboards();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [searchTerm, currentPage, pageSize]);
|
}, [searchTerm, currentPage, pageSize]);
|
||||||
@@ -91,7 +76,7 @@ export default function DashboardListClient({ initialDashboards, initialPaginati
|
|||||||
// 페이지네이션 정보 계산
|
// 페이지네이션 정보 계산
|
||||||
const paginationInfo: PaginationInfo = {
|
const paginationInfo: PaginationInfo = {
|
||||||
currentPage,
|
currentPage,
|
||||||
totalPages: Math.ceil(totalCount / pageSize),
|
totalPages: Math.ceil(totalCount / pageSize) || 1,
|
||||||
totalItems: totalCount,
|
totalItems: totalCount,
|
||||||
itemsPerPage: pageSize,
|
itemsPerPage: pageSize,
|
||||||
startItem: totalCount === 0 ? 0 : (currentPage - 1) * pageSize + 1,
|
startItem: totalCount === 0 ? 0 : (currentPage - 1) * pageSize + 1,
|
||||||
|
|||||||
@@ -1,73 +1,22 @@
|
|||||||
import DashboardListClient from "@/app/(main)/admin/dashboard/DashboardListClient";
|
import DashboardListClient from "@/app/(main)/admin/dashboard/DashboardListClient";
|
||||||
import { cookies } from "next/headers";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 서버에서 초기 대시보드 목록 fetch
|
* 대시보드 관리 페이지
|
||||||
|
* - 클라이언트 컴포넌트를 렌더링하는 래퍼
|
||||||
|
* - 초기 로딩부터 CSR로 처리
|
||||||
*/
|
*/
|
||||||
async function getInitialDashboards() {
|
export default function DashboardListPage() {
|
||||||
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();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-background flex min-h-screen flex-col">
|
<div className="bg-background flex min-h-screen flex-col">
|
||||||
<div className="space-y-6 p-6">
|
<div className="space-y-6 p-6">
|
||||||
{/* 페이지 헤더 (서버에서 렌더링) */}
|
{/* 페이지 헤더 */}
|
||||||
<div className="space-y-2 border-b pb-4">
|
<div className="space-y-2 border-b pb-4">
|
||||||
<h1 className="text-3xl font-bold tracking-tight">대시보드 관리</h1>
|
<h1 className="text-3xl font-bold tracking-tight">대시보드 관리</h1>
|
||||||
<p className="text-muted-foreground text-sm">대시보드를 생성하고 관리할 수 있습니다</p>
|
<p className="text-muted-foreground text-sm">대시보드를 생성하고 관리할 수 있습니다</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 나머지 컨텐츠 (클라이언트 컴포넌트 + 서버 데이터) */}
|
{/* 클라이언트 컴포넌트 */}
|
||||||
<DashboardListClient initialDashboards={initialData.dashboards} initialPagination={initialData.pagination} />
|
<DashboardListClient />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user