diff --git a/src/app/[locale]/(protected)/loading.tsx b/src/app/[locale]/(protected)/loading.tsx
index f549fc4f..bcadc8a1 100644
--- a/src/app/[locale]/(protected)/loading.tsx
+++ b/src/app/[locale]/(protected)/loading.tsx
@@ -1,4 +1,4 @@
-import { ListPageSkeleton } from '@/components/ui/skeleton';
+import { GenericPageSkeleton } from '@/components/ui/skeleton';
/**
* Protected Group Loading UI
@@ -7,8 +7,12 @@ import { ListPageSkeleton } from '@/components/ui/skeleton';
* - AuthenticatedLayout 내에서 표시됨 (사이드바, 헤더 유지)
* - React Suspense 자동 적용
* - 페이지 전환 시 즉각적인 피드백
- * - 스켈레톤 UI로 레이아웃 유지하며 로딩 표시
+ * - 범용 스켈레톤으로 모든 페이지 타입에 대응
+ *
+ * 참고:
+ * - 리스트 페이지: IntegratedListTemplateV2 내부에서 상세 스켈레톤 처리
+ * - 커스텀 페이지: 이 범용 스켈레톤이 표시됨
*/
export default function ProtectedLoading() {
- return ;
+ return ;
}
diff --git a/src/app/[locale]/(protected)/settings/account-info/page.tsx b/src/app/[locale]/(protected)/settings/account-info/page.tsx
index 98c960f2..2e7fbcc1 100644
--- a/src/app/[locale]/(protected)/settings/account-info/page.tsx
+++ b/src/app/[locale]/(protected)/settings/account-info/page.tsx
@@ -1,9 +1,14 @@
'use client';
import { useEffect, useState } from 'react';
+import { User } from 'lucide-react';
import { AccountInfoClient } from '@/components/settings/AccountInfoManagement';
import { getAccountInfo } from '@/components/settings/AccountInfoManagement/actions';
import type { AccountInfo, TermsAgreement, MarketingConsent } from '@/components/settings/AccountInfoManagement/types';
+import { PageLayout } from '@/components/organisms/PageLayout';
+import { PageHeader } from '@/components/organisms/PageHeader';
+import { Card, CardContent, CardHeader } from '@/components/ui/card';
+import { Skeleton } from '@/components/ui/skeleton';
const DEFAULT_ACCOUNT_INFO: AccountInfo = {
id: '',
@@ -44,9 +49,72 @@ export default function AccountInfoPage() {
if (isLoading) {
return (
-
+
+
+
+ {/* 계정 정보 카드 스켈레톤 */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 약관 동의 정보 카드 스켈레톤 */}
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/app/[locale]/(protected)/subscription/page.tsx b/src/app/[locale]/(protected)/subscription/page.tsx
index d3f4291f..ffe6592c 100644
--- a/src/app/[locale]/(protected)/subscription/page.tsx
+++ b/src/app/[locale]/(protected)/subscription/page.tsx
@@ -1,8 +1,13 @@
'use client';
import { useEffect, useState } from 'react';
+import { CreditCard } from 'lucide-react';
import { SubscriptionManagement } from '@/components/settings/SubscriptionManagement';
import { getSubscriptionData } from '@/components/settings/SubscriptionManagement/actions';
+import { PageLayout } from '@/components/organisms/PageLayout';
+import { PageHeader } from '@/components/organisms/PageHeader';
+import { Card, CardContent } from '@/components/ui/card';
+import { Skeleton } from '@/components/ui/skeleton';
export default function SubscriptionPage() {
const [data, setData] = useState>['data']>(undefined);
@@ -18,9 +23,47 @@ export default function SubscriptionPage() {
if (isLoading) {
return (
-
+
+
+ {/* 헤더 액션 버튼 스켈레톤 */}
+
+
+
+
+
+ {/* 구독 정보 카드 그리드 스켈레톤 */}
+
+ {[1, 2, 3].map((i) => (
+
+
+
+
+
+
+ ))}
+
+ {/* 구독 정보 카드 스켈레톤 */}
+
+
+
+
+
+ {[1, 2, 3].map((i) => (
+
+
+
+
+
+ ))}
+
+
+
+
+
);
}
diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx
index 6cbd8b9b..a345451e 100644
--- a/src/components/ui/skeleton.tsx
+++ b/src/components/ui/skeleton.tsx
@@ -598,6 +598,65 @@ function PageHeaderSkeleton({
);
}
+// ============================================
+// 14. 범용 페이지 스켈레톤 (커스텀 페이지용)
+// ============================================
+/**
+ * 모든 페이지에 대응하는 심플한 범용 스켈레톤
+ * - 리스트 페이지: IntegratedListTemplateV2 내부에서 처리
+ * - 커스텀 페이지: 이 스켈레톤 사용 (settings, qms 등)
+ */
+function GenericPageSkeleton() {
+ return (
+
+ {/* 페이지 헤더 영역 */}
+
+
+ {/* 액션 버튼 영역 */}
+
+
+
+
+
+ {/* 콘텐츠 카드 영역 */}
+
+
+
+ {Array.from({ length: 6 }).map((_, i) => (
+
+
+
+
+ ))}
+
+
+
+
+ {/* 추가 콘텐츠 카드 */}
+
+
+
+
+ {Array.from({ length: 3 }).map((_, i) => (
+
+
+
+
+
+ ))}
+
+
+
+
+ );
+}
+
// ============================================
// Export
// ============================================
@@ -615,4 +674,5 @@ export {
ListPageSkeleton,
PageHeaderSkeleton,
ContentSkeleton,
+ GenericPageSkeleton,
};
diff --git a/src/layouts/AuthenticatedLayout.tsx b/src/layouts/AuthenticatedLayout.tsx
index f4df1dbb..246d5cbc 100644
--- a/src/layouts/AuthenticatedLayout.tsx
+++ b/src/layouts/AuthenticatedLayout.tsx
@@ -477,13 +477,43 @@ export default function AuthenticatedLayout({ children }: AuthenticatedLayoutPro
// By removing this check, we allow the component to render immediately with default values
// and update once hydration completes through the useEffect above.
- // 🔧 새로고침 시 스피너 표시 (hydration 전)
+ // 🔧 새로고침 시 스켈레톤 표시 (hydration 전)
+ // GenericPageSkeleton import는 상단에 추가 필요
if (!isMounted) {
return (
-
-
-
-
로딩 중...
+
+ {/* 헤더 스켈레톤 */}
+
+
+ {/* 사이드바 + 콘텐츠 스켈레톤 */}
+
+ {/* 사이드바 스켈레톤 */}
+
+
+ {/* 콘텐츠 스켈레톤 */}
+
+ {/* 페이지 헤더 */}
+
+ {/* 콘텐츠 카드 */}
+
+
+ {[1, 2, 3, 4, 5, 6].map((i) => (
+
+ ))}
+
+
+
);