diff --git a/frontend/src/pages/mobile/public/Album.jsx b/frontend/src/pages/mobile/public/Album.jsx
index 22a4d2d..1fca0a7 100644
--- a/frontend/src/pages/mobile/public/Album.jsx
+++ b/frontend/src/pages/mobile/public/Album.jsx
@@ -1,22 +1,18 @@
import { motion } from 'framer-motion';
-import { useState, useEffect } from 'react';
+import { useQuery } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import { getAlbums } from '../../../api/public/albums';
// 모바일 앨범 목록 페이지
function MobileAlbum() {
const navigate = useNavigate();
- const [albums, setAlbums] = useState([]);
- const [loading, setLoading] = useState(true);
- useEffect(() => {
- getAlbums()
- .then(data => {
- setAlbums(data);
- setLoading(false);
- })
- .catch(console.error);
- }, []);
+ // useQuery로 앨범 데이터 로드
+ const { data: albums = [], isLoading: loading } = useQuery({
+ queryKey: ['albums'],
+ queryFn: getAlbums,
+ });
+
if (loading) {
return (
diff --git a/frontend/src/pages/mobile/public/Home.jsx b/frontend/src/pages/mobile/public/Home.jsx
index 89f43df..8689b4e 100644
--- a/frontend/src/pages/mobile/public/Home.jsx
+++ b/frontend/src/pages/mobile/public/Home.jsx
@@ -1,6 +1,6 @@
import { motion } from 'framer-motion';
import { ChevronRight, Clock, Tag } from 'lucide-react';
-import { useState, useEffect } from 'react';
+import { useQuery } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import { getTodayKST } from '../../../utils/date';
import { getMembers } from '../../../api/public/members';
@@ -10,27 +10,27 @@ import { getUpcomingSchedules } from '../../../api/public/schedules';
// 모바일 홈 페이지
function MobileHome() {
const navigate = useNavigate();
- const [members, setMembers] = useState([]);
- const [albums, setAlbums] = useState([]);
- const [schedules, setSchedules] = useState([]);
- // 데이터 로드
- useEffect(() => {
- // 멤버 로드
- getMembers()
- .then(data => setMembers(data.filter(m => !m.is_former)))
- .catch(console.error);
+ // useQuery로 멤버 데이터 로드 (활동 중인 멤버만)
+ const { data: members = [] } = useQuery({
+ queryKey: ['members'],
+ queryFn: getMembers,
+ select: (data) => data.filter(m => !m.is_former),
+ });
- // 앨범 로드 (최신 2개)
- getAlbums()
- .then(data => setAlbums(data.slice(0, 2)))
- .catch(console.error);
+ // useQuery로 앨범 로드 (최신 2개)
+ const { data: albums = [] } = useQuery({
+ queryKey: ['albums'],
+ queryFn: getAlbums,
+ select: (data) => data.slice(0, 2),
+ });
+
+ // useQuery로 다가오는 일정 로드
+ const { data: schedules = [] } = useQuery({
+ queryKey: ['upcomingSchedules', 3],
+ queryFn: () => getUpcomingSchedules(3),
+ });
- // 다가오는 일정 로드
- getUpcomingSchedules(3)
- .then(data => setSchedules(data))
- .catch(console.error);
- }, []);
return (
diff --git a/frontend/src/pages/mobile/public/Members.jsx b/frontend/src/pages/mobile/public/Members.jsx
index e63ea31..be9db69 100644
--- a/frontend/src/pages/mobile/public/Members.jsx
+++ b/frontend/src/pages/mobile/public/Members.jsx
@@ -1,22 +1,23 @@
import { motion, AnimatePresence } from 'framer-motion';
-import { useState, useEffect } from 'react';
+import { useState, useMemo } from 'react';
+import { useQuery } from '@tanstack/react-query';
import { Instagram, Calendar, X } from 'lucide-react';
import { getMembers } from '../../../api/public/members';
// 모바일 멤버 페이지
function MobileMembers() {
- const [members, setMembers] = useState([]);
- const [formerMembers, setFormerMembers] = useState([]);
const [selectedMember, setSelectedMember] = useState(null);
- useEffect(() => {
- getMembers()
- .then(data => {
- setMembers(data.filter(m => !m.is_former));
- setFormerMembers(data.filter(m => m.is_former));
- })
- .catch(console.error);
- }, []);
+ // useQuery로 멤버 데이터 로드
+ const { data: allMembers = [] } = useQuery({
+ queryKey: ['members'],
+ queryFn: getMembers,
+ });
+
+ // useMemo로 현재/전 멤버 분리
+ const members = useMemo(() => allMembers.filter(m => !m.is_former), [allMembers]);
+ const formerMembers = useMemo(() => allMembers.filter(m => m.is_former), [allMembers]);
+
// 나이 계산
const calculateAge = (birthDate) => {
diff --git a/frontend/src/pages/pc/public/Album.jsx b/frontend/src/pages/pc/public/Album.jsx
index e2ac260..b63946d 100644
--- a/frontend/src/pages/pc/public/Album.jsx
+++ b/frontend/src/pages/pc/public/Album.jsx
@@ -1,4 +1,4 @@
-import { useState, useEffect } from 'react';
+import { useQuery } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import { motion } from 'framer-motion';
import { Calendar, Music } from 'lucide-react';
@@ -7,20 +7,13 @@ import { formatDate } from '../../../utils/date';
function Album() {
const navigate = useNavigate();
- const [albums, setAlbums] = useState([]);
- const [loading, setLoading] = useState(true);
+
+ // useQuery로 앨범 데이터 로드
+ const { data: albums = [], isLoading: loading } = useQuery({
+ queryKey: ['albums'],
+ queryFn: getAlbums,
+ });
- useEffect(() => {
- getAlbums()
- .then(data => {
- setAlbums(data);
- setLoading(false);
- })
- .catch(error => {
- console.error('앨범 데이터 로드 오류:', error);
- setLoading(false);
- });
- }, []);
// 타이틀곡 찾기
const getTitleTrack = (tracks) => {
diff --git a/frontend/src/pages/pc/public/Home.jsx b/frontend/src/pages/pc/public/Home.jsx
index c40a54b..d5b3e95 100644
--- a/frontend/src/pages/pc/public/Home.jsx
+++ b/frontend/src/pages/pc/public/Home.jsx
@@ -1,260 +1,304 @@
-import { useState, useEffect } from 'react';
-import { motion } from 'framer-motion';
-import { Link } from 'react-router-dom';
-import { Calendar, ArrowRight, Clock, Link2, Tag } from 'lucide-react';
-import { getTodayKST } from '../../../utils/date';
-import { getMembers } from '../../../api/public/members';
-import { getUpcomingSchedules } from '../../../api/public/schedules';
+import { useState } from "react";
+import { useQuery } from "@tanstack/react-query";
+import { motion } from "framer-motion";
+import { Link } from "react-router-dom";
+import { Calendar, ArrowRight, Clock, Link2, Tag } from "lucide-react";
+import { getTodayKST } from "../../../utils/date";
+import { getMembers } from "../../../api/public/members";
+import { getUpcomingSchedules } from "../../../api/public/schedules";
function Home() {
- const [members, setMembers] = useState([]);
- const [upcomingSchedules, setUpcomingSchedules] = useState([]);
+ // useQuery로 멤버 데이터 로드
+ const { data: members = [] } = useQuery({
+ queryKey: ["members"],
+ queryFn: getMembers,
+ });
- useEffect(() => {
- // 멤버 데이터 로드
- getMembers()
- .then(data => setMembers(data))
- .catch(error => console.error('멤버 데이터 로드 오류:', error));
-
- // 다가오는 일정 로드 (오늘 이후 3개)
- getUpcomingSchedules(3)
- .then(data => setUpcomingSchedules(data))
- .catch(error => console.error('일정 데이터 로드 오류:', error));
- }, []);
+ // useQuery로 다가오는 일정 로드 (오늘 이후 3개)
+ const { data: upcomingSchedules = [] } = useQuery({
+ queryKey: ["upcomingSchedules", 3],
+ queryFn: () => getUpcomingSchedules(3),
+ });
- return (
-
- {/* 히어로 섹션 */}
-
-
-
-
- fromis_9
- 프로미스나인
-
- 인사드리겠습니다. 둘, 셋!
- 이제는 약속해 소중히 간직해,
- 당신의 아이돌로 성장하겠습니다!
-
-
- 멤버 보기
-
-
-
-
-
- {/* 장식 */}
-
-
-
- {/* 그룹 통계 섹션 */}
-
-
-
- {[
- { value: "2018.01.24", label: "데뷔일" },
- { value: `D+${(Math.floor((new Date() - new Date('2018-01-24')) / (1000 * 60 * 60 * 24)) + 1).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}
-
-
- ))}
-
-
-
-
- {/* 일정 미리보기 */}
-
-
-
- {upcomingSchedules.length === 0 ? (
-
- ) : (
-
- {upcomingSchedules.map((schedule) => {
- const scheduleDate = new Date(schedule.date);
- const today = new Date();
- const currentYear = today.getFullYear();
- const currentMonth = today.getMonth();
-
- const scheduleYear = scheduleDate.getFullYear();
- const scheduleMonth = scheduleDate.getMonth();
- const isCurrentYear = scheduleYear === currentYear;
- const isCurrentMonth = isCurrentYear && scheduleMonth === currentMonth;
-
- const day = scheduleDate.getDate();
- const weekdays = ['일', '월', '화', '수', '목', '금', '토'];
- const weekday = weekdays[scheduleDate.getDay()];
-
- // 멤버 처리
- const memberList = schedule.member_names ? schedule.member_names.split(',') : [];
- const displayMembers = memberList.length >= 5 ? ['프로미스나인'] : memberList;
-
- return (
-
- {/* 날짜 영역 - primary 색상 고정 */}
-
- {/* 현재 년도가 아니면 년.월 표시 */}
- {!isCurrentYear && (
-
- {scheduleYear}.{scheduleMonth + 1}
-
- )}
- {/* 현재 달이 아니면 월 표시 (현재 년도일 때) */}
- {isCurrentYear && !isCurrentMonth && (
-
- {scheduleMonth + 1}월
-
- )}
- {day}
- {weekday}
-
-
- {/* 내용 영역 */}
-
-
{schedule.title}
-
-
- {schedule.time && (
-
-
- {schedule.time.slice(0, 5)}
-
- )}
- {schedule.category_name && (
-
-
- {schedule.category_name}
-
- )}
- {schedule.source_name && (
-
-
- {schedule.source_name}
-
- )}
-
-
- {/* 멤버 태그 */}
- {displayMembers.length > 0 && (
-
- {displayMembers.map((name, i) => (
-
- {name.trim()}
-
- ))}
-
- )}
-
-
- );
- })}
-
- )}
-
-
-
-
+ return (
+
+ {/* 히어로 섹션 */}
+
+
+
+
+ fromis_9
+ 프로미스나인
+
+ 인사드리겠습니다. 둘, 셋!
+
+ 이제는 약속해 소중히 간직해,
+
+ 당신의 아이돌로 성장하겠습니다!
+
+
+ 멤버 보기
+
+
+
- );
+
+ {/* 장식 */}
+
+
+
+ {/* 그룹 통계 섹션 */}
+
+
+
+ {[
+ { value: "2018.01.24", label: "데뷔일" },
+ {
+ value: `D+${(
+ Math.floor(
+ (new Date() - new Date("2018-01-24")) /
+ (1000 * 60 * 60 * 24)
+ ) + 1
+ ).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}
+
+
+
+ ))}
+
+
+
+
+ {/* 일정 미리보기 */}
+
+
+
+ {upcomingSchedules.length === 0 ? (
+
+ ) : (
+
+ {upcomingSchedules.map((schedule) => {
+ const scheduleDate = new Date(schedule.date);
+ const today = new Date();
+ const currentYear = today.getFullYear();
+ const currentMonth = today.getMonth();
+
+ const scheduleYear = scheduleDate.getFullYear();
+ const scheduleMonth = scheduleDate.getMonth();
+ const isCurrentYear = scheduleYear === currentYear;
+ const isCurrentMonth =
+ isCurrentYear && scheduleMonth === currentMonth;
+
+ const day = scheduleDate.getDate();
+ const weekdays = ["일", "월", "화", "수", "목", "금", "토"];
+ const weekday = weekdays[scheduleDate.getDay()];
+
+ // 멤버 처리
+ const memberList = schedule.member_names
+ ? schedule.member_names.split(",")
+ : [];
+ const displayMembers =
+ memberList.length >= 5 ? ["프로미스나인"] : memberList;
+
+ return (
+
+ {/* 날짜 영역 - primary 색상 고정 */}
+
+ {/* 현재 년도가 아니면 년.월 표시 */}
+ {!isCurrentYear && (
+
+ {scheduleYear}.{scheduleMonth + 1}
+
+ )}
+ {/* 현재 달이 아니면 월 표시 (현재 년도일 때) */}
+ {isCurrentYear && !isCurrentMonth && (
+
+ {scheduleMonth + 1}월
+
+ )}
+ {day}
+
+ {weekday}
+
+
+
+ {/* 내용 영역 */}
+
+
+ {schedule.title}
+
+
+
+ {schedule.time && (
+
+
+ {schedule.time.slice(0, 5)}
+
+ )}
+ {schedule.category_name && (
+
+
+ {schedule.category_name}
+
+ )}
+ {schedule.source_name && (
+
+
+ {schedule.source_name}
+
+ )}
+
+
+ {/* 멤버 태그 */}
+ {displayMembers.length > 0 && (
+
+ {displayMembers.map((name, i) => (
+
+ {name.trim()}
+
+ ))}
+
+ )}
+
+
+ );
+ })}
+
+ )}
+
+
+
+ );
}
export default Home;
-
diff --git a/frontend/src/pages/pc/public/Members.jsx b/frontend/src/pages/pc/public/Members.jsx
index e529ab3..a641b35 100644
--- a/frontend/src/pages/pc/public/Members.jsx
+++ b/frontend/src/pages/pc/public/Members.jsx
@@ -1,24 +1,16 @@
-import { useState, useEffect } from 'react';
+import { useQuery } from '@tanstack/react-query';
import { motion } from 'framer-motion';
import { Instagram, Calendar } from 'lucide-react';
import { getMembers } from '../../../api/public/members';
import { formatDate } from '../../../utils/date';
function Members() {
- const [members, setMembers] = useState([]);
- const [loading, setLoading] = useState(true);
+ // useQuery로 멤버 데이터 로드
+ const { data: members = [], isLoading: loading } = useQuery({
+ queryKey: ['members'],
+ queryFn: getMembers,
+ });
- useEffect(() => {
- getMembers()
- .then(data => {
- setMembers(data);
- setLoading(false);
- })
- .catch(error => {
- console.error('데이터 로드 오류:', error);
- setLoading(false);
- });
- }, []);
if (loading) {
return (