diff --git a/src/features/dev/components/DevOnly.tsx b/src/features/dev/components/DevOnly.tsx
new file mode 100644
index 0000000..f660205
--- /dev/null
+++ b/src/features/dev/components/DevOnly.tsx
@@ -0,0 +1,28 @@
+/**
+ * DevOnly — `/dev/*` 라우트 가드.
+ *
+ * `window.location.hostname` 이 로컬호스트 계열이 아닐 경우 루트로 리다이렉트.
+ * 클라이언트 사이드 가드라 보안 의미보다는 "운영 도메인에서 실수로 노출되는 것 방지" 용도.
+ * 진짜 차단이 필요하면 서버/CDN 레벨에서 경로를 막아야 한다.
+ */
+import { Navigate, Outlet } from 'react-router';
+
+const LOCAL_HOSTNAMES = new Set([
+ 'localhost',
+ '127.0.0.1',
+ '0.0.0.0',
+ '::1',
+ '[::1]',
+]);
+
+function isLocalHost(): boolean {
+ if (typeof window === 'undefined') return false;
+ return LOCAL_HOSTNAMES.has(window.location.hostname);
+}
+
+export default function DevOnly() {
+ if (!isLocalHost()) {
+ return ;
+ }
+ return ;
+}
diff --git a/src/features/dev/pages/ClinicsPage.tsx b/src/features/dev/pages/ClinicsPage.tsx
new file mode 100644
index 0000000..2cd9d26
--- /dev/null
+++ b/src/features/dev/pages/ClinicsPage.tsx
@@ -0,0 +1,148 @@
+/**
+ * Dev: 클리닉 리스트 페이지.
+ *
+ * 백엔드 `GET /api/clinics` (listClinics) 응답을 표로 확인하는 개발용 화면.
+ * 운영 도메인 노출 방지는 라우트 단의 DevOnly 가드에 위임.
+ */
+import { useState } from 'react';
+import { useListClinics } from '@/shared/api/generated/clinics/clinics';
+import { PageContainer } from '@/shared/ui/page-container';
+import { Spinner } from '@/shared/ui/spinner';
+import { EmptyState } from '@/shared/ui/empty-state';
+import { Button } from '@/shared/ui/button';
+
+const PAGE_SIZE = 50;
+
+function formatDate(raw: string): string {
+ try {
+ return new Date(raw).toLocaleString('ko-KR', {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ });
+ } catch {
+ return raw;
+ }
+}
+
+export default function ClinicsPage() {
+ const [offset, setOffset] = useState(0);
+
+ const { data, isLoading, error, refetch, isFetching } = useListClinics(
+ { limit: PAGE_SIZE, offset },
+ { query: { staleTime: 0 } },
+ );
+
+ const items = data?.status === 200 ? data.data : [];
+
+ return (
+
+
+
+
+ {isLoading ? (
+
+
+
+ ) : error ? (
+
+ 클리닉 리스트 조회 실패: {String((error as Error)?.message ?? error)}
+
+ ) : items.length === 0 ? (
+
+ ) : (
+
+
+
+
+ | 병원명 |
+ EN |
+ 상태 |
+ URL |
+ 주소 |
+ 생성일 |
+ hospital_id |
+
+
+
+ {items.map((c) => (
+
+ | {c.hospital_name} |
+ {c.hospital_name_en ?? '—'} |
+
+
+ {c.status}
+
+ |
+
+ {c.url ? (
+
+ {c.url}
+
+ ) : (
+ '—'
+ )}
+ |
+ {c.road_address ?? '—'} |
+ {formatDate(c.created_at)} |
+ {c.hospital_id} |
+
+ ))}
+
+
+
+ )}
+
+ {/* 페이지네이션 — 정확한 total 이 없어서 단순 prev/next 만 노출 */}
+
+
+
+
+
+
+ );
+}
diff --git a/src/features/dev/routes.tsx b/src/features/dev/routes.tsx
index 2b9968c..9ec7c18 100644
--- a/src/features/dev/routes.tsx
+++ b/src/features/dev/routes.tsx
@@ -1,7 +1,16 @@
import { lazy } from 'react'
+import DevOnly from './components/DevOnly'
const ComponentsPage = lazy(() => import('./pages/ComponentsPage'))
+const ClinicsPage = lazy(() => import('./pages/ClinicsPage'))
+// `/dev/*` 는 DevOnly 가드를 거쳐 로컬호스트에서만 접근 가능.
export const devRoutes = [
- { path: 'dev/components', element: },
+ {
+ element: ,
+ children: [
+ { path: 'dev/components', element: },
+ { path: 'dev/clinics', element: },
+ ],
+ },
]