diff --git a/frontend-temp/src/App.jsx b/frontend-temp/src/App.jsx
index 234310a..c256c2e 100644
--- a/frontend-temp/src/App.jsx
+++ b/frontend-temp/src/App.jsx
@@ -1,145 +1,14 @@
-import { BrowserRouter, Routes, Route, NavLink } from "react-router-dom";
-import { cn, getTodayKST, formatFullDate } from "@/utils";
-import { useUIStore } from "@/stores";
-import { useIsMobile, useCategories, useScheduleData } from "@/hooks";
-import { ErrorBoundary, Loading, ToastContainer, ScheduleCard, Layout } from "@/components";
-import { Schedule, Album } from "@/pages";
-
-/**
- * 홈 페이지 (임시)
- */
-function Home() {
- const today = getTodayKST();
- const isMobile = useIsMobile();
- const { showSuccess, showError } = useUIStore();
- const { data: categories, isLoading: categoriesLoading } = useCategories();
- const currentDate = new Date();
- const { data: schedules, isLoading: schedulesLoading } = useScheduleData(
- currentDate.getFullYear(),
- currentDate.getMonth() + 1
- );
-
- return (
-
-
-
-
- fromis_9 Frontend Refactoring
-
-
Phase 7 진행 중 - 스케줄 페이지
-
- 디바이스: {isMobile ? "모바일" : "PC"}
-
-
-
-
-
오늘: {formatFullDate(today)}
-
-
-
카테고리 ({categories?.length || 0}개)
- {categoriesLoading ? (
-
- ) : (
-
- {categories?.map((c) => (
-
- {c.name}
-
- ))}
-
- )}
-
-
-
-
토스트 테스트
-
-
-
-
-
-
-
-
페이지 이동
-
-
- 일정 페이지
-
-
-
-
-
- {/* ScheduleCard 컴포넌트 테스트 */}
- {schedulesLoading ? (
-
-
-
- ) : schedules?.length > 0 ? (
- <>
- {/* Public Variant (공개 페이지용) */}
-
-
variant="public" (공개 페이지용)
-
- {schedules.slice(0, 2).map((schedule) => (
- showSuccess(`${schedule.title} 클릭`)}
- />
- ))}
-
-
-
- {/* Admin Variant (관리자 페이지용) */}
-
-
variant="admin" (관리자 페이지용)
-
- {schedules.slice(0, 2).map((schedule) => (
- showSuccess(`${schedule.title} 클릭`)}
- onEdit={(s) => showSuccess(`${s.title} 수정`)}
- onDelete={(s) => showError(`${s.title} 삭제`)}
- />
- ))}
-
-
- >
- ) : (
-
- )}
-
-
- );
-}
+import { BrowserRouter, Routes, Route } from "react-router-dom";
+import { ErrorBoundary, ToastContainer, Layout } from "@/components";
+import { Schedule, Album, Home, Members, NotFound } from "@/pages";
/**
* 프로미스나인 팬사이트 메인 앱
*
- * Phase 8: 앨범 페이지 마이그레이션
- * - Album 페이지 (PC/Mobile 통합)
- * - useAlbums 훅 추가
+ * Phase 9: 기타 공개 페이지 마이그레이션
+ * - Home 페이지 (PC/Mobile 통합)
+ * - Members 페이지 (PC/Mobile 통합)
+ * - NotFound 페이지
*/
function App() {
return (
@@ -157,6 +26,28 @@ function App() {
}
/>
+ {/* 멤버 */}
+
+
+
+
+ }
+ />
+
+ {/* 앨범 */}
+
+
+
+
+ }
+ />
+
{/* 스케줄 */}
- {/* 임시 페이지들 */}
+ {/* 404 */}
- 멤버 페이지 (준비 중)
-
- }
- />
- {/* 앨범 */}
-
-
-
+
+
}
/>
diff --git a/frontend-temp/src/hooks/index.js b/frontend-temp/src/hooks/index.js
index 7bd4b1d..3f93f14 100644
--- a/frontend-temp/src/hooks/index.js
+++ b/frontend-temp/src/hooks/index.js
@@ -27,3 +27,6 @@ export { useAdminAuth, useRedirectIfAuthenticated } from './useAdminAuth';
// 앨범 데이터
export { useAlbums, useAlbumDetail, useAlbumGallery } from './useAlbumData';
+
+// 멤버 데이터
+export { useMembers, useMemberDetail } from './useMemberData';
diff --git a/frontend-temp/src/hooks/useMemberData.js b/frontend-temp/src/hooks/useMemberData.js
new file mode 100644
index 0000000..072e251
--- /dev/null
+++ b/frontend-temp/src/hooks/useMemberData.js
@@ -0,0 +1,24 @@
+import { useQuery } from '@tanstack/react-query';
+import { memberApi } from '@/api';
+
+/**
+ * 멤버 목록 조회 훅
+ */
+export function useMembers() {
+ return useQuery({
+ queryKey: ['members'],
+ queryFn: memberApi.getMembers,
+ });
+}
+
+/**
+ * 멤버 상세 조회 훅
+ * @param {number} id - 멤버 ID
+ */
+export function useMemberDetail(id) {
+ return useQuery({
+ queryKey: ['member', id],
+ queryFn: () => memberApi.getMemberById(id),
+ enabled: !!id,
+ });
+}
diff --git a/frontend-temp/src/pages/common/NotFound.jsx b/frontend-temp/src/pages/common/NotFound.jsx
new file mode 100644
index 0000000..0fb2393
--- /dev/null
+++ b/frontend-temp/src/pages/common/NotFound.jsx
@@ -0,0 +1,30 @@
+import { Link } from 'react-router-dom';
+import { Home } from 'lucide-react';
+import { useIsMobile } from '@/hooks';
+
+/**
+ * 404 페이지 (PC/Mobile 통합)
+ */
+function NotFound() {
+ const isMobile = useIsMobile();
+
+ return (
+
+
+ 404
+
+
+ 페이지를 찾을 수 없습니다
+
+
+
+
홈으로 돌아가기
+
+
+ );
+}
+
+export default NotFound;
diff --git a/frontend-temp/src/pages/common/index.js b/frontend-temp/src/pages/common/index.js
new file mode 100644
index 0000000..4d20aa9
--- /dev/null
+++ b/frontend-temp/src/pages/common/index.js
@@ -0,0 +1 @@
+export { default as NotFound } from './NotFound';
diff --git a/frontend-temp/src/pages/home/Home.jsx b/frontend-temp/src/pages/home/Home.jsx
new file mode 100644
index 0000000..5e24af7
--- /dev/null
+++ b/frontend-temp/src/pages/home/Home.jsx
@@ -0,0 +1,333 @@
+import { Link } from 'react-router-dom';
+import { motion } from 'framer-motion';
+import { Calendar, ArrowRight, Clock, Tag, Music } from 'lucide-react';
+import { useIsMobile, useMembers, useAlbums, useUpcomingSchedules } from '@/hooks';
+import { Loading } from '@/components';
+
+/**
+ * 홈 페이지 (PC/Mobile 통합)
+ */
+function Home() {
+ const isMobile = useIsMobile();
+
+ // 멤버 데이터
+ const { data: members = [] } = useMembers();
+
+ // 앨범 데이터 (최신 4개)
+ const { data: allAlbums = [] } = useAlbums();
+ const albums = allAlbums.slice(0, 4);
+
+ // 다가오는 일정 (3개)
+ const { data: upcomingSchedules = [] } = useUpcomingSchedules(3);
+
+ // D+Day 계산
+ const debutDate = new Date('2018-01-24');
+ const today = new Date();
+ const dDay = Math.floor((today - debutDate) / (1000 * 60 * 60 * 24)) + 1;
+
+ // 모바일 레이아웃
+ if (isMobile) {
+ return (
+
+ {/* 히어로 */}
+
+
+ {/* 통계 */}
+
+ {[
+ { value: '2018.01.24', label: '데뷔일' },
+ { value: `D+${dDay.toLocaleString()}`, label: 'D+Day' },
+ ].map((stat, i) => (
+
+
{stat.value}
+
{stat.label}
+
+ ))}
+
+
+ {/* 멤버 미리보기 */}
+
+
+
+ {members.filter((m) => !m.is_former).map((member) => (
+
+
+

+
+
{member.name}
+
+ ))}
+
+
+
+ {/* 앨범 미리보기 */}
+
+
+
+ {albums.slice(0, 2).map((album) => (
+
+
+

+
+
{album.title}
+
+ ))}
+
+
+
+ {/* 일정 미리보기 */}
+
+
+ {upcomingSchedules.length === 0 ? (
+
+ ) : (
+
+ {upcomingSchedules.map((schedule) => (
+
+
{schedule.title}
+
+ {new Date(schedule.date).getMonth() + 1}월 {new Date(schedule.date).getDate()}일
+ {schedule.time && ` · ${schedule.time.slice(0, 5)}`}
+
+
+ ))}
+
+ )}
+
+
+ );
+ }
+
+ // PC 레이아웃
+ return (
+
+ {/* 히어로 섹션 */}
+
+
+
+
+ fromis_9
+ 프로미스나인
+
+ 인사드리겠습니다. 둘, 셋!
+
+ 이제는 약속해 소중히 간직해,
+
+ 당신의 아이돌로 성장하겠습니다!
+
+
+
+
+
+
+ {/* 그룹 통계 */}
+
+
+
+ {[
+ { value: '2018.01.24', label: '데뷔일' },
+ { value: `D+${dDay.toLocaleString()}`, label: 'D+Day' },
+ { value: '5', label: '멤버 수' },
+ { value: 'flover', label: '팬덤명' },
+ ].map((stat, index) => (
+
+ {stat.value}
+ {stat.label}
+
+ ))}
+
+
+
+
+ {/* 멤버 미리보기 */}
+
+
+
+
+ {members.filter((m) => !m.is_former).map((member, index) => (
+
+
+

+
+
+
+
{member.name}
+
+
+ ))}
+
+
+
+
+ {/* 앨범 미리보기 */}
+
+
+
+
+ {albums.map((album, index) => (
+
(window.location.href = `/album/${encodeURIComponent(album.title)}`)}
+ >
+
+

+
+
+
+
{album.tracks?.length || 0}곡 수록
+
+
+
+
+
{album.title}
+
{album.release_date?.slice(0, 4)}
+
+
+ ))}
+
+
+
+
+ {/* 일정 미리보기 */}
+
+
+
+ {upcomingSchedules.length === 0 ? (
+
+ ) : (
+
+ {upcomingSchedules.map((schedule) => {
+ const scheduleDate = new Date(schedule.date);
+ const categoryColor = schedule.category_color || '#6366f1';
+ const memberList = schedule.member_names
+ ? schedule.member_names.split(',')
+ : schedule.members?.map((m) => m.name) || [];
+
+ return (
+
+
+ {scheduleDate.getDate()}
+
+ {['일', '월', '화', '수', '목', '금', '토'][scheduleDate.getDay()]}
+
+
+
+
{schedule.title}
+
+ {schedule.time && (
+
+
+ {schedule.time.slice(0, 5)}
+
+ )}
+
+
+ {schedule.category_name}
+
+
+ {memberList.length > 0 && (
+
+ {memberList.map((name, i) => (
+
+ {name.trim()}
+
+ ))}
+
+ )}
+
+
+ );
+ })}
+
+ )}
+
+
+
+ );
+}
+
+export default Home;
diff --git a/frontend-temp/src/pages/home/index.js b/frontend-temp/src/pages/home/index.js
new file mode 100644
index 0000000..c2d92a5
--- /dev/null
+++ b/frontend-temp/src/pages/home/index.js
@@ -0,0 +1 @@
+export { default as Home } from './Home';
diff --git a/frontend-temp/src/pages/index.js b/frontend-temp/src/pages/index.js
index 2a3da66..558eff9 100644
--- a/frontend-temp/src/pages/index.js
+++ b/frontend-temp/src/pages/index.js
@@ -3,3 +3,6 @@
*/
export * from './schedule';
export * from './album';
+export * from './home';
+export * from './members';
+export * from './common';
diff --git a/frontend-temp/src/pages/members/Members.jsx b/frontend-temp/src/pages/members/Members.jsx
new file mode 100644
index 0000000..8ad7a4a
--- /dev/null
+++ b/frontend-temp/src/pages/members/Members.jsx
@@ -0,0 +1,181 @@
+import { motion } from 'framer-motion';
+import { Instagram, Calendar } from 'lucide-react';
+import { useIsMobile, useMembers } from '@/hooks';
+import { Loading } from '@/components';
+import { formatDate } from '@/utils';
+
+/**
+ * 멤버 카드 컴포넌트 (PC)
+ */
+function MemberCard({ member, isFormer = false, delay = 0 }) {
+ return (
+
+
+ {/* 이미지 */}
+
+

+
+
+ {/* 정보 */}
+
+
+ {member.name}
+
+
+
+
+ {formatDate(member.birth_date, 'YYYY.MM.DD')}
+
+
+ {/* 인스타그램 링크 */}
+ {member.instagram && !isFormer && (
+
+
+ Instagram
+
+ )}
+
+
+ {/* 호버 효과 - 컬러 바 */}
+
+
+
+ );
+}
+
+/**
+ * 멤버 페이지 (PC/Mobile 통합)
+ */
+function Members() {
+ const isMobile = useIsMobile();
+ const { data: members = [], isLoading } = useMembers();
+
+ const currentMembers = members.filter((m) => !m.is_former);
+ const formerMembers = members.filter((m) => m.is_former);
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ // 모바일 레이아웃
+ if (isMobile) {
+ return (
+
+ {/* 현재 멤버 */}
+
+ {currentMembers.map((member) => (
+
+
+

+
+
+
{member.name}
+
{formatDate(member.birth_date, 'YYYY.MM.DD')}
+
+
+ ))}
+
+
+ {/* 전 멤버 */}
+ {formerMembers.length > 0 && (
+ <>
+
전 멤버
+
+ {formerMembers.map((member) => (
+
+
+

+
+
+
{member.name}
+
{formatDate(member.birth_date, 'YYYY.MM.DD')}
+
+
+ ))}
+
+ >
+ )}
+
+ );
+ }
+
+ // PC 레이아웃
+ return (
+
+
+ {/* 헤더 */}
+
+
+ 멤버
+
+
+ 프로미스나인의 멤버를 소개합니다
+
+
+
+ {/* 현재 멤버 그리드 */}
+
+ {currentMembers.map((member, index) => (
+
+ ))}
+
+
+ {/* 전 멤버 섹션 */}
+ {formerMembers.length > 0 && (
+
+ 전 멤버
+
+ {formerMembers.map((member, index) => (
+
+ ))}
+
+
+ )}
+
+
+ );
+}
+
+export default Members;
diff --git a/frontend-temp/src/pages/members/index.js b/frontend-temp/src/pages/members/index.js
new file mode 100644
index 0000000..c1d2028
--- /dev/null
+++ b/frontend-temp/src/pages/members/index.js
@@ -0,0 +1 @@
+export { default as Members } from './Members';